Author: craigmcc
Date: Sat Jan 22 22:55:57 2005
New Revision: 126200

URL: http://svn.apache.org/viewcvs?view=rev&rev=126200
Log:
Add initial, minimal, support for remote requests.  Use of these APIs will
typically be made by rich JSF components that require back-channel interactions
with the server (perhaps through something like XmlHttpRequest), but the
facility is generally useful for REST-style interactions with a server.

Fundamentally, an application provides a series of commands (as in Commons
Chain) in a catalog named (by default) "remote", and that have a context-
relative URI that matches one of the defined regular expressions (by default
this is like a "*.remote" extension mapping).  A command whose name matches
the context relative URI (i.e. servlet path + path info) will be executed
to produce the corresponding result.  Support classes are provided to make
it easy to write such a command, particularly:

* RemoteContext -- a Commons Chain "Context" object that encapsulates the
  state of the current request, but isolates the command from Servlet API
  issues (making it *much* easier to build unit tests).

* ResponseWrapper -- a wrapper class (around a Writer) that makes it very
  easy to compose XML responses, with APIs inspired by the
  javax.faces.context.ResponseWriter API in JSF.  This is by no means the
  only way to create XML output, but it will feel familiar to JSF aware
  developers, among others.


Added:
   struts/shale/trunk/core-library/src/java/org/apache/shale/remote/
   
struts/shale/trunk/core-library/src/java/org/apache/shale/remote/ContextAttributes.java
   
struts/shale/trunk/core-library/src/java/org/apache/shale/remote/MapEntry.java
   
struts/shale/trunk/core-library/src/java/org/apache/shale/remote/RemoteCommand.java
   
struts/shale/trunk/core-library/src/java/org/apache/shale/remote/RemoteContext.java
   
struts/shale/trunk/core-library/src/java/org/apache/shale/remote/RequestAttributes.java
   
struts/shale/trunk/core-library/src/java/org/apache/shale/remote/ResponseWrapper.java
   
struts/shale/trunk/core-library/src/java/org/apache/shale/remote/ServletRemoteContext.java
   
struts/shale/trunk/core-library/src/java/org/apache/shale/remote/SessionAttributes.java
   struts/shale/trunk/core-library/src/java/org/apache/shale/remote/package.html
Modified:
   
struts/shale/trunk/core-library/src/java/org/apache/shale/faces/shale-config.xml

Modified: 
struts/shale/trunk/core-library/src/java/org/apache/shale/faces/shale-config.xml
Url: 
http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/java/org/apache/shale/faces/shale-config.xml?view=diff&rev=126200&p1=struts/shale/trunk/core-library/src/java/org/apache/shale/faces/shale-config.xml&r1=126199&p2=struts/shale/trunk/core-library/src/java/org/apache/shale/faces/shale-config.xml&r2=126200
==============================================================================
--- 
struts/shale/trunk/core-library/src/java/org/apache/shale/faces/shale-config.xml
    (original)
+++ 
struts/shale/trunk/core-library/src/java/org/apache/shale/faces/shale-config.xml
    Sat Jan 22 22:55:57 2005
@@ -34,9 +34,30 @@
 <catalog           name="shale">
 
 
-  <!-- ================ Perform Dispatch Processing ======================== 
-->
+  <!-- ===================== Remote Requests =============================== 
-->
+
+  <chain           name="remote">
+
+    <!-- Perform remote request processing for any request whose context -->
+    <!-- relative path ends with "*.remote" -->
+    <command  className="org.apache.shale.remote.RemoteCommand"
+            catalogName="remote"
+               patterns="\S*\.remote"/>
+
+  </chain>
+
+  <!-- ================ Perform Request Processing ========================= 
-->
 
   <chain           name="standard">
+
+    <!-- Perform remote request processing for matching requests -->
+    <!-- Successful match will terminate the processing chain -->
+    <!-- FIXME - determine whether we want this before or after -->
+    <!-- the "preprocess" command -->
+    <command  className="org.apache.commons.chain.generic.LookupCommand"
+            catalogName="shale"
+                   name="remote"
+               optional="true"/>
 
     <!-- Invoke "preprocess" command if it exists -->
     <command  className="org.apache.commons.chain.generic.LookupCommand"

Added: 
struts/shale/trunk/core-library/src/java/org/apache/shale/remote/ContextAttributes.java
Url: 
http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/java/org/apache/shale/remote/ContextAttributes.java?view=auto&rev=126200
==============================================================================
--- (empty file)
+++ 
struts/shale/trunk/core-library/src/java/org/apache/shale/remote/ContextAttributes.java
     Sat Jan 22 22:55:57 2005
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shale.remote;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.servlet.ServletContext;
+
+
+/**
+ * <p>Private implementation of <code>Map</code> for servlet context
+ * attributes.</p>
+ *
+ * $Id$
+ */
+
+final class ContextAttributes implements Map {
+
+
+    public ContextAttributes(ServletContext context) {
+        this.context = context;
+    }
+
+
+    private ServletContext context = null;
+
+
+    public void clear() {
+        Iterator keys = keySet().iterator();
+        while (keys.hasNext()) {
+            context.removeAttribute((String) keys.next());
+        }
+    }
+
+
+    public boolean containsKey(Object key) {
+        return (context.getAttribute(key(key)) != null);
+    }
+
+
+    public boolean containsValue(Object value) {
+        if (value == null) {
+            return (false);
+        }
+        Enumeration keys = context.getAttributeNames();
+        while (keys.hasMoreElements()) {
+            Object next = context.getAttribute((String) keys.nextElement());
+            if (next == value) {
+                return (true);
+            }
+        }
+        return (false);
+    }
+
+
+    public Set entrySet() {
+        Set set = new HashSet();
+        Enumeration keys = context.getAttributeNames();
+        String key;
+        while (keys.hasMoreElements()) {
+            key = (String)keys.nextElement();
+            set.add(new MapEntry(key, context.getAttribute(key), true));
+        }
+        return (set);
+    }
+
+
+    public boolean equals(Object o) {
+        return (context.equals(o));
+    }
+
+
+    public Object get(Object key) {
+        return (context.getAttribute(key(key)));
+    }
+
+
+    public int hashCode() {
+        return (context.hashCode());
+    }
+
+
+    public boolean isEmpty() {
+        return (size() < 1);
+    }
+
+
+    public Set keySet() {
+        Set set = new HashSet();
+        Enumeration keys = context.getAttributeNames();
+        while (keys.hasMoreElements()) {
+            set.add(keys.nextElement());
+        }
+        return (set);
+    }
+
+
+    public Object put(Object key, Object value) {
+        if (value == null) {
+            return (remove(key));
+        }
+        String skey = key(key);
+        Object previous = context.getAttribute(skey);
+        context.setAttribute(skey, value);
+        return (previous);
+    }
+
+
+    public void putAll(Map map) {
+        Iterator keys = map.keySet().iterator();
+        while (keys.hasNext()) {
+            String key = (String) keys.next();
+            context.setAttribute(key, map.get(key));
+        }
+    }
+
+
+    public Object remove(Object key) {
+        String skey = key(key);
+        Object previous = context.getAttribute(skey);
+        context.removeAttribute(skey);
+        return (previous);
+    }
+
+
+    public int size() {
+        int n = 0;
+        Enumeration keys = context.getAttributeNames();
+        while (keys.hasMoreElements()) {
+            keys.nextElement();
+            n++;
+        }
+        return (n);
+    }
+
+
+    public Collection values() {
+        List list = new ArrayList();
+        Enumeration keys = context.getAttributeNames();
+        while (keys.hasMoreElements()) {
+            list.add(context.getAttribute((String) keys.nextElement()));
+        }
+        return (list);
+    }
+
+
+    private String key(Object key) {
+        if (key == null) {
+            throw new IllegalArgumentException();
+        } else if (key instanceof String) {
+            return ((String) key);
+        } else {
+            return (key.toString());
+        }
+    }
+
+
+}

Added: 
struts/shale/trunk/core-library/src/java/org/apache/shale/remote/MapEntry.java
Url: 
http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/java/org/apache/shale/remote/MapEntry.java?view=auto&rev=126200
==============================================================================
--- (empty file)
+++ 
struts/shale/trunk/core-library/src/java/org/apache/shale/remote/MapEntry.java  
    Sat Jan 22 22:55:57 2005
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shale.remote;
+
+import java.util.Map;
+
+/**
+ * <p>Map.Entry implementation that can be constructed to either be read-only
+ * or not.</p>
+ *
+ * $Id$
+ */
+
+class MapEntry implements Map.Entry {
+
+
+    /**
+     * <p>The entry key.</p>
+     */
+    private Object key;
+
+    /**
+     * <p>The entry value.</p>
+     */
+    private Object value;
+
+    /**
+     * <p>Whether the entry can be modified.</p>
+     */
+    private boolean modifiable = false;
+
+
+    /**
+     * <p>Creates a map entry that can either allow modifications or not.</p>
+     *
+     * @param key The entry key
+     * @param value The entry value
+     * @param modifiable Whether the entry should allow modification or not
+     */
+    public MapEntry(Object key, Object value, boolean modifiable) {
+        this.key = key;
+        this.value = value;
+        this.modifiable = modifiable;
+    }
+
+
+    /**
+     * <p>Gets the entry key.</p>
+     *
+     * @return The entry key
+     */
+    public Object getKey() {
+        return key;
+    }
+
+
+    /**
+     * <p>Gets the entry value.</p>
+     *
+     * @return The entry key
+     */
+    public Object getValue() {
+        return value;
+    }
+
+
+    /**
+     * <p>Sets the entry value if the entry can be modified.</p>
+     *
+     * @param val The new value
+     * @return The old entry value
+     * @throws UnsupportedOperationException If the entry cannot be modified
+     */
+    public Object setValue(Object val) {
+        if (modifiable) {
+            Object oldVal = this.value;
+            this.value = val;
+            return oldVal;
+        } else {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+
+    /**
+     * <p>Determines if this entry is equal to the passed object.</p>
+     *
+     * @param o The object to test
+     * @return True if equal, else false
+     */
+    public boolean equals(Object o) {
+        if (o != null && o instanceof Map.Entry) {
+            Map.Entry entry = (Map.Entry)o;
+            return (this.getKey() == null ?
+                    entry.getKey() == null : 
this.getKey().equals(entry.getKey()))  &&
+                   (this.getValue() == null ?
+                    entry.getValue() == null : 
this.getValue().equals(entry.getValue()));
+        }
+        return false;
+    }
+
+
+    /**
+     * <p>Returns the hashcode for this entry.</p>
+     *
+     * @return The and'ed hashcode of the key and value
+     */
+    public int hashCode() {
+        return (this.getKey() == null   ? 0 : this.getKey().hashCode()) ^
+               (this.getValue() == null ? 0 : this.getValue().hashCode());
+    }
+}

Added: 
struts/shale/trunk/core-library/src/java/org/apache/shale/remote/RemoteCommand.java
Url: 
http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/java/org/apache/shale/remote/RemoteCommand.java?view=auto&rev=126200
==============================================================================
--- (empty file)
+++ 
struts/shale/trunk/core-library/src/java/org/apache/shale/remote/RemoteCommand.java
 Sat Jan 22 22:55:57 2005
@@ -0,0 +1,231 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shale.remote;
+
+import java.io.IOException;
+import java.io.StreamTokenizer;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+import javax.servlet.http.HttpServletRequest;
+import org.apache.commons.chain.Catalog;
+import org.apache.commons.chain.CatalogFactory;
+import org.apache.commons.chain.Command;
+import org.apache.commons.chain.Context;
+import org.apache.shale.faces.ShaleWebContext;
+
+/**
+ * <p>Implementation of <code>Command</code> (from the Commons Chain API)
+ * that executes an appropriate <code>Command</code> or <code>Chain</code>
+ * to implement remote requests from client side components.</p>
+ *
+ * $Id$
+ */
+public class RemoteCommand implements Command {
+    
+
+    // -------------------------------------------------------------- 
Properties
+
+
+    /**
+     * <p>The name of the <code>Catalog</code> in which commands are searched
+     * for to execute specific requests.  The default value is "remote".</p>
+     */
+    private String catalogName = "remote";
+    public String getCatalogName() { return this.catalogName; }
+    public void setCatalogName(String catalogName)
+    { this.catalogName = catalogName; }
+
+
+    /**
+     * <p>Comma-delimited list of regular expression pattern(s) the context
+     * relative URI of this request must match in order to be processed.  The
+     * default value matches any request that ends with ".remote".</p>
+     */
+    private String patterns = "\\S*\\.remote";
+    public String getPatterns() { return this.patterns; }
+    public void setPatterns(String patterns) { this.patterns = patterns; }
+
+
+    // --------------------------------------------------------- Command 
Methods
+
+
+    /**
+     * <p>If the context-relative URI for this request matches one of the
+     * specified regular expression patterns, use the URI to look up the name
+     * of a command in the specified catalog, construct an appropriate
+     * [EMAIL PROTECTED] RemoteContext} for it, execute this command, and 
return
+     * <code>true</code> to indicate that processing for this request has
+     * been completed.  If the context-relative URI does not match one of
+     * the specified regular expression patterns, return <code>false</code>
+     * so that the request will be processed by the remainder of the standard
+     * processing chain.</p>
+     *
+     * @param context <code>Context</code> for this request
+     *
+     * @exception Exception if thrown by a subordinate command
+     */
+    public boolean execute(Context context) throws Exception {
+
+        // Calculate the context-relative URI for this request
+        ShaleWebContext swcontext = (ShaleWebContext) context;
+        HttpServletRequest request = swcontext.getRequest();
+        String servletPath = request.getServletPath();
+        if (servletPath == null) {
+            servletPath = "";
+        }
+        String pathInfo = request.getPathInfo();
+        if (pathInfo == null) {
+            pathInfo = "";
+        }
+        String uri = servletPath + pathInfo;
+
+        // If this URI does not match our pattern(s), return false
+        // to let the rest of the standard processing chain deal with it
+        if (!match(uri)) {
+            return false;
+        }
+
+        // Look up the appropriate command for this request
+        Command command = catalog().getCommand(uri);
+        if (command == null) {
+            // FIXME - report an error somehow
+            return true;
+        }
+
+        // Construct a RemoteContext and execute this command with it
+        // FIXME - do we want to carry over attributes from swcontext too?
+        RemoteContext rcontext =
+          new ServletRemoteContext(swcontext.getContext(),
+                                   swcontext.getRequest(),
+                                   swcontext.getResponse());
+        command.execute(rcontext);
+        return true;
+
+    }
+
+
+    // --------------------------------------------------------- Private 
Methods
+
+
+    /**
+     * <p>The cached <code>Catalog</code> instance in which we will look
+     * up commands to be executed.</p>
+     */
+    private Catalog catalog = null;
+
+
+    /**
+     * <p>Return the <code>Catalog</code> to be used for looking up commands,
+     * caching this value for future use.</p>
+     */
+    private Catalog catalog() {
+
+        if (catalog == null) {
+            catalog = CatalogFactory.getInstance().getCatalog(catalogName);
+        }
+        return catalog;
+
+    }
+
+
+    /**
+     * <p>The cached regular expression pattern(s) we will use for 
matching.</p>
+     */
+    private Pattern match[] = null;
+
+
+    /**
+     * <p>Return <code>true</code> if the specified URI matches one of the
+     * specified regular expressions.</p>
+     *
+     * @param uri Context-relative URI to be matched
+     */
+    private boolean match(String uri) {
+
+        // Precreate our matching patterns if needed
+        if (match == null) {
+            match = precompile(patterns);
+        }
+
+        // Return true if the specified URI matches a pattern
+        for (int i = 0; i < match.length; i++) {
+            if (match[i].matcher(uri).matches()) {
+                return true;
+            }
+        }
+        return false;
+
+    }
+
+
+    /**
+     * <p>Parse the specified string of comma-delimited (and optionally quoted,
+     * if an embedded comma is required) regular expressions into an array
+     * of precompiled <code>Pattern</code> instances that represent these
+     * expressons.</p>
+     *
+     * @param expr Comma-delimited regular expressions
+     */
+    // FIXME refactor so this code is not duplicated from AbstractRegExpFilter
+    private Pattern[] precompile(String expr) {
+
+        if (expr == null) {
+            return new Pattern[0];
+        }
+
+        // Set up to parse the specified expression
+        StreamTokenizer st =
+          new StreamTokenizer(new StringReader(expr));
+        st.eolIsSignificant(false);
+        st.lowerCaseMode(false);
+        st.slashSlashComments(false);
+        st.slashStarComments(false);
+        st.wordChars(0x00, 0xff);
+        st.quoteChar('\'');
+        st.quoteChar('"');
+        st.whitespaceChars(0, ' ');
+        st.whitespaceChars(',', ',');
+        List list = new ArrayList();
+        int type = 0;
+
+        // Parse each included expression
+        while (true) {
+            try {
+                type = st.nextToken();
+            } catch (IOException e) {
+                ; // Can not happen
+            }
+            if (type == st.TT_EOF) {
+                break;
+            } else if (type == st.TT_NUMBER) {
+                list.add(Pattern.compile("" + st.nval));
+            } else if (type == st.TT_WORD) {
+                list.add(Pattern.compile(st.sval.trim()));
+            } else {
+                throw new IllegalArgumentException(expr);
+            }
+        }
+
+        // Return the precompiled patterns as an array
+        return (Pattern[]) list.toArray(new Pattern[list.size()]);
+
+    }
+
+
+}

Added: 
struts/shale/trunk/core-library/src/java/org/apache/shale/remote/RemoteContext.java
Url: 
http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/java/org/apache/shale/remote/RemoteContext.java?view=auto&rev=126200
==============================================================================
--- (empty file)
+++ 
struts/shale/trunk/core-library/src/java/org/apache/shale/remote/RemoteContext.java
 Sat Jan 22 22:55:57 2005
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shale.remote;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.Locale;
+import java.util.Map;
+import org.apache.commons.chain.Context;
+
+/**
+ * <p>[EMAIL PROTECTED] RemoteContext} is an extension of <code>Context</code>
+ * (from the Commons Chain package APIs) that encapsulates the remote
+ * communications requirements of typical remoting applications.  An
+ * instance of this class will be constructed by the framework to wrap
+ * each request, facilitating the use of Commons Chain <code>Command</code>
+ * or <code>Chain</code> instances to perform the requested remote
+ * processing.</p>
+ *
+ * $Id$
+ */
+
+public interface RemoteContext extends Context  {
+    
+    
+    // -------------------------------------------------------------- 
Properties
+
+
+    /**
+     * <p>Return a <code>Map</code> of the application-scope attributes
+     * associated with this request.</p>
+     */
+    public Map getContextAttributes();
+
+
+    /**
+     * <p>Return a <code>Map</code> of the request-scope attributes
+     * associated with this request.</p>
+     */
+    public Map getRequestAttributes();
+
+
+    /**
+     * <p>Return the content length specified by this request.</p>
+     */
+    public int getRequestContentLength();
+
+
+    /**
+     * <p>Return the content type specified by this request.</p>
+     */
+    public String getRequestContentType();
+
+
+    /**
+     * <p>Return the character encoding specified by this request (if any).
+     * Otherwise, return <code>null</code>.</p>
+     */
+    public String getRequestEncoding();
+
+
+    /**
+     * <p>Return the <code>Locale</code> to use for this request.</p>
+     */
+    public Locale getRequestLocale();
+
+
+    /**
+     * <p>Return the HTTP method used to submit this request.</p>
+     */
+    public String getRequestMethod();
+
+
+    /**
+     * <p>Return an immutable <code>Map</code> containing the parameters
+     * submitted with this request, keyed by parameter name.  Values in
+     * this map will be String arrays of the value(s) for the corresponding
+     * parameter name.</p>
+     */
+    public Map getRequestParameters();
+
+
+    /**
+     * <p>Return a <code>Reader</code> that may be used to consume the
+     * character content of this request.</p>
+     *
+     * @exception IllegalStateException if <code>getRequestStream()</code>
+     *  has already been called
+     * @exception IOException if an intput/output error occurs
+     */
+    public Reader getRequestReader() throws IOException;
+
+    
+    /**
+     * <p>Return an <code>InputStream</code> that may be used to consume the
+     * binary content of this request.</p>
+     *
+     * @exception IllegalStateException if <code>getRequestReader()</code>
+     *  has already been called
+     * @exception IOException if an intput/output error occurs
+     */
+    public InputStream getRequestStream() throws IOException;
+
+
+    /**
+     * <p>Return the content length to be specified for this response.</p>
+     */
+    public int getResponseContentLength();
+
+
+    /**
+     * <p>Set the content length to be specified for this response.</p>
+     *
+     * @param contentLength The new content length
+     */
+    public void setResponseContentLength(int contentLength);
+
+
+    /**
+     * <p>Return the content type to be specified for this response.</p>
+     */
+    public String getResponseContentType();
+
+
+    /**
+     * <p>Set the content type to be specified for this response.</p>
+     *
+     * @param contentType The new content type
+     */
+    public void setResponseContentType(String contentType);
+
+
+    /**
+     * <p>Return the character encoding to be specified for this response.</p>
+     */
+    public String getResponseEncoding();
+
+
+    /**
+     * <p>Set the character encoding to be specified for this response.
+     * To be effective, this method <code>MUST</code> be called before
+     * <code>getResponseWriter()</code> is called.</p>
+     *
+     * @param encoding The new character encoding
+     */
+    public void setResponseEncoding(String encoding);
+
+
+    /**
+     * <p>Return an <code>OutputStream</code> that may be used to construct
+     * the binary content of this response.</p>
+     *
+     * @exception IllegalStateException if <code>getResponseWriter()</code>
+     *  has already been called
+     * @exception IOException if an intput/output error occurs
+     */
+    public OutputStream getResponseStream() throws IOException;
+
+
+    /**
+     * <p>Return a <code>Writer</code> that may be used to construct
+     * the character content of this response.</p>
+     *
+     * @exception IllegalStateException if <code>getResponseStream()</code>
+     *  has already been called
+     * @exception IOException if an intput/output error occurs
+     */
+    public Writer getResponseWriter() throws IOException;
+
+
+    /**
+     * <p>Return a <code>Map</code> of the session-scope attributes
+     * associated with this request.  Calling this method will cause a
+     * session to be created, if there is not currently one present.</p>
+     */
+    public Map getSessionAttributes();
+
+
+}

Added: 
struts/shale/trunk/core-library/src/java/org/apache/shale/remote/RequestAttributes.java
Url: 
http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/java/org/apache/shale/remote/RequestAttributes.java?view=auto&rev=126200
==============================================================================
--- (empty file)
+++ 
struts/shale/trunk/core-library/src/java/org/apache/shale/remote/RequestAttributes.java
     Sat Jan 22 22:55:57 2005
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shale.remote;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.servlet.http.HttpServletRequest;
+
+
+/**
+ * <p>Private implementation of <code>Map</code> for servlet request
+ * attributes.</p>
+ *
+ * $Id$
+ */
+
+final class RequestAttributes implements Map {
+
+
+    public RequestAttributes(HttpServletRequest request) {
+        this.request = request;
+    }
+
+
+    private HttpServletRequest request = null;
+
+
+    public void clear() {
+        Iterator keys = keySet().iterator();
+        while (keys.hasNext()) {
+            request.removeAttribute((String) keys.next());
+        }
+    }
+
+
+    public boolean containsKey(Object key) {
+        return (request.getAttribute(key(key)) != null);
+    }
+
+
+    public boolean containsValue(Object value) {
+        if (value == null) {
+            return (false);
+        }
+        Enumeration keys = request.getAttributeNames();
+        while (keys.hasMoreElements()) {
+            Object next = request.getAttribute((String) keys.nextElement());
+            if (next == value) {
+                return (true);
+            }
+        }
+        return (false);
+    }
+
+
+    public Set entrySet() {
+        Set set = new HashSet();
+        Enumeration keys = request.getAttributeNames();
+        String key;
+        while (keys.hasMoreElements()) {
+            key = (String) keys.nextElement();
+            set.add(new MapEntry(key, request.getAttribute(key), true));
+        }
+        return (set);
+    }
+
+
+    public boolean equals(Object o) {
+        return (request.equals(o));
+    }
+
+
+    public Object get(Object key) {
+        return (request.getAttribute(key(key)));
+    }
+
+
+    public int hashCode() {
+        return (request.hashCode());
+    }
+
+
+    public boolean isEmpty() {
+        return (size() < 1);
+    }
+
+
+    public Set keySet() {
+        Set set = new HashSet();
+        Enumeration keys = request.getAttributeNames();
+        while (keys.hasMoreElements()) {
+            set.add(keys.nextElement());
+        }
+        return (set);
+    }
+
+
+    public Object put(Object key, Object value) {
+        if (value == null) {
+            return (remove(key));
+        }
+        String skey = key(key);
+        Object previous = request.getAttribute(skey);
+        request.setAttribute(skey, value);
+        return (previous);
+    }
+
+
+    public void putAll(Map map) {
+        Iterator keys = map.keySet().iterator();
+        while (keys.hasNext()) {
+            String key = (String) keys.next();
+            request.setAttribute(key, map.get(key));
+        }
+    }
+
+
+    public Object remove(Object key) {
+        String skey = key(key);
+        Object previous = request.getAttribute(skey);
+        request.removeAttribute(skey);
+        return (previous);
+    }
+
+
+    public int size() {
+        int n = 0;
+        Enumeration keys = request.getAttributeNames();
+        while (keys.hasMoreElements()) {
+            keys.nextElement();
+            n++;
+        }
+        return (n);
+    }
+
+
+    public Collection values() {
+        List list = new ArrayList();
+        Enumeration keys = request.getAttributeNames();
+        while (keys.hasMoreElements()) {
+            list.add(request.getAttribute((String) keys.nextElement()));
+        }
+        return (list);
+    }
+
+
+    private String key(Object key) {
+        if (key == null) {
+            throw new IllegalArgumentException();
+        } else if (key instanceof String) {
+            return ((String) key);
+        } else {
+            return (key.toString());
+        }
+    }
+
+
+}

Added: 
struts/shale/trunk/core-library/src/java/org/apache/shale/remote/ResponseWrapper.java
Url: 
http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/java/org/apache/shale/remote/ResponseWrapper.java?view=auto&rev=126200
==============================================================================
--- (empty file)
+++ 
struts/shale/trunk/core-library/src/java/org/apache/shale/remote/ResponseWrapper.java
       Sat Jan 22 22:55:57 2005
@@ -0,0 +1,380 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shale.remote;
+
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * <p>Convenience wrapper around a <code>Writer</code> that includes utility
+ * methods useful in constructing XML output.  Appropriate filtering and
+ * wrapping for sensitive characters is performed automatically.</p>
+ *
+ * <p><strong>IMPLEMENTATION NOTE</strong> - This class makes many calls to
+ * the <code>writer.write()</code> method.  Therefore, it is strongly suggested
+ * that you provide a <code>Writer</code> instance that does buffering.</p>
+ *
+ * $Id$
+ */
+public class ResponseWrapper {
+    
+
+    // ------------------------------------------------------------ 
Constructors
+
+
+    /**
+     * <p>Construct a new [EMAIL PROTECTED] ResponseWrapper} wrapping the 
specified
+     * <code>Writer</code>.  The character encoding will be assumed to be
+     * <code>ISO-8859-1</code>.</p>
+     *
+     * @param writer <code>Writer</code> to which our output will be written
+     */
+    public ResponseWrapper(Writer writer) {
+
+        this(writer, "ISO-8859-1");
+
+    }
+
+
+    /**
+     * <p>Construct a new [EMAIL PROTECTED] ResponseWrapper} wrapping the 
specified
+     * <code>Writer</code> that has been configured with the specified
+     * character encoding.</p>
+     *  
+     * @param writer <code>Writer</code> to which our output will be written
+     * @param encoding Character encoding used on this <code>Writer</code>
+     */
+    public ResponseWrapper(Writer writer, String encoding) {
+
+        this.writer = writer;
+        this.encoding = encoding;
+
+    }
+
+
+    // ------------------------------------------------------ Instance 
Variables
+
+
+    /**
+     * <p>The character encoding that has been set on the underlying
+     * <code>Writer</code>.</p>
+     */
+    private String encoding = null;
+
+
+    /**
+     * <p>Flag indicating that we have a currently open element.</p>
+     */
+    private boolean open = false;
+
+
+    /**
+     * <p>The <code>Writer</code> to which our output will be written
+     */
+    private Writer writer = null;
+
+
+    // ---------------------------------------------------------- Public 
Methods
+
+
+    /**
+     * <p>Create any appropriate prologue for the beginning of this 
document.</p>
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void startDocument() throws IOException {
+
+        writer.write("<?xml version=\"1.0\" encoding=\"" + encoding + 
"\"?>\n");
+
+    }
+
+
+    /**
+     * <p>Write the start of an element, up to and including the element name.
+     * Once this method has been called, clients can call the
+     * <code>writeAttrbute()</code> or <code>writeURIAttribute()</code>
+     * methods to add attributes and corresponding values.  The starting
+     * element will be closed (that is, the trailing '>' character added)
+     * on any subsequent call to <code>startElement()</code>,
+     * <code>writeComment()</code>, <code>writeText()</code>,
+     * <code>writeNewline()</code>,
+     * <code>endElement()</code>, or <code>endDocument()</code>.</p>
+     *
+     * @param name Element name to be started
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void startElement(String name) throws IOException {
+
+        close();
+        writer.write('<');
+        writer.write(name);
+        open = true;
+
+    }
+
+
+    /**
+     * <p>Write an attribute name and corresponding value, after converting
+     * that text to a String (if necessary), and after performing any
+     * escaping appropriate for XML.  This method may only be called
+     * after a call to <code>startElement()</code>,
+     * and before the opened element has been closed.</p>
+     *
+     * @param name Attribute name to be written
+     * @param value Attribute value to be written
+     *
+     * @exception IllegalStateException if no element is currently open
+     * @exception IOException if an input/output error occurs
+     */
+    public void writeAttribute(String name, Object value) throws IOException {
+
+        if (!open) {
+            throw new IllegalStateException();
+        }
+        writer.write(" ");
+        writer.write(name);
+        writer.write("=\"");
+        if (value instanceof String) {
+            string(writer, (String) value);
+        } else {
+            string(writer, value.toString());
+        }
+        writer.write("\"");
+
+    }
+
+
+    /**
+     * <p>Write a URI attribute name and corresponding value, after converting
+     * that text to a String (if necessary), and after performing any
+     * escaping appropriate for XML.  This method may only be called
+     * after a call to <code>startElement()</code>,
+     * and before the opened element has been closed.</p>
+     *
+     * <p><strong>IMPLEMENTATION NOTE</strong> - Currently, this just calls
+     * through to <code>writeAttribute()</code>, so it serves as a placeholder
+     * until it is determined whether URIs will need to be treated differently
+     * than other attribute types.</p>
+     *
+     * @param name Attribute name to be written
+     * @param value Attribute value to be written
+     *
+     * @exception IllegalStateException if no element is currently open
+     * @exception IOException if an input/output error occurs
+     */
+    public void writeURIAttribute(String name, Object value) throws 
IOException {
+
+        writeAttribute(name, value);
+
+    }
+
+
+
+    /**
+     * <p>Write a comment containing the specified text, after converting
+     * that text to a String (if necessary).  If there is an open element
+     * that has been created by a call to <code>startElement()</code>, that
+     * element will be closed first.</p>
+     *
+     * @param comment Commented text to be written
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void writeComment(Object comment) throws IOException {
+
+        close();
+        writer.write("<!-- ");
+        if (comment instanceof String) {
+            writer.write((String) comment);
+        } else {
+            writer.write(comment.toString());
+        }
+        writer.write(" -->");
+
+    }
+
+
+    /**
+     * <p>Write a newline to the underlying writer, after closing any
+     * element that is currently open.</p>
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void writeNewline() throws IOException {
+
+        close();
+        writer.write("\n");
+
+    }
+
+
+    /**
+     * <p>Write an object as body text, after converting it to a String
+     * (if necessary), and after performing any escaping appropriate for
+     * XML.  If there is an open element that has been created by a call
+     * to <code>startElement()</code>, that element will be closed first.</p>
+     *
+     * @param text Body text to be written
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void writeText(Object text) throws IOException {
+
+        close();
+        if (text instanceof String) {
+            string(writer, (String) text);
+        } else {
+            string(writer, text.toString());
+        }
+    
+    }
+
+
+    /**
+     * <p>Write the end of an element, after closing any open element created
+     * by a call to <code>startElement()</code>.  Elements must be closed
+     * in the inverse order from which they were opened; it is an error
+     * to do otherwise.</p>
+     *
+     * @param name Element name to be ended
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void endElement(String name) throws IOException {
+
+        if (open) {
+            writer.write("/");
+            writer.close();
+        } else {
+            writer.write("</");
+            writer.write(name);
+            writer.write(">");
+        }
+    }
+
+
+    /**
+     * <p>Finish any appropriate epilogue for the ending of this document,
+     * and flush any remaining content to the writer.  Afterwards, call
+     * <code>flush()</code> on the writer as well.</p>
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public void endDocument() throws IOException {
+
+        close();
+        writer.flush();
+
+    }
+
+
+    // --------------------------------------------------------- Private 
Methods
+
+
+    /**
+     * <p>Close any element that is currently open.</p>
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    private void close() throws IOException {
+
+        if (open) {
+            writer.write(">");
+            open = false;
+        }
+
+    }
+
+
+    /**
+     * <p>Write a numeric character reference for specified character
+     * to the specfied writer.</p>
+     *
+     * @param writer Writer we are writing to
+     * @param ch Character to be translated and appended
+     */
+    private void numeric(Writer writer, char ch) throws IOException {
+
+        writer.write("&#");
+        writer.write(String.valueOf(ch));
+        writer.write(";");
+
+    }
+
+
+    /**
+     * <p>Write the specified string (after performing suitable
+     * replacement of characters by corresponding entities) to the
+     * specified writer.</p>
+     *
+     * @param writer Writer we are writing to
+     * @param s String to be filtered and written
+     */
+    private void string(Writer writer, String s) throws IOException {
+
+        // Process every character in the incoming string
+        int n = s.length();
+        for (int i = 0; i < n; i++) {
+
+            // Process the next character in the text to be rendered
+            char ch = s.charAt(i);
+            if (ch <= 0xff) {
+                // In single byte characters, replace only the five
+                // characters for which well-known entities exist in XML
+                if (ch == 0x22) {
+                    writer.write("&quot;");
+                } else if (ch == 0x26) {
+                    writer.write("&amp;");
+                } else if (ch == 0x27) {
+                    writer.write("&apos;");
+                } else if (ch == 0x3C) {
+                    writer.write("&lt;");
+                } else if (ch == 0X3E) {
+                    writer.write("&gt;");
+                } else {
+                    writer.write(ch);
+                }
+            } else {
+                if (substitution()) {
+                    numeric(writer, ch);
+                } else {
+                    writer.write(ch);
+                }
+            }
+
+        }
+
+    }
+
+
+    /**
+     * <p>Return true if entity substitution should be performed on double
+     * byte character values.</p>
+     */
+    private boolean substitution() {
+
+        if ("UTF-8".equals(encoding) || "UTF-16".equals(encoding)) {
+            return false;
+        } else {
+            return true;
+        }
+
+    }
+
+
+}

Added: 
struts/shale/trunk/core-library/src/java/org/apache/shale/remote/ServletRemoteContext.java
Url: 
http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/java/org/apache/shale/remote/ServletRemoteContext.java?view=auto&rev=126200
==============================================================================
--- (empty file)
+++ 
struts/shale/trunk/core-library/src/java/org/apache/shale/remote/ServletRemoteContext.java
  Sat Jan 22 22:55:57 2005
@@ -0,0 +1,288 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shale.remote;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.Locale;
+import java.util.Map;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.commons.chain.impl.ContextBase;
+
+/**
+ * <p>Implementation of [EMAIL PROTECTED] RemoteContext} suitable for use when 
processing
+ * an HTTP request received via the Servlet API.</p>
+ *
+ * $Id$
+ */
+public class ServletRemoteContext extends ContextBase implements RemoteContext 
{
+    
+
+    // -------------------------------------------------------- Static 
Variables
+
+
+    /**
+     * <p>Buffer size for input and output streams.</p>
+     */
+    private static final int BYTES_BUFFER_SIZE = 1024;
+
+
+    /**
+     * <p>Buffer size for readers and writers.</p>
+     */
+    private static final int CHARS_BUFFER_SIZE = 2048;
+
+
+    // ------------------------------------------------------------- 
Constructor
+
+
+    /**
+     * <p>Construct a new [EMAIL PROTECTED] ServletRemoteContext} relevant for
+     * the specified request, response, and context instances.</p>
+     *
+     * @param context <code>ServletContext</code> for this web application
+     * @param request <code>HttpServletRequest</code> for this request
+     * @parma response <code>HttpServletResponse</code> for this response
+     */
+    public ServletRemoteContext(ServletContext context,
+                                HttpServletRequest request,
+                                HttpServletResponse response) {
+
+        this.context = context;
+        this.request = request;
+        this.response = response;
+
+    }
+
+
+    // ------------------------------------------------------ Instance 
Variables
+
+
+    /**
+     * <p>The <code>ServletContext</code> for this web application.</p>
+     */
+    private ServletContext context = null;
+
+
+    /**
+     * <p>The <code>Map</code> of application scope attributes.</p>
+     */
+    private Map contextAttributes = null;
+
+
+    /**
+     * <p>The <code>HttpServletRequest</code> for this request.</p>
+     */
+    private HttpServletRequest request = null;
+
+
+    /**
+     * <p>The <code>Map</code> of request scope attributes.</p>
+     */
+    private Map requestAttributes = null;
+
+
+    /**
+     * <p>The <code>HttpServletResponse</code> for this response.</p>
+     */
+    private HttpServletResponse response = null;
+
+
+    /**
+     * <p>The <code>Map</code> of session scope attributes.</p>
+     */
+    private Map sessionAttributes = null;
+
+
+    // --------------------------------------------------- RemoteContext 
Methods
+
+
+    // Specified by RemoteContext
+    public Map getContextAttributes() {
+
+        if (contextAttributes == null) {
+            contextAttributes = new ContextAttributes(context);
+        }
+        return contextAttributes;
+
+    }
+
+
+    // Specified by RemoteContext
+    public Map getRequestAttributes() {
+
+        if (requestAttributes == null) {
+            requestAttributes = new RequestAttributes(request);
+        }
+        return requestAttributes;
+
+    }
+
+
+    // Specified by RemoteContext
+    public int getRequestContentLength() {
+
+        return request.getContentLength();
+
+    }
+
+
+    // Specified by RemoteContext
+    public String getRequestContentType() {
+
+        return request.getContentType();
+
+    }
+
+
+    // Specified by RemoteContext
+    public String getRequestEncoding() {
+
+        return request.getCharacterEncoding();
+
+    }
+
+
+    // Specified by RemoteContext
+    public Locale getRequestLocale() {
+
+        return request.getLocale();
+
+    }
+
+
+    // Specified by RemoteContext
+    public String getRequestMethod() {
+
+        return request.getMethod();
+
+    }
+
+
+    // Specified by RemoteContext
+    public Map getRequestParameters() {
+
+        return request.getParameterMap();
+
+    }
+
+
+    // Specified by RemoteContext
+    public Reader getRequestReader() throws IOException {
+
+        return new BufferedReader(request.getReader(), CHARS_BUFFER_SIZE);
+
+    }
+
+    
+    // Specified by RemoteContext
+    public InputStream getRequestStream() throws IOException {
+
+        return new BufferedInputStream(request.getInputStream(), 
BYTES_BUFFER_SIZE);
+
+    }
+
+
+    private int responseContentLength = 0;
+
+
+    // Specified by RemoteContext
+    public int getResponseContentLength() {
+
+        return responseContentLength;
+
+    }
+
+
+    // Specified by RemoteContext
+    public void setResponseContentLength(int contentLength) {
+
+        this.responseContentLength = contentLength;
+        response.setContentLength(contentLength);
+
+    }
+
+
+    // Specified by RemoteContext
+    public String getResponseContentType() {
+
+        return response.getContentType();
+
+    }
+
+
+    // Specified by RemoteContext
+    public void setResponseContentType(String contentType) {
+
+        response.setContentType(contentType);
+
+    }
+
+
+    // Specified by RemoteContext
+    public String getResponseEncoding() {
+
+        return response.getCharacterEncoding();
+
+    }
+
+
+    // Specified by RemoteContext
+    public void setResponseEncoding(String encoding) {
+
+        response.setCharacterEncoding(encoding);
+
+    }
+
+
+    // Specified by RemoteContext
+    public OutputStream getResponseStream() throws IOException {
+
+        return new BufferedOutputStream(response.getOutputStream(), 
BYTES_BUFFER_SIZE);
+
+    }
+
+
+    // Specified by RemoteContext
+    public Writer getResponseWriter() throws IOException {
+
+        return new BufferedWriter(response.getWriter(), CHARS_BUFFER_SIZE);
+
+    }
+
+
+    // Specified by RemoteContext
+    public Map getSessionAttributes() {
+
+        if (sessionAttributes == null) {
+            sessionAttributes = new SessionAttributes(request.getSession());
+        }
+        return sessionAttributes;
+
+    }
+
+
+}

Added: 
struts/shale/trunk/core-library/src/java/org/apache/shale/remote/SessionAttributes.java
Url: 
http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/java/org/apache/shale/remote/SessionAttributes.java?view=auto&rev=126200
==============================================================================
--- (empty file)
+++ 
struts/shale/trunk/core-library/src/java/org/apache/shale/remote/SessionAttributes.java
     Sat Jan 22 22:55:57 2005
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shale.remote;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.servlet.http.HttpSession;
+
+
+/**
+ * <p>Private implementation of <code>Map</code> for HTTP session
+ * attributes.</p>
+ *
+ * $Id$
+ */
+
+final class SessionAttributes implements Map {
+
+
+    public SessionAttributes(HttpSession session) {
+        this.session = session;
+    }
+
+
+    private HttpSession session = null;
+
+
+    public void clear() {
+        Iterator keys = keySet().iterator();
+        while (keys.hasNext()) {
+            session.removeAttribute((String) keys.next());
+        }
+    }
+
+
+    public boolean containsKey(Object key) {
+        return (session.getAttribute(key(key)) != null);
+    }
+
+
+    public boolean containsValue(Object value) {
+        if (value == null) {
+            return (false);
+        }
+        Enumeration keys = session.getAttributeNames();
+        while (keys.hasMoreElements()) {
+            Object next = session.getAttribute((String) keys.nextElement());
+            if (next == value) {
+                return (true);
+            }
+        }
+        return (false);
+    }
+
+
+    public Set entrySet() {
+        Set set = new HashSet();
+        Enumeration keys = session.getAttributeNames();
+        String key;
+        while (keys.hasMoreElements()) {
+            key = (String) keys.nextElement();
+            set.add(new MapEntry(key, session.getAttribute(key), true));
+        }
+        return (set);
+    }
+
+
+    public boolean equals(Object o) {
+        return (session.equals(o));
+    }
+
+
+    public Object get(Object key) {
+        return (session.getAttribute(key(key)));
+    }
+
+
+    public int hashCode() {
+        return (session.hashCode());
+    }
+
+
+    public boolean isEmpty() {
+        return (size() < 1);
+    }
+
+
+    public Set keySet() {
+        Set set = new HashSet();
+        Enumeration keys = session.getAttributeNames();
+        while (keys.hasMoreElements()) {
+            set.add(keys.nextElement());
+        }
+        return (set);
+    }
+
+
+    public Object put(Object key, Object value) {
+        if (value == null) {
+            return (remove(key));
+        }
+        String skey = key(key);
+        Object previous = session.getAttribute(skey);
+        session.setAttribute(skey, value);
+        return (previous);
+    }
+
+
+    public void putAll(Map map) {
+        Iterator keys = map.keySet().iterator();
+        while (keys.hasNext()) {
+            String key = (String) keys.next();
+            session.setAttribute(key, map.get(key));
+        }
+    }
+
+
+    public Object remove(Object key) {
+        String skey = key(key);
+        Object previous = session.getAttribute(skey);
+        session.removeAttribute(skey);
+        return (previous);
+    }
+
+
+    public int size() {
+        int n = 0;
+        Enumeration keys = session.getAttributeNames();
+        while (keys.hasMoreElements()) {
+            keys.nextElement();
+            n++;
+        }
+        return (n);
+    }
+
+
+    public Collection values() {
+        List list = new ArrayList();
+        Enumeration keys = session.getAttributeNames();
+        while (keys.hasMoreElements()) {
+            list.add(session.getAttribute((String) keys.nextElement()));
+        }
+        return (list);
+    }
+
+
+    private String key(Object key) {
+        if (key == null) {
+            throw new IllegalArgumentException();
+        } else if (key instanceof String) {
+            return ((String) key);
+        } else {
+            return (key.toString());
+        }
+    }
+
+
+}

Added: 
struts/shale/trunk/core-library/src/java/org/apache/shale/remote/package.html
Url: 
http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/java/org/apache/shale/remote/package.html?view=auto&rev=126200
==============================================================================
--- (empty file)
+++ 
struts/shale/trunk/core-library/src/java/org/apache/shale/remote/package.html   
    Sat Jan 22 22:55:57 2005
@@ -0,0 +1,25 @@
+<!--
+ * Copyright 2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+
+<!-- $Id$ -->
+
+<body>
+
+<p>This package contains interfaces and APIs to support access for remoting
+(client side components that need to perform server side activities and/or
+retrieve information on a background thread.</p>
+
+</body>

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to