You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by mr...@apache.org on 2007/10/20 16:48:29 UTC

svn commit: r586735 - in /struts/sandbox/trunk/struts2-rest-plugin: ./ src/main/java/org/apache/struts2/rest/ src/main/java/org/apache/struts2/rest/handler/ src/main/resources/ src/test/java/org/apache/struts2/rest/

Author: mrdon
Date: Sat Oct 20 07:48:27 2007
New Revision: 586735

URL: http://svn.apache.org/viewvc?rev=586735&view=rev
Log:
Redesigned rest plugin to fit in better with Struts 2 apps and work with the codebehind plugin

Added:
    struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/ContentTypeHandlerSelector.java
    struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/ContentTypeInterceptor.java
    struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/DefaultRestInfo.java
    struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/ResourceClasspathPackageProvider.java
    struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/RestInfo.java
    struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/handler/ContentTypeHandler.java
    struts/sandbox/trunk/struts2-rest-plugin/src/test/java/org/apache/struts2/rest/RestActionMapperTest.java
Removed:
    struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/BasicRestful.java
    struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/RestActionProxy.java
    struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/Restful.java
    struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/handler/MimeTypeHandler.java
    struts/sandbox/trunk/struts2-rest-plugin/src/test/java/org/apache/struts2/rest/RestActionInvoicationTest.java
Modified:
    struts/sandbox/trunk/struts2-rest-plugin/pom.xml
    struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/RestActionInvocation.java
    struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/RestActionMapper.java
    struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/RestActionProxyFactory.java
    struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/handler/HtmlHandler.java
    struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/handler/XStreamHandler.java
    struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/handler/XStreamJsonHandler.java
    struts/sandbox/trunk/struts2-rest-plugin/src/main/resources/struts-plugin.xml

Modified: struts/sandbox/trunk/struts2-rest-plugin/pom.xml
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-rest-plugin/pom.xml?rev=586735&r1=586734&r2=586735&view=diff
==============================================================================
--- struts/sandbox/trunk/struts2-rest-plugin/pom.xml (original)
+++ struts/sandbox/trunk/struts2-rest-plugin/pom.xml Sat Oct 20 07:48:27 2007
@@ -2,18 +2,21 @@
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
-
+	<parent>
+        <groupId>org.apache.struts</groupId>
+        <artifactId>struts2-plugins</artifactId>
+        <version>2.1.0-SNAPSHOT</version>
+    </parent>
    <groupId>org.apache.struts</groupId>
    <artifactId>struts2-rest-plugin</artifactId>
-   <version>1.0-SNAPSHOT</version>
+   <version>2.1.0-SNAPSHOT</version>
    <name>Struts 2 Plugin</name>
 
     <dependencies>
-
         <dependency>
             <groupId>org.apache.struts</groupId>
-            <artifactId>struts2-core</artifactId>
-            <version>2.0.8</version>
+            <artifactId>struts2-codebehind-plugin</artifactId>
+            <version>${pom.version}</version>
         </dependency>
         <dependency>
             <groupId>com.thoughtworks.xstream</groupId>
@@ -42,28 +45,7 @@
             <version>3.8.1</version>
             <scope>test</scope>
         </dependency>
-         <dependency>
-            <groupId>mockobjects</groupId>
-            <artifactId>mockobjects-core</artifactId>
-            <version>0.09</version>
-            <scope>test</scope>
-        </dependency>
 
     </dependencies>
-
-    <build>
-        <defaultGoal>install</defaultGoal>
-        <pluginManagement>
-            <plugins>
-               <plugin>
-                    <artifactId>maven-compiler-plugin</artifactId>
-                    <configuration>
-                      <source>1.5</source>
-                      <target>1.5</target>
-                    </configuration>
-                </plugin>
-            </plugins>
-       </pluginManagement>
-    </build>
 
 </project>

Added: struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/ContentTypeHandlerSelector.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/ContentTypeHandlerSelector.java?rev=586735&view=auto
==============================================================================
--- struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/ContentTypeHandlerSelector.java (added)
+++ struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/ContentTypeHandlerSelector.java Sat Oct 20 07:48:27 2007
@@ -0,0 +1,49 @@
+package org.apache.struts2.rest;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.struts2.rest.handler.ContentTypeHandler;
+
+import com.opensymphony.xwork2.inject.Container;
+import com.opensymphony.xwork2.inject.Inject;
+
+public class ContentTypeHandlerSelector {
+
+    private Map<String,ContentTypeHandler> handlers = new HashMap<String,ContentTypeHandler>();
+    private String defaultHandlerName;
+
+    @Inject("struts.rest.defaultHandlerName")
+    public void setDefaultHandlerName(String name) {
+        this.defaultHandlerName = name;
+    }
+    
+    @Inject
+    public void setContainer(Container container) {
+        Set<String> names = container.getInstanceNames(ContentTypeHandler.class);
+        for (String name : names) {
+            ContentTypeHandler handler = container.getInstance(ContentTypeHandler.class, name);
+            this.handlers.put(handler.getExtension(), handler);
+        }
+    }
+    
+    public ContentTypeHandler getHandlerForRequest(HttpServletRequest req) {
+        String extension = findExtension(req.getRequestURI());
+        if (extension == null) {
+            extension = defaultHandlerName;
+        }
+        return handlers.get(extension);
+    }
+    
+    protected String findExtension(String url) {
+        int dotPos = url.lastIndexOf('.');
+        int slashPos = url.lastIndexOf('/');
+        if (dotPos > slashPos && dotPos > -1) {
+            return url.substring(dotPos+1);
+        }
+        return null;
+    }
+}

Added: struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/ContentTypeInterceptor.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/ContentTypeInterceptor.java?rev=586735&view=auto
==============================================================================
--- struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/ContentTypeInterceptor.java (added)
+++ struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/ContentTypeInterceptor.java Sat Oct 20 07:48:27 2007
@@ -0,0 +1,41 @@
+package org.apache.struts2.rest;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.struts2.ServletActionContext;
+import org.apache.struts2.rest.handler.ContentTypeHandler;
+
+import com.opensymphony.xwork2.ActionInvocation;
+import com.opensymphony.xwork2.ModelDriven;
+import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.interceptor.Interceptor;
+
+public class ContentTypeInterceptor implements Interceptor {
+
+    ContentTypeHandlerSelector selector;
+    
+    @Inject
+    public void setContentTypeHandlerSelector(ContentTypeHandlerSelector sel) {
+        this.selector = sel;
+    }
+    
+    public void destroy() {}
+
+    public void init() {}
+
+    public String intercept(ActionInvocation invocation) throws Exception {
+        HttpServletRequest request = ServletActionContext.getRequest();
+        ContentTypeHandler handler = selector.getHandlerForRequest(request);
+        
+        Object target = invocation.getAction();
+        if (target instanceof ModelDriven) {
+            target = ((ModelDriven)target).getModel();
+        }
+        
+        if (request.getContentLength() > 0) {
+            handler.toObject(request.getInputStream(), target);
+        }
+        return invocation.invoke();
+    }
+
+}

Added: struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/DefaultRestInfo.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/DefaultRestInfo.java?rev=586735&view=auto
==============================================================================
--- struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/DefaultRestInfo.java (added)
+++ struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/DefaultRestInfo.java Sat Oct 20 07:48:27 2007
@@ -0,0 +1,88 @@
+package org.apache.struts2.rest;
+
+import static javax.servlet.http.HttpServletResponse.*;
+
+import java.util.Date;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+public class DefaultRestInfo implements RestInfo {
+    String resultCode;
+    int status = SC_OK;
+    Object etag;
+    Object locationId;
+    String location;
+    boolean disableCaching;
+    Date lastModified;
+    
+    public DefaultRestInfo renderResult(String code) {
+        this.resultCode = code;
+        return this;
+    }
+    
+    public DefaultRestInfo withStatus(int code) {
+        this.status = code;
+        return this;
+    }
+    
+    public DefaultRestInfo withETag(Object etag) {
+        this.etag = etag;
+        return this;
+    }
+    
+    public DefaultRestInfo setLocationId(Object id) {
+        this.locationId = id;
+        return this;
+    }
+    
+    public DefaultRestInfo setLocation(String loc) {
+        this.location = loc;
+        return this;
+    }
+    
+    public DefaultRestInfo lastModified(Date date) {
+        this.lastModified = date;
+        return this;
+    }
+    
+    public DefaultRestInfo disableCaching() {
+        this.disableCaching = true;
+        return this;
+    }
+    
+    /* (non-Javadoc)
+     * @see org.apache.struts2.rest.RestInfo#apply(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object)
+     */
+    public String apply(HttpServletRequest request, HttpServletResponse response, Object target) {
+        response.setStatus(status);
+        if (disableCaching) {
+            response.setHeader("Cache-Control", "no-cache");
+        } else if (lastModified != null) {
+            response.setDateHeader("LastModified", lastModified.getTime());
+        } else {
+            if (etag == null) {
+                etag = String.valueOf(target.hashCode());
+            }
+            response.setHeader("ETag", etag.toString());
+        }
+        if (locationId != null) {
+            String url = request.getRequestURL().toString();
+            int lastSlash = url.lastIndexOf("/");
+            int lastDot = url.lastIndexOf(".");
+            if (lastDot > lastSlash && lastDot > -1) {
+                url = url.substring(0, lastDot)+locationId+url.substring(lastDot);
+            } else {
+                url += locationId;
+            }
+            response.setHeader("Location", url);
+        } else if (location != null) {
+            response.setHeader("Location", location);
+        }
+        return resultCode;
+    }
+    
+    
+    
+    
+}

Added: struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/ResourceClasspathPackageProvider.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/ResourceClasspathPackageProvider.java?rev=586735&view=auto
==============================================================================
--- struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/ResourceClasspathPackageProvider.java (added)
+++ struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/ResourceClasspathPackageProvider.java Sat Oct 20 07:48:27 2007
@@ -0,0 +1,27 @@
+package org.apache.struts2.rest;
+
+import org.apache.struts2.config.ClasspathPackageProvider;
+
+import com.opensymphony.xwork2.util.ResolverUtil.ClassTest;
+
+/**
+ * Checks for actions ending in Resource indicating a Rest resource
+ */
+public class ResourceClasspathPackageProvider extends ClasspathPackageProvider {
+    
+    @Override
+    protected ClassTest createActionClassTest() {
+        return new ClassTest() {
+            // Match Action implementations and classes ending with "Resource"
+            public boolean matches(Class type) {
+                return (type.getSimpleName().endsWith("Resource"));
+            }
+        };
+    }
+    
+    @Override
+    protected String getClassSuffix() {
+        return "Resource";
+    }
+
+}

Modified: struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/RestActionInvocation.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/RestActionInvocation.java?rev=586735&r1=586734&r2=586735&view=diff
==============================================================================
--- struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/RestActionInvocation.java (original)
+++ struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/RestActionInvocation.java Sat Oct 20 07:48:27 2007
@@ -25,6 +25,7 @@
 import com.opensymphony.xwork2.ActionInvocation;
 import com.opensymphony.xwork2.ActionProxy;
 import com.opensymphony.xwork2.DefaultActionInvocation;
+import com.opensymphony.xwork2.ModelDriven;
 import com.opensymphony.xwork2.ObjectFactory;
 import com.opensymphony.xwork2.Result;
 import com.opensymphony.xwork2.UnknownHandler;
@@ -32,20 +33,21 @@
 import com.opensymphony.xwork2.config.entities.ActionConfig;
 import com.opensymphony.xwork2.config.entities.InterceptorMapping;
 import com.opensymphony.xwork2.config.entities.ResultConfig;
+import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
 import com.opensymphony.xwork2.inject.Container;
 import com.opensymphony.xwork2.inject.Inject;
 import com.opensymphony.xwork2.interceptor.PreResultListener;
 import com.opensymphony.xwork2.util.ValueStack;
 import com.opensymphony.xwork2.util.ValueStackFactory;
-import com.opensymphony.xwork2.util.XWorkConverter;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
 import com.opensymphony.xwork2.util.profiling.UtilTimerStack;
+import com.opensymphony.xwork2.util.logging.Logger;
 
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
 import org.apache.struts2.ServletActionContext;
-import org.apache.struts2.rest.handler.MimeTypeHandler;
+import org.apache.struts2.rest.handler.ContentTypeHandler;
 
 import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
@@ -75,52 +77,24 @@
  */
 public class RestActionInvocation extends DefaultActionInvocation {
     
-    private final Log LOG = LogFactory.getLog(RestActionInvocation.class);
+    private static final long serialVersionUID = 3485701178946428716L;
+
+    private static final Logger LOG = LoggerFactory.getLogger(RestActionInvocation.class);
     
-    private XWorkConverter converter;
-    private Map<String,MimeTypeHandler> handlers = new HashMap<String,MimeTypeHandler>();
-    private String defaultHandlerName;
-
-    protected RestActionInvocation(ObjectFactory objectFactory, UnknownHandler handler, ActionProxy proxy, Map extraContext, boolean pushAction, ActionEventListener actionEventListener) throws Exception {
-        super(objectFactory, handler, proxy, extraContext, pushAction,
-                actionEventListener);
-    }
+    private ContentTypeHandlerSelector handlerSelector;
 
-    protected RestActionInvocation(ObjectFactory objectFactory, UnknownHandler handler, ActionProxy proxy, Map extraContext, boolean pushAction) throws Exception {
-        super(objectFactory, handler, proxy, extraContext, pushAction);
+    protected RestActionInvocation(Map extraContext, boolean pushAction) throws Exception {
+        super(extraContext, pushAction);
     }
 
-    protected RestActionInvocation(ObjectFactory objectFactory, UnknownHandler handler, ActionProxy proxy, Map extraContext) throws Exception {
-        super(objectFactory, handler, proxy, extraContext);
-    }
-    
-    public void setXWorkConverter(XWorkConverter conv) {
-        this.converter = conv;
-    }
-    
-    public void setDefaultHandlerName(String name) {
-        this.defaultHandlerName = name;
+    @Inject
+    public void setMimeTypeHandlerSelector(ContentTypeHandlerSelector sel) {
+        this.handlerSelector = sel;
     }
     
-    public void addMimeTypeHandler(String name, MimeTypeHandler handler) {
-        this.handlers.put(name, handler);
-    }
-
     protected String invokeAction(Object action, ActionConfig actionConfig) throws Exception {
-        
-        HttpServletRequest req = ServletActionContext.getRequest();
-        String extension = findExtension(req.getRequestURI());
-        if (extension == null) {
-            extension = defaultHandlerName;
-        }
-        System.out.println("extension:"+extension);
-        MimeTypeHandler handler = handlers.get(extension);
-        if (handler != null && req.getContentLength() > 0) {
-            invocationContext.getParameters().put("body", handler.toObject(req.getInputStream()));
-        }
-        
         String methodName = proxy.getMethod();
-        
+
         if (LOG.isDebugEnabled()) {
             LOG.debug("Executing action method = " + actionConfig.getMethodName());
         }
@@ -131,36 +105,37 @@
             
             boolean methodCalled = false;
             Object methodResult = null;
-            MethodMatch methodMatch = null;
+            Method method = null;
             try {
-                methodMatch = findMethod(action, methodName, invocationContext.getParameters());
+                method = getAction().getClass().getMethod(methodName, new Class[0]);
             } catch (NoSuchMethodException e) {
                 // hmm -- OK, try doXxx instead
                 try {
                     String altMethodName = "do" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1);
-                    methodMatch = findMethod(action, altMethodName, invocationContext.getParameters());
+                    method = getAction().getClass().getMethod(altMethodName, new Class[0]);
                 } catch (NoSuchMethodException e1) {
-            		throw e;
+                    // well, give the unknown handler a shot
+                    if (unknownHandler != null) {
+                        try {
+                            methodResult = unknownHandler.handleUnknownActionMethod(action, methodName);
+                            methodCalled = true;
+                        } catch (NoSuchMethodException e2) {
+                            // throw the original one
+                            throw e;
+                        }
+                    } else {
+                        throw e;
+                    }
                 }
             }
-        	
-        	if (!methodCalled) {
-                methodResult = callAction(action, methodMatch, invocationContext.getParameters());
-        	}
-        	
-            if (methodResult instanceof Result) {
-            	this.result = (Result) methodResult;
-            	return null;
-            } else {
-                if (handler != null && methodResult != null) {
-                    return handler.fromObject(methodResult, this);
-                } else {
-                    // treat as normal result code
-                    return (String) methodResult;
-                }
+            
+            if (!methodCalled) {
+                methodResult = method.invoke(action, new Object[0]);
             }
+            
+            return processResult(actionConfig, methodResult);
         } catch (NoSuchMethodException e) {
-            throw new IllegalArgumentException("The " + methodName + "() is not defined in action " + getAction().getClass() + "", e);
+            throw new IllegalArgumentException("The " + methodName + "() is not defined in action " + getAction().getClass() + "");
         } catch (InvocationTargetException e) {
             // We try to return the source exception.
             Throwable t = e.getTargetException();
@@ -181,93 +156,41 @@
         }
     }
 
-    protected Object callAction(Object action, MethodMatch methodMatch, Map reqParams) throws IllegalAccessException, InvocationTargetException {
-        Object methodResult;
-        Class[] argTypes = methodMatch.method.getParameterTypes();
-        Object[] args = new Object[argTypes.length];
-        
-        int x=0;
-        for (String name : methodMatch.params) {
-            args[x] = converter.convertValue(null, reqParams.get(name), argTypes[x]);
-            x++;
-        }
-        
-        methodResult = methodMatch.method.invoke(action, args);
-        return methodResult;
-    }
-    
-    protected MethodMatch findMethod(Object action, String actionName, Map<String,String> params) throws NoSuchMethodException {
-        
-        // Short cut so that only restful actions get the full method scan treatment
-        if (action.getClass().getAnnotation(Restful.class) == null && !(action instanceof BasicRestful)) {
-            return new MethodMatch(action.getClass().getMethod(actionName, new Class[0]), Collections.EMPTY_LIST);
-        }
-        Set<String> paramsInReq = params.keySet();
-        
-        int max = -1;
-        Method method = null;
-        List<String> nameParams = null;
-        
-        for (Method m : action.getClass().getMethods()) {
-            String methodName = m.getName();
-            if (methodName.startsWith(actionName)) {
-                int count = 0;
-                List<String> paramsInName = findParamsInName(methodName);
-                for (String paramName : paramsInName) {
-                    if (paramsInReq.contains(paramName)) {
-                        count++;
-                    } else {
-                        count = -1;
-                        break;
-                    }
-                }
+    protected String processResult(ActionConfig actionConfig, Object methodResult) throws IOException {
+        if (methodResult instanceof Result) {
+            this.explicitResult = (Result) methodResult;
+            return null;
+        } else if (methodResult != null) {
+            HttpServletRequest req = ServletActionContext.getRequest();
+            HttpServletResponse res = ServletActionContext.getResponse();
+            ContentTypeHandler handler = handlerSelector.getHandlerForRequest(req);
+            Object target = action;
+            if (target instanceof ModelDriven) {
+                target = ((ModelDriven)target).getModel();
+            }
+            
+            if (methodResult instanceof RestInfo) {
+                RestInfo info = (RestInfo) methodResult;
+                resultCode = info.apply(req, res, target);
+            } else {
+                resultCode = (String) methodResult;
+            }
+            
+            String extCode = resultCode+"-"+handler.getExtension();
+            if (actionConfig.getResults().get(extCode) != null) {
+                resultCode = extCode;
+            } else {
+                ByteArrayOutputStream bout = new ByteArrayOutputStream();
                 
-                if (count > max) {
-                    max = count;
-                    method = m;
-                    nameParams = paramsInName;
+                resultCode = handler.fromObject(target, resultCode, bout);
+                if (bout.size() > 0) {
+                    res.setContentLength(bout.size());
+                    res.setContentType(handler.getContentType());
+                    res.getOutputStream().write(bout.toByteArray());
+                    res.getOutputStream().close();
                 }
             }
         }
-        
-        if (method == null) {
-            throw new NoSuchMethodException("Unable to find method for "+actionName+" with params "+params.keySet());
-        }
-        return new MethodMatch(method, nameParams);
+        return resultCode;
     }
-    
-    protected List<String> findParamsInName(String name) {
-        List<String> list = new ArrayList<String>();
-        int withPos = name.indexOf("With");
-        if (withPos > -1) {
-            String[] params = name.substring(withPos+4).split("And");
-            for (int x=0; x<params.length; x++) {
-                list.add(params[x].toLowerCase());
-            }
-        }
-        return list;
-    }
-    
-    protected String findExtension(String url) {
-        int dotPos = url.lastIndexOf('.');
-        int slashPos = url.lastIndexOf('/');
-        if (dotPos > slashPos && dotPos > -1) {
-            return url.substring(dotPos+1);
-        }
-        return null;
-    }
-    
-    static class MethodMatch {
-        public Method method;
-        public List<String> params;
-        public MethodMatch(Method method, List<String> params) {
-           super();
-           this.method = method;
-           this.params = params;
-        }
-         
-         
-     }
-    
-    
 }

Modified: struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/RestActionMapper.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/RestActionMapper.java?rev=586735&r1=586734&r2=586735&view=diff
==============================================================================
--- struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/RestActionMapper.java (original)
+++ struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/RestActionMapper.java Sat Oct 20 07:48:27 2007
@@ -24,6 +24,8 @@
 import com.opensymphony.xwork2.config.ConfigurationManager;
 import com.opensymphony.xwork2.config.entities.PackageConfig;
 import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
 
 import javax.servlet.http.HttpServletRequest;
 
@@ -34,8 +36,6 @@
 import java.util.StringTokenizer;
 import java.net.URLDecoder;
 
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
 import org.apache.struts2.RequestUtils;
 import org.apache.struts2.StrutsConstants;
 import org.apache.struts2.dispatcher.mapper.ActionMapping;
@@ -80,12 +80,12 @@
  * </p>
  * <ul> 
  *  <li><code>GET:    /movie/               => method="index"</code></li>
- *  <li><code>GET:    /movie/Thrillers      => method="view", id="Thrillers"</code></li>
- *  <li><code>GET:    /movie/Thrillers!edit => method="edit", id="Thrillers"</code></li>
- *  <li><code>GET:    /movie/new            => method="editNew"</code></li>
+ *  <li><code>GET:    /movie/Thrillers      => method="show", id="Thrillers"</code></li>
+ *  <li><code>GET:    /movie/Thrillers;edit => method="input", id="Thrillers"</code></li>
+ *  <li><code>GET:    /movie/new            => method="input"</code></li>
  *  <li><code>POST:   /movie/               => method="create"</code></li>
  *  <li><code>PUT:    /movie/Thrillers      => method="update", id="Thrillers"</code></li>
- *  <li><code>DELETE: /movie/Thrillers      => method="remove", id="Thrillers"</code></li>
+ *  <li><code>DELETE: /movie/Thrillers      => method="destroy", id="Thrillers"</code></li>
  * </ul>
  * <p>
  * To simulate the HTTP methods PUT and DELETE, since they aren't supported by HTML,
@@ -102,41 +102,33 @@
  */
 public class RestActionMapper extends DefaultActionMapper {
 
-    protected static final Log LOG = LogFactory.getLog(RestActionMapper.class);
-    public static final String HTTP_METHOD_PARAM = "__http_method";
-    private String idParameterName = null;
-    private boolean allowDynamicMethodCalls;
-    private List<String> extensions;
+    protected static final Logger LOG = LoggerFactory.getLogger(RestActionMapper.class);
+    public static final String HTTP_METHOD_PARAM = "_method";
+    private String idParameterName = "id";
     
     public RestActionMapper() {
     }
     
-    @Inject(StrutsConstants.STRUTS_ENABLE_DYNAMIC_METHOD_INVOCATION)
-    public void setAllowDynamicMethodCalls(String allow) {
-        allowDynamicMethodCalls = "true".equals(allow);
+    public String getIdParameterName() {
+        return idParameterName;
     }
-    
-    @Inject(StrutsConstants.STRUTS_ACTION_EXTENSION)
-    public void setExtensions(String extensions) {
-        if (!"".equals(extensions)) {
-            this.extensions = Arrays.asList(extensions.split(","));
-        } else {
-            this.extensions = null;
-        }
+
+    @Inject(required=false,value=StrutsConstants.STRUTS_ID_PARAMETER_NAME)
+    public void setIdParameterName(String idParameterName) {
+        this.idParameterName = idParameterName;
     }
     
-    
     public ActionMapping getMapping(HttpServletRequest request,
             ConfigurationManager configManager) {
         ActionMapping mapping = new ActionMapping();
         String uri = getUri(request);
 
-        uri = dropExtension(uri);
+        uri = dropExtension(uri, mapping);
         if (uri == null) {
             return null;
         }
 
-        String fullName = parseNameAndNamespace(uri, mapping, configManager);
+        parseNameAndNamespace(uri, mapping, configManager);
 
         handleSpecialParameters(request, mapping);
 
@@ -144,23 +136,27 @@
             return null;
         }
 
-        if (allowDynamicMethodCalls) {
-            // handle "name!method" convention.
-            String name = mapping.getName();
-            int exclamation = name.lastIndexOf("!");
-            if (exclamation != -1) {
-                mapping.setName(name.substring(0, exclamation));
-                mapping.setMethod(name.substring(exclamation + 1));
-            }
+        // handle "name!method" convention.
+        String name = mapping.getName();
+        int exclamation = name.lastIndexOf("!");
+        if (exclamation != -1) {
+            mapping.setName(name.substring(0, exclamation));
+            mapping.setMethod(name.substring(exclamation + 1));
         }
 
+        String fullName = mapping.getName();
         // Only try something if the action name is specified
         if (fullName != null && fullName.length() > 0) {
             int lastSlashPos = fullName.lastIndexOf('/');
+            String id = null;
+            if (lastSlashPos > -1) {
+                id = fullName.substring(lastSlashPos+1);
+            }
 
             // If a method hasn't been explicitly named, try to guess using ReST-style patterns
             if (mapping.getMethod() == null) {
 
+                // Handle uris ending in '/'
                 if (lastSlashPos == fullName.length() -1) {
 
                     // Index e.g. foo/
@@ -172,75 +168,44 @@
                         mapping.setMethod("create");
                     }
 
-                } else if (lastSlashPos > -1) {
-                    String id = fullName.substring(lastSlashPos+1);
-
+                // Handle uris with an id at the end
+                } else if (id != null) {
+                    
+                    // Viewing the form to edit an item e.g. foo/1;edit
+                    if (isGet(request) && id.endsWith(";edit")) {
+                        id = id.substring(0, id.length() - ";edit".length());
+                        mapping.setMethod("input");
+                        
                     // Viewing the form to create a new item e.g. foo/new
-                    if (isGet(request) && "new".equals(id)) {
-                        mapping.setMethod("editNew");
-
-                    // Viewing an item e.g. foo/1
-                    } else if (isGet(request)) {
-                        mapping.setMethod("view");
+                    } else if (isGet(request) && "new".equals(id)) {
+                        mapping.setMethod("input");
 
                     // Removing an item e.g. foo/1
                     } else if (isDelete(request)) {
-                        mapping.setMethod("remove");
+                        mapping.setMethod("destroy");
+                        
+                    // Viewing an item e.g. foo/1
+                    } else if (isGet(request)) {
+                        mapping.setMethod("show");
                     
                     // Updating an item e.g. foo/1    
                     }  else if (isPut(request)) {
                         mapping.setMethod("update");
                     }
-                    
-                    if (idParameterName != null) {
-                        if (mapping.getParams() == null) {
-                            mapping.setParams(new HashMap());
-                        }
-                        mapping.getParams().put(idParameterName, id);
-                    }
-                }
-                
-                if (idParameterName != null && lastSlashPos > -1) {
-                    fullName = fullName.substring(0, lastSlashPos);
                 }
             }
-
-            // Try to determine parameters from the url before the action name
-            int actionSlashPos = fullName.lastIndexOf('/', lastSlashPos - 1);
-            if (actionSlashPos > 0 && actionSlashPos < lastSlashPos) {
-                String params = fullName.substring(0, actionSlashPos);
-                HashMap<String,String> parameters = new HashMap<String,String>();
-                try {
-                    StringTokenizer st = new StringTokenizer(params, "/");
-                    boolean isNameTok = true;
-                    String paramName = null;
-                    String paramValue;
-
-                    while (st.hasMoreTokens()) {
-                        if (isNameTok) {
-                            paramName = URLDecoder.decode(st.nextToken(), "UTF-8");
-                            isNameTok = false;
-                        } else {
-                            paramValue = URLDecoder.decode(st.nextToken(), "UTF-8");
-
-                            if ((paramName != null) && (paramName.length() > 0)) {
-                                parameters.put(paramName, paramValue);
-                            }
-
-                            isNameTok = true;
-                        }
-                    }
-                    if (parameters.size() > 0) {
-                        if (mapping.getParams() == null) {
-                            mapping.setParams(new HashMap());
-                        }
-                        mapping.getParams().putAll(parameters);
+            
+            // cut off the id parameter, even if a method is specified
+            if (id != null) {
+                if (!"new".equals(id)) {
+                    if (mapping.getParams() == null) {
+                        mapping.setParams(new HashMap());
                     }
-                } catch (Exception e) {
-                    LOG.warn(e);
+                    mapping.getParams().put(idParameterName, new String[]{id});
                 }
-                fullName = fullName.substring(actionSlashPos+1);
+                fullName = fullName.substring(0, lastSlashPos);
             }
+
             mapping.setName(fullName);
         }
 
@@ -248,38 +213,14 @@
     }
     
     /**
-     * Gets the uri from the request
-     *
-     * @param request
-     *            The request
-     * @return The uri
-     */
-    String getUri(HttpServletRequest request) {
-        // handle http dispatcher includes.
-        String uri = (String) request
-                .getAttribute("javax.servlet.include.servlet_path");
-        if (uri != null) {
-            return uri;
-        }
-
-        uri = RequestUtils.getServletPath(request);
-        if (uri != null && !"".equals(uri)) {
-            return uri;
-        }
-
-        uri = request.getRequestURI();
-        return uri.substring(request.getContextPath().length());
-    }
-    
-    /**
-     * Parses the name and namespace from the uri
+     * Parses the name and namespace from the uri.  Doesn't allow slashes in name.
      *
      * @param uri
      *            The uri
      * @param mapping
      *            The action mapping to populate
      */
-    String parseNameAndNamespace(String uri, ActionMapping mapping,
+    protected void parseNameAndNamespace(String uri, ActionMapping mapping,
             ConfigurationManager configManager) {
         String namespace, name;
         int lastSlash = uri.lastIndexOf("/");
@@ -293,66 +234,21 @@
             namespace = "/";
             name = uri.substring(lastSlash + 1);
         } else {
-            // Try to find the namespace in those defined, defaulting to ""
-            Configuration config = configManager.getConfiguration();
-            String prefix = uri.substring(0, lastSlash);
-            namespace = "";
-            // Find the longest matching namespace, defaulting to the default
-            for (Iterator i = config.getPackageConfigs().values().iterator(); i
-                    .hasNext();) {
-                String ns = ((PackageConfig) i.next()).getNamespace();
-                if (ns != null && prefix.startsWith(ns) && (prefix.length() == ns.length() || prefix.charAt(ns.length()) == '/')) {
-                    if (ns.length() > namespace.length()) {
-                        namespace = ns;
-                    }
-                }
-            }
-
-            name = uri.substring(namespace.length() + 1);
-        }
-        String fullName = name;
-
-        if (name != null) {
-            int pos = name.lastIndexOf('/');
-            if (pos > -1 && pos < name.length() - 1) {
-                name = name.substring(pos + 1);
+            int secondToLastSlash = uri.lastIndexOf('/', lastSlash - 1);
+            if (secondToLastSlash == 0) {
+                namespace = "/";
+                name = uri.substring(secondToLastSlash + 1);
+            } else if (secondToLastSlash > -1) {
+                namespace = uri.substring(0, secondToLastSlash);
+                name = uri.substring(secondToLastSlash + 1);
+            } else {
+                namespace = "";
+                name = uri;
             }
         }
 
         mapping.setNamespace(namespace);
         mapping.setName(name);
-        return fullName;
-    }
-
-    /**
-     * Drops the extension from the action name
-     *
-     * @param name
-     *            The action name
-     * @return The action name without its extension
-     */
-    String dropExtension(String name) {
-        if (name != null) {
-            int pos = name.lastIndexOf('.');
-            if (pos > -1) {
-                return name.substring(0, name.lastIndexOf('.'));
-            } else {
-                return name;
-            }
-                 
-        }
-        return null;
-    }
-
-    /**
-     * Returns null if no extension is specified.
-     */
-    String getDefaultExtension() {
-        if (extensions == null) {
-            return null;
-        } else {
-            return (String) extensions.get(0);
-        }
     }
 
     protected boolean isGet(HttpServletRequest request) {
@@ -375,19 +271,8 @@
         if ("delete".equalsIgnoreCase(request.getMethod())) {
             return true;
         } else {
-            return isPost(request) && "delete".equalsIgnoreCase(request.getParameter(HTTP_METHOD_PARAM));
+            return "delete".equalsIgnoreCase(request.getParameter(HTTP_METHOD_PARAM));
         }
     }
-
-	public String getIdParameterName() {
-		return idParameterName;
-	}
-
-	@Inject(required=false,value=StrutsConstants.STRUTS_ID_PARAMETER_NAME)
-	public void setIdParameterName(String idParameterName) {
-		this.idParameterName = idParameterName;
-	}
-    
-    
 
 }

Modified: struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/RestActionProxyFactory.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/RestActionProxyFactory.java?rev=586735&r1=586734&r2=586735&view=diff
==============================================================================
--- struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/RestActionProxyFactory.java (original)
+++ struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/RestActionProxyFactory.java Sat Oct 20 07:48:27 2007
@@ -22,7 +22,9 @@
 
 import java.util.Map;
 
+import com.opensymphony.xwork2.ActionInvocation;
 import com.opensymphony.xwork2.ActionProxy;
+import com.opensymphony.xwork2.DefaultActionInvocation;
 import com.opensymphony.xwork2.DefaultActionProxyFactory;
 import com.opensymphony.xwork2.inject.Container;
 import com.opensymphony.xwork2.inject.Inject;
@@ -34,10 +36,9 @@
 public class RestActionProxyFactory extends DefaultActionProxyFactory {
 
     public ActionProxy createActionProxy(String namespace, String actionName, Map extraContext, boolean executeResult, boolean cleanupContext) throws Exception {
-        ActionProxy proxy = new RestActionProxy(namespace, actionName, extraContext, executeResult, cleanupContext);
-        container.inject(proxy);
-        proxy.prepare();
-        return proxy;
+        ActionInvocation inv = new RestActionInvocation(extraContext, true);
+        container.inject(inv);
+        return createActionProxy(inv, namespace, actionName, extraContext, executeResult, cleanupContext);
     }
 
 }

Added: struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/RestInfo.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/RestInfo.java?rev=586735&view=auto
==============================================================================
--- struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/RestInfo.java (added)
+++ struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/RestInfo.java Sat Oct 20 07:48:27 2007
@@ -0,0 +1,10 @@
+package org.apache.struts2.rest;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+public interface RestInfo {
+
+    String apply(HttpServletRequest request,
+            HttpServletResponse response, Object target);
+}
\ No newline at end of file

Added: struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/handler/ContentTypeHandler.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/handler/ContentTypeHandler.java?rev=586735&view=auto
==============================================================================
--- struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/handler/ContentTypeHandler.java (added)
+++ struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/handler/ContentTypeHandler.java Sat Oct 20 07:48:27 2007
@@ -0,0 +1,37 @@
+/*
+ * $Id: Restful2ActionMapper.java 540819 2007-05-23 02:48:36Z mrdon $
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.struts2.rest.handler;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import com.opensymphony.xwork2.ActionInvocation;
+
+public interface ContentTypeHandler {
+    void toObject(InputStream in, Object target);
+    
+    String fromObject(Object obj, String resultCode, OutputStream stream) throws IOException;
+    
+    String getContentType();
+    
+    String getExtension();
+}

Modified: struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/handler/HtmlHandler.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/handler/HtmlHandler.java?rev=586735&r1=586734&r2=586735&view=diff
==============================================================================
--- struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/handler/HtmlHandler.java (original)
+++ struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/handler/HtmlHandler.java Sat Oct 20 07:48:27 2007
@@ -22,20 +22,27 @@
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.OutputStream;
 import java.util.Collections;
 
 import com.opensymphony.xwork2.Action;
 import com.opensymphony.xwork2.ActionInvocation;
 
-public class HtmlHandler implements MimeTypeHandler {
+public class HtmlHandler implements ContentTypeHandler {
 
-    public String fromObject(Object obj, ActionInvocation inv) throws IOException {
-        inv.getStack().push(Collections.singletonMap("body", obj));
-        return Action.SUCCESS;
+    public String fromObject(Object obj, String resultCode, OutputStream out) throws IOException {
+        return resultCode;
     }
 
-    public Object toObject(InputStream in) {
-        return null;
+    public void toObject(InputStream in, Object target) {
+    }
+
+    public String getExtension() {
+        return "xhtml";
+    }
+
+    public String getContentType() {
+        return "application/xhtml+xml";
     }
 
 }

Modified: struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/handler/XStreamHandler.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/handler/XStreamHandler.java?rev=586735&r1=586734&r2=586735&view=diff
==============================================================================
--- struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/handler/XStreamHandler.java (original)
+++ struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/handler/XStreamHandler.java Sat Oct 20 07:48:27 2007
@@ -32,22 +32,17 @@
 import com.opensymphony.xwork2.ActionInvocation;
 import com.thoughtworks.xstream.XStream;
 
-public class XStreamHandler implements MimeTypeHandler {
+public class XStreamHandler implements ContentTypeHandler {
 
-    public String fromObject(Object obj, ActionInvocation inv) throws IOException {
-        HttpServletResponse response = ServletActionContext.getResponse();
+    public String fromObject(Object obj, String resultCode, OutputStream out) throws IOException {
         XStream xstream = createXStream();
-        ByteArrayOutputStream bout = new ByteArrayOutputStream();
-        xstream.toXML(obj, bout);
-        response.getOutputStream().write(bout.toByteArray());
-        response.setContentLength(bout.size());
-        response.setContentType(getContentType());
+        xstream.toXML(obj, out);
         return null;
     }
 
-    public Object toObject(InputStream in) {
+    public void toObject(InputStream in, Object target) {
         XStream xstream = createXStream();
-        return xstream.fromXML(in);
+        xstream.fromXML(in, target);
     }
     
     protected XStream createXStream() {
@@ -55,7 +50,10 @@
     }
 
     public String getContentType() {
-        return "text/xml";
+        return "application/xml";
     }
 
+    public String getExtension() {
+        return "xml";
+    }
 }

Modified: struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/handler/XStreamJsonHandler.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/handler/XStreamJsonHandler.java?rev=586735&r1=586734&r2=586735&view=diff
==============================================================================
--- struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/handler/XStreamJsonHandler.java (original)
+++ struts/sandbox/trunk/struts2-rest-plugin/src/main/java/org/apache/struts2/rest/handler/XStreamJsonHandler.java Sat Oct 20 07:48:27 2007
@@ -35,5 +35,7 @@
         return "text/javascript";
     }
     
-    
+    public String getExtension() {
+        return "json";
+    }
 }

Modified: struts/sandbox/trunk/struts2-rest-plugin/src/main/resources/struts-plugin.xml
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-rest-plugin/src/main/resources/struts-plugin.xml?rev=586735&r1=586734&r2=586735&view=diff
==============================================================================
--- struts/sandbox/trunk/struts2-rest-plugin/src/main/resources/struts-plugin.xml (original)
+++ struts/sandbox/trunk/struts2-rest-plugin/src/main/resources/struts-plugin.xml Sat Oct 20 07:48:27 2007
@@ -8,10 +8,14 @@
 
     <bean type="com.opensymphony.xwork2.ActionProxyFactory" name="rest" class="org.apache.struts2.rest.RestActionProxyFactory" />
 	<bean type="org.apache.struts2.dispatcher.mapper.ActionMapper" name="rest" class="org.apache.struts2.rest.RestActionMapper" />
+	
+	<bean type="com.opensymphony.xwork2.config.PackageProvider" name="rest" class="org.apache.struts2.rest.ResourceClasspathPackageProvider" />
 
-    <bean type="org.apache.struts2.rest.handler.MimeTypeHandler" name="xml" class="org.apache.struts2.rest.handler.XStreamHandler" />
-    <bean type="org.apache.struts2.rest.handler.MimeTypeHandler" name="js" class="org.apache.struts2.rest.handler.XStreamJsonHandler" />
-    <bean type="org.apache.struts2.rest.handler.MimeTypeHandler" name="html" class="org.apache.struts2.rest.handler.HtmlHandler" />
+	<bean class="org.apache.struts2.rest.ContentTypeHandlerSelector" />
+
+    <bean type="org.apache.struts2.rest.handler.ContentTypeHandler" name="xml" class="org.apache.struts2.rest.handler.XStreamHandler" />
+    <bean type="org.apache.struts2.rest.handler.ContentTypeHandler" name="json" class="org.apache.struts2.rest.handler.XStreamJsonHandler" />
+    <bean type="org.apache.struts2.rest.handler.ContentTypeHandler" name="html" class="org.apache.struts2.rest.handler.HtmlHandler" />
 
     <constant name="struts.actionProxyFactory" value="rest" />
     <constant name="struts.rest.defaultHandlerName" value="xml" />
@@ -21,7 +25,51 @@
     <constant name="struts.configuration.classpath.defaultParentPackage" value="rest-default" />
     
     <package name="rest-default" extends="struts-default">
-    	<default-interceptor-ref name="basicStack"/>
+        <interceptors>
+            <interceptor name="rest" class="org.apache.struts2.rest.ContentTypeInterceptor"/>
+
+            <!-- A complete stack with all the common interceptors in place.
+                 Generally, this stack should be the one you use, though it
+                 may do more than you need. Also, the ordering can be
+                 switched around (ex: if you wish to have your servlet-related
+                 objects applied before prepare() is called, you'd need to move
+                 servlet-config interceptor up.
+
+                 This stack also excludes from the normal validation and workflow
+                 the method names input, back, and cancel. These typically are
+                 associated with requests that should not be validated.
+                 -->
+            <interceptor-stack name="restDefaultStack">
+                <interceptor-ref name="exception"/>
+                <interceptor-ref name="alias"/>
+                <interceptor-ref name="servletConfig"/>
+                <interceptor-ref name="prepare"/>
+                <interceptor-ref name="i18n"/>
+                <interceptor-ref name="chain"/>
+                <interceptor-ref name="debugging"/>
+                <interceptor-ref name="profiling"/>
+                <interceptor-ref name="scopedModelDriven"/>
+                <interceptor-ref name="modelDriven"/>
+                <interceptor-ref name="fileUpload"/>
+                <interceptor-ref name="checkbox"/>
+                <interceptor-ref name="staticParams"/>
+                <interceptor-ref name="params">
+                  <param name="excludeParams">dojo\..*</param>
+                </interceptor-ref>
+                <interceptor-ref name="rest" />
+                <interceptor-ref name="conversionError"/>
+                <interceptor-ref name="validation">
+                    <param name="excludeMethods">input,back,cancel,browse</param>
+                </interceptor-ref>
+                <interceptor-ref name="workflow">
+                    <param name="excludeMethods">input,back,cancel,browse</param>
+                </interceptor-ref>
+            </interceptor-stack>
+    
+        </interceptors>
+    
+    
+    	<default-interceptor-ref name="restDefaultStack"/>
     </package>
 
 </struts>

Added: struts/sandbox/trunk/struts2-rest-plugin/src/test/java/org/apache/struts2/rest/RestActionMapperTest.java
URL: http://svn.apache.org/viewvc/struts/sandbox/trunk/struts2-rest-plugin/src/test/java/org/apache/struts2/rest/RestActionMapperTest.java?rev=586735&view=auto
==============================================================================
--- struts/sandbox/trunk/struts2-rest-plugin/src/test/java/org/apache/struts2/rest/RestActionMapperTest.java (added)
+++ struts/sandbox/trunk/struts2-rest-plugin/src/test/java/org/apache/struts2/rest/RestActionMapperTest.java Sat Oct 20 07:48:27 2007
@@ -0,0 +1,38 @@
+package org.apache.struts2.rest;
+
+import org.apache.struts2.dispatcher.mapper.ActionMapping;
+
+import junit.framework.TestCase;
+
+public class RestActionMapperTest extends TestCase {
+
+    private RestActionMapper mapper;
+    
+    public void setUp() throws Exception {
+        mapper = new RestActionMapper();
+    }
+
+    public void testParseNameAndNamespace() {
+        tryUri("/foo/23", "/", "foo/23");
+        tryUri("/foo/", "/", "foo/");
+        tryUri("foo", "", "foo");
+        tryUri("/", "/", "");
+    }
+    
+    public void testParseNameAndNamespaceWithNamespaces() {
+        tryUri("/ns/foo/23", "/ns", "foo/23");
+        tryUri("/ns/foo/", "/ns", "foo/");
+    }
+    
+    public void testParseNameAndNamespaceWithEdit() {
+        tryUri("/ns/foo/23;edit", "/ns", "foo/23;edit");
+    }
+    
+    private void tryUri(String uri, String expectedNamespace, String expectedName) {
+        ActionMapping mapping = new ActionMapping();
+        mapper.parseNameAndNamespace(uri, mapping, null);
+        assertEquals(expectedName, mapping.getName());
+        assertEquals(expectedNamespace, mapping.getNamespace());
+    }
+
+}