[seam-commits] Seam SVN: r10628 - in branches/community/Seam_2_1: doc/Seam_Reference_Guide/en-US and 5 other directories.

seam-commits at lists.jboss.org seam-commits at lists.jboss.org
Fri Apr 24 09:29:32 EDT 2009


Author: christian.bauer at jboss.com
Date: 2009-04-24 09:29:32 -0400 (Fri, 24 Apr 2009)
New Revision: 10628

Added:
   branches/community/Seam_2_1/examples/restbay/src/org/jboss/seam/example/restbay/resteasy/EventComponentTestResource.java
   branches/community/Seam_2_1/examples/restbay/src/org/jboss/seam/example/restbay/resteasy/PlainTestResource.java
   branches/community/Seam_2_1/examples/restbay/src/org/jboss/seam/example/restbay/resteasy/StatelessEjbTestResource.java
   branches/community/Seam_2_1/examples/restbay/src/org/jboss/seam/example/restbay/resteasy/StatelessEjbTestResourceBean.java
   branches/community/Seam_2_1/examples/restbay/src/org/jboss/seam/example/restbay/resteasy/TestComponent.java
   branches/community/Seam_2_1/src/resteasy/org/jboss/seam/resteasy/SeamResteasyProviderFactory.java
   branches/community/Seam_2_1/src/resteasy/org/jboss/seam/resteasy/SeamResteasyResourceFactory.java
Modified:
   branches/community/Seam_2_1/build/root.pom.xml
   branches/community/Seam_2_1/doc/Seam_Reference_Guide/en-US/Webservices.xml
   branches/community/Seam_2_1/examples/restbay/resources/WEB-INF/components.xml
   branches/community/Seam_2_1/examples/restbay/resources/jboss-rest-bay-ds.xml
   branches/community/Seam_2_1/examples/restbay/src/org/jboss/seam/example/restbay/resteasy/TestResource.java
   branches/community/Seam_2_1/examples/restbay/src/org/jboss/seam/example/restbay/test/BasicServiceTest.java
   branches/community/Seam_2_1/src/resteasy/org/jboss/seam/resteasy/Application.java
   branches/community/Seam_2_1/src/resteasy/org/jboss/seam/resteasy/ResourceHome.java
   branches/community/Seam_2_1/src/resteasy/org/jboss/seam/resteasy/ResourceQuery.java
   branches/community/Seam_2_1/src/resteasy/org/jboss/seam/resteasy/ResteasyBootstrap.java
   branches/community/Seam_2_1/src/resteasy/org/jboss/seam/resteasy/ResteasyDispatcher.java
   branches/community/Seam_2_1/src/resteasy/org/jboss/seam/resteasy/ResteasyResourceAdapter.java
Log:
JBSEAM-3449, JBSEAM-4150, upgrade to RESTEasy 1.1RC2, support for plain EJB resources, bootstrap improvements, more tests in example

Modified: branches/community/Seam_2_1/build/root.pom.xml
===================================================================
--- branches/community/Seam_2_1/build/root.pom.xml	2009-04-24 13:13:27 UTC (rev 10627)
+++ branches/community/Seam_2_1/build/root.pom.xml	2009-04-24 13:29:32 UTC (rev 10628)
@@ -300,7 +300,7 @@
       <dependency>
          <groupId>org.jboss.resteasy</groupId>
          <artifactId>resteasy-jaxrs</artifactId>
-         <version>1.1-RC1</version>
+         <version>1.1-RC2</version>
          <exclusions>
              <exclusion>
                <groupId>commons-codec</groupId>

Modified: branches/community/Seam_2_1/doc/Seam_Reference_Guide/en-US/Webservices.xml
===================================================================
--- branches/community/Seam_2_1/doc/Seam_Reference_Guide/en-US/Webservices.xml	2009-04-24 13:13:27 UTC (rev 10627)
+++ branches/community/Seam_2_1/doc/Seam_Reference_Guide/en-US/Webservices.xml	2009-04-24 13:29:32 UTC (rev 10628)
@@ -340,6 +340,7 @@
      <resteasy:resource-class-names>
          <value>org.foo.MyCustomerResource</value>
          <value>org.foo.MyOrderResource</value>
+         <value>org.foo.MyStatelessEJBImplementation</value>
      </resteasy:resource-class-names>
 
      <resteasy:provider-class-names>
@@ -349,6 +350,18 @@
  </resteasy:application>]]></programlisting>
 
          <para>
+            RESTEasy supports plain EJBs (EJBs that are not Seam components) as resources. Instead of configuring the
+            JNDI names in a non-portable fashion in <literal>web.xml</literal> (see RESTEasy documentation), you can
+            simply list the EJB implementation classes, not the business interfaces, in <literal>components.xml</literal>
+            as shown above. Note that you have to annotate the <literal>@Local</literal> interface of the EJB with
+            <literal>@Path</literal>, <literal>@GET</literal>, and so on - not the bean implementation class. This allows
+            you to keep your application deployment-portable with the global Seam <literal>jndi-pattern</literal> switch
+            on <literal>&lt;core:init/&gt;</literal>. Note that EJB resources will not be found even if scanning of
+            resources is enabled, you always have to list them manually. Again, this is only relevant for EJB resources
+            that are not also Seam components and that do not have a <literal>@Name</literal> annotation.
+         </para>
+
+         <para>
             The <literal>use-built-in-providers</literal> switch enables (default) or disables the RESTEasy built-in
             providers. We recommend you leave them enabled, as they provide plaintext, JSON, and JAXB marshalling
             out of the box.

Modified: branches/community/Seam_2_1/examples/restbay/resources/WEB-INF/components.xml
===================================================================
--- branches/community/Seam_2_1/examples/restbay/resources/WEB-INF/components.xml	2009-04-24 13:13:27 UTC (rev 10627)
+++ branches/community/Seam_2_1/examples/restbay/resources/WEB-INF/components.xml	2009-04-24 13:29:32 UTC (rev 10628)
@@ -35,7 +35,11 @@
                            entity-class="org.jboss.seam.example.restbay.Category"
                            auto-create="true"/>
 
-    <resteasy:application resource-path-prefix="/restv1"/>
+    <resteasy:application resource-path-prefix="/restv1">
+        <resteasy:resource-class-names>
+            <value>org.jboss.seam.example.restbay.resteasy.StatelessEjbTestResourceBean</value>
+        </resteasy:resource-class-names>
+    </resteasy:application>
 
     <resteasy:resource-home path="/configuredCategory" name="configuredCategoryResourceHome"
                             entity-home="#{categoryHome}" entity-id-class="java.lang.Integer"

Modified: branches/community/Seam_2_1/examples/restbay/resources/jboss-rest-bay-ds.xml
===================================================================
--- branches/community/Seam_2_1/examples/restbay/resources/jboss-rest-bay-ds.xml	2009-04-24 13:13:27 UTC (rev 10627)
+++ branches/community/Seam_2_1/examples/restbay/resources/jboss-rest-bay-ds.xml	2009-04-24 13:29:32 UTC (rev 10628)
@@ -1,9 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
 
-<!DOCTYPE datasources
-    PUBLIC "-//JBoss//DTD JBOSS JCA Config 1.5//EN"
-    "http://www.jboss.org/j2ee/dtd/jboss-ds_1_5.dtd">
-
 <datasources>
     <local-tx-datasource>
         <jndi-name>restbayDatasource</jndi-name>

Added: branches/community/Seam_2_1/examples/restbay/src/org/jboss/seam/example/restbay/resteasy/EventComponentTestResource.java
===================================================================
--- branches/community/Seam_2_1/examples/restbay/src/org/jboss/seam/example/restbay/resteasy/EventComponentTestResource.java	                        (rev 0)
+++ branches/community/Seam_2_1/examples/restbay/src/org/jboss/seam/example/restbay/resteasy/EventComponentTestResource.java	2009-04-24 13:29:32 UTC (rev 10628)
@@ -0,0 +1,138 @@
+package org.jboss.seam.example.restbay.resteasy;
+
+import org.jboss.resteasy.annotations.Form;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.In;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.CookieParam;
+import javax.ws.rs.Encoded;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MultivaluedMap;
+import java.util.GregorianCalendar;
+import java.util.List;
+
+/**
+ * @author Christian Bauer
+ */
+ at Name("eventComponentTestResource")
+ at Path("/eventComponentTest")
+public class EventComponentTestResource extends TestResource
+{
+
+   @In
+   TestComponent testComponent;
+
+   @GET
+   @Path("/echouri")
+   @Override
+   public String echoUri()
+   {
+      return super.echoUri();
+   }
+
+   @GET
+   @Path("/echoquery")
+   @Override
+   public String echoQueryParam(@QueryParam("bar") String bar)
+   {
+      return super.echoQueryParam(bar);
+   }
+
+   @GET
+   @Path("/echoheader")
+   @Override
+   public String echoHeaderParam(@HeaderParam("bar") String bar)
+   {
+      return super.echoHeaderParam(bar);
+   }
+
+   @GET
+   @Path("/echocookie")
+   @Override
+   public String echoCookieParam(@CookieParam("bar") String bar)
+   {
+      return super.echoCookieParam(bar);
+   }
+
+   @GET
+   @Path("/echotwoparams/{1}/{2}")
+   @Override
+   public String echoTwoParams(@PathParam("1") String one, @PathParam("2") String two)
+   {
+      return super.echoTwoParams(one, two);
+   }
+
+   @GET
+   @Path("/echoencoded/{val}")
+   @Override
+   public String echoEncoded(@PathParam("val") @Encoded String val)
+   {
+      return super.echoEncoded(val);
+   }
+
+   @POST
+   @Path("/echoformparams")
+   @Consumes("application/x-www-form-urlencoded")
+   @Override
+   public String echoFormParams(MultivaluedMap<String, String> formMap)
+   {
+      return super.echoFormParams(formMap);
+   }
+
+   @POST
+   @Path("/echoformparams2")
+   @Override
+   public String echoFormParams2(@FormParam("foo") String[] foo)
+   {
+      return super.echoFormParams2(foo);
+   }
+
+   @POST
+   @Path("/echoformparams3")
+   @Override
+   public String echoFormParams3(@Form TestForm form)
+   {
+      return super.echoFormParams3(form);
+   }
+
+   @Path("/foo/bar/{baz}")
+   @Override
+   public SubResource getBar(@PathParam("baz") String baz)
+   {
+      return super.getBar(baz);
+   }
+
+   @GET
+   @Path("/convertDate/{isoDate}")
+   @Override
+   public long convertPathParam(@PathParam("isoDate") GregorianCalendar isoDate)
+   {
+      return super.convertPathParam(isoDate);
+   }
+
+   @GET
+   @Path("/trigger/unsupported")
+   @Override
+   public String throwException()
+   {
+      return super.throwException();
+   }
+
+   @GET
+   @Path("/commaSeparated")
+   @Produces("text/csv")
+   @Override
+   public List<String[]> getCommaSeparated()
+   {
+      return testComponent.getCommaSeparated();
+   }
+
+}

Added: branches/community/Seam_2_1/examples/restbay/src/org/jboss/seam/example/restbay/resteasy/PlainTestResource.java
===================================================================
--- branches/community/Seam_2_1/examples/restbay/src/org/jboss/seam/example/restbay/resteasy/PlainTestResource.java	                        (rev 0)
+++ branches/community/Seam_2_1/examples/restbay/src/org/jboss/seam/example/restbay/resteasy/PlainTestResource.java	2009-04-24 13:29:32 UTC (rev 10628)
@@ -0,0 +1,121 @@
+package org.jboss.seam.example.restbay.resteasy;
+
+import org.jboss.resteasy.annotations.Form;
+
+import javax.ws.rs.*;
+import javax.ws.rs.core.MultivaluedMap;
+import java.util.GregorianCalendar;
+import java.util.List;
+
+/**
+ * @author Christian Bauer
+ */
+ at Path("/plainTest")
+public class PlainTestResource extends TestResource
+{
+
+   @GET
+   @Path("/echouri")
+   @Override
+   public String echoUri()
+   {
+      return super.echoUri();
+   }
+
+   @GET
+   @Path("/echoquery")
+   @Override
+   public String echoQueryParam(@QueryParam("bar") String bar)
+   {
+      return super.echoQueryParam(bar);
+   }
+
+   @GET
+   @Path("/echoheader")
+   @Override
+   public String echoHeaderParam(@HeaderParam("bar") String bar)
+   {
+      return super.echoHeaderParam(bar);
+   }
+
+   @GET
+   @Path("/echocookie")
+   @Override
+   public String echoCookieParam(@CookieParam("bar") String bar)
+   {
+      return super.echoCookieParam(bar);
+   }
+
+   @GET
+   @Path("/echotwoparams/{1}/{2}")
+   @Override
+   public String echoTwoParams(@PathParam("1") String one, @PathParam("2") String two)
+   {
+      return super.echoTwoParams(one, two);
+   }
+
+   @GET
+   @Path("/echoencoded/{val}")
+   @Override
+   public String echoEncoded(@PathParam("val") @Encoded String val)
+   {
+      return super.echoEncoded(val);
+   }
+
+   @POST
+   @Path("/echoformparams")
+   @Consumes("application/x-www-form-urlencoded")
+   @Override
+   public String echoFormParams(MultivaluedMap<String, String> formMap)
+   {
+      return super.echoFormParams(formMap);
+   }
+
+   @POST
+   @Path("/echoformparams2")
+   @Override
+   public String echoFormParams2(@FormParam("foo") String[] foo)
+   {
+      return super.echoFormParams2(foo);
+   }
+
+   @POST
+   @Path("/echoformparams3")
+   @Override
+   public String echoFormParams3(@Form TestForm form)
+   {
+      return super.echoFormParams3(form);
+   }
+
+   @Path("/foo/bar/{baz}")
+   @Override
+   public SubResource getBar(@PathParam("baz") String baz)
+   {
+      return super.getBar(baz);
+   }
+
+   @GET
+   @Path("/convertDate/{isoDate}")
+   @Override
+   public long convertPathParam(@PathParam("isoDate") GregorianCalendar isoDate)
+   {
+      return super.convertPathParam(isoDate);
+   }
+
+   @GET
+   @Path("/trigger/unsupported")
+   @Override
+   public String throwException()
+   {
+      return super.throwException();
+   }
+
+   @GET
+   @Path("/commaSeparated")
+   @Produces("text/csv")
+   @Override
+   public List<String[]> getCommaSeparated()
+   {
+      return super.getCommaSeparated();
+   }
+}

Added: branches/community/Seam_2_1/examples/restbay/src/org/jboss/seam/example/restbay/resteasy/StatelessEjbTestResource.java
===================================================================
--- branches/community/Seam_2_1/examples/restbay/src/org/jboss/seam/example/restbay/resteasy/StatelessEjbTestResource.java	                        (rev 0)
+++ branches/community/Seam_2_1/examples/restbay/src/org/jboss/seam/example/restbay/resteasy/StatelessEjbTestResource.java	2009-04-24 13:29:32 UTC (rev 10628)
@@ -0,0 +1,93 @@
+package org.jboss.seam.example.restbay.resteasy;
+
+import org.jboss.resteasy.annotations.Form;
+
+import javax.ejb.Local;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.UriInfo;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.Path;
+import javax.ws.rs.GET;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.CookieParam;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Encoded;
+import javax.ws.rs.POST;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.Produces;
+import java.util.GregorianCalendar;
+import java.util.List;
+
+/**
+ * @author Christian Bauer
+ */
+ at Local
+ at Path("/statelessEjbTest")
+public interface StatelessEjbTestResource
+{
+
+   // TODO: RESTEasy can not inject setter/fields on plain EJBs, see https://jira.jboss.org/jira/browse/RESTEASY-151
+   // That's why we have to do it through parameter injection below
+   @Context
+   public void setUriInfo(UriInfo uriInfo);
+
+   @Context
+   public void setHeaders(HttpHeaders headers);
+
+
+   @GET
+   @Path("/echouri")
+   String echoUri(@Context UriInfo uriInfo); // TODO
+
+   @GET
+   @Path("/echoquery")
+   String echoQueryParam(@QueryParam("bar") String bar);
+
+   @GET
+   @Path("/echoheader")
+   String echoHeaderParam(@HeaderParam("bar") String bar);
+
+   @GET
+   @Path("/echocookie")
+   String echoCookieParam(@CookieParam("bar") String bar);
+
+   @GET
+   @Path("/echotwoparams/{1}/{2}")
+   String echoTwoParams(@PathParam("1") String one, @PathParam("2") String two);
+
+   @GET
+   @Path("/echoencoded/{val}")
+   String echoEncoded(@PathParam("val") @Encoded String val);
+
+   @POST
+   @Path("/echoformparams")
+   @Consumes("application/x-www-form-urlencoded")
+   String echoFormParams(MultivaluedMap<String, String> formMap);
+
+   @POST
+   @Path("/echoformparams2")
+   String echoFormParams2(@FormParam("foo") String[] foo);
+
+   @POST
+   @Path("/echoformparams3")
+   String echoFormParams3(@Form TestForm form);
+
+   @Path("/foo/bar/{baz}")
+   SubResource getBar(@PathParam("baz") String baz);
+
+   @GET
+   @Path("/convertDate/{isoDate}")
+   long convertPathParam(@PathParam("isoDate") GregorianCalendar isoDate);
+
+   @GET
+   @Path("/trigger/unsupported")
+   String throwException();
+
+   @GET
+   @Path("/commaSeparated")
+   @Produces("text/csv")
+   List<String[]> getCommaSeparated(@Context HttpHeaders headers); // TODO
+}

Added: branches/community/Seam_2_1/examples/restbay/src/org/jboss/seam/example/restbay/resteasy/StatelessEjbTestResourceBean.java
===================================================================
--- branches/community/Seam_2_1/examples/restbay/src/org/jboss/seam/example/restbay/resteasy/StatelessEjbTestResourceBean.java	                        (rev 0)
+++ branches/community/Seam_2_1/examples/restbay/src/org/jboss/seam/example/restbay/resteasy/StatelessEjbTestResourceBean.java	2009-04-24 13:29:32 UTC (rev 10628)
@@ -0,0 +1,101 @@
+package org.jboss.seam.example.restbay.resteasy;
+
+import javax.ejb.Stateless;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.UriInfo;
+import java.util.GregorianCalendar;
+import java.util.List;
+
+/**
+ * @author Christian Bauer
+ */
+ at Stateless
+public class StatelessEjbTestResourceBean extends TestResource implements StatelessEjbTestResource
+{
+
+   @javax.annotation.Resource // EJB injection!
+   javax.ejb.SessionContext ejbSessionContext;
+
+
+   public String echoUri(@Context UriInfo uriInfo)
+   {
+      assert ejbSessionContext != null; // Ensure this is executed in the EJB container
+      setUriInfo(uriInfo);
+      return super.echoUri();
+   }
+
+   @Override
+   public String echoQueryParam(String bar)
+   {
+      return super.echoQueryParam(bar);
+   }
+
+   @Override
+   public String echoHeaderParam(String bar)
+   {
+      return super.echoHeaderParam(bar);
+   }
+
+   @Override
+   public String echoCookieParam(String bar)
+   {
+      return super.echoCookieParam(bar);
+   }
+
+   @Override
+   public String echoTwoParams(String one, String two)
+   {
+      return super.echoTwoParams(one, two);
+   }
+
+   @Override
+   public String echoEncoded(String val)
+   {
+      return super.echoEncoded(val);
+   }
+
+   @Override
+   public String echoFormParams(MultivaluedMap<String, String> formMap)
+   {
+      return super.echoFormParams(formMap);
+   }
+
+   @Override
+   public String echoFormParams2(String[] foo)
+   {
+      return super.echoFormParams2(foo);
+   }
+
+   @Override
+   public String echoFormParams3(TestForm form)
+   {
+      return super.echoFormParams3(form);
+   }
+
+   @Override
+   public SubResource getBar(String baz)
+   {
+      return super.getBar(baz);
+   }
+
+   @Override
+   public long convertPathParam(GregorianCalendar isoDate)
+   {
+      return super.convertPathParam(isoDate);
+   }
+
+   @Override
+   public String throwException()
+   {
+      return super.throwException();
+   }
+
+   public List<String[]> getCommaSeparated(@Context HttpHeaders headers)
+   {
+      setHeaders(headers);
+      return super.getCommaSeparated();
+   }
+
+}
\ No newline at end of file

Added: branches/community/Seam_2_1/examples/restbay/src/org/jboss/seam/example/restbay/resteasy/TestComponent.java
===================================================================
--- branches/community/Seam_2_1/examples/restbay/src/org/jboss/seam/example/restbay/resteasy/TestComponent.java	                        (rev 0)
+++ branches/community/Seam_2_1/examples/restbay/src/org/jboss/seam/example/restbay/resteasy/TestComponent.java	2009-04-24 13:29:32 UTC (rev 10628)
@@ -0,0 +1,23 @@
+package org.jboss.seam.example.restbay.resteasy;
+
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.AutoCreate;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * @author Christian Bauer
+ */
+ at Name("testComponent")
+ at AutoCreate
+public class TestComponent
+{
+
+   public List<String[]> getCommaSeparated() {
+      List<String[]> csv = new ArrayList();
+      csv.add(new String[]{"foo", "bar"});
+      csv.add(new String[]{"asdf", "123"});
+      return csv;
+   }
+}

Modified: branches/community/Seam_2_1/examples/restbay/src/org/jboss/seam/example/restbay/resteasy/TestResource.java
===================================================================
--- branches/community/Seam_2_1/examples/restbay/src/org/jboss/seam/example/restbay/resteasy/TestResource.java	2009-04-24 13:13:27 UTC (rev 10627)
+++ branches/community/Seam_2_1/examples/restbay/src/org/jboss/seam/example/restbay/resteasy/TestResource.java	2009-04-24 13:29:32 UTC (rev 10628)
@@ -1,66 +1,67 @@
 package org.jboss.seam.example.restbay.resteasy;
 
-import org.jboss.resteasy.annotations.Form;
-
-import javax.ws.rs.*;
-import javax.ws.rs.core.*;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.UriInfo;
+import java.util.ArrayList;
 import java.util.GregorianCalendar;
 import java.util.List;
-import java.util.ArrayList;
 
 /**
  * Plain JAX RS root resource, no Seam components/lifecycle.
  * 
  * @author Christian Bauer
  */
- at Path("/test")
 public class TestResource
 {
 
    @Context
-   UriInfo uriInfo;
+   protected UriInfo uriInfo;
 
    @Context
-   HttpHeaders headers;
+   protected HttpHeaders headers;
 
-   @GET
-   @Path("/echouri")
+   public void setUriInfo(UriInfo uriInfo)
+   {
+      this.uriInfo = uriInfo;
+   }
+
+   public void setHeaders(HttpHeaders headers)
+   {
+      this.headers = headers;
+   }
+
    public String echoUri()
    {
       return uriInfo.getPath();
    }
 
-   @GET
-   @Path("/echoquery")
-   public String echoQueryParam(@QueryParam("bar") String bar)
+   public String echoQueryParam(String bar)
    {
       return bar;
    }
 
-   @GET
-   @Path("/echoheader")
-   public String echoHeaderParam(@HeaderParam("bar") String bar)
+   public String echoHeaderParam(String bar)
    {
       return bar;
    }
 
-   @GET
-   @Path("/echocookie")
-   public String echoCookieParam(@CookieParam("bar") String bar)
+   public String echoCookieParam(String bar)
    {
       return bar;
    }
 
-   @GET
-   @Path("/echoencoded/{val}")
-   public String echoEncoded(@PathParam("val") @Encoded String val)
+   public String echoTwoParams(String one, String two)
    {
+      return one+two;
+   }
+
+   public String echoEncoded(String val)
+   {
       return val;
    }
 
-   @POST
-   @Path("/echoformparams")
-   @Consumes("application/x-www-form-urlencoded")
    public String echoFormParams(MultivaluedMap<String, String> formMap)
    {
       String result = "";
@@ -71,9 +72,7 @@
       return result;
    }
 
-   @POST
-   @Path("/echoformparams2")
-   public String echoFormParams2(@FormParam("foo") String[] foo)
+   public String echoFormParams2(String[] foo)
    {
       String result = "";
       for (String s : foo)
@@ -83,44 +82,31 @@
       return result;
    }
 
-   @POST
-   @Path("/echoformparams3")
-   public String echoFormParams3(@Form TestForm form)
+   public String echoFormParams3(TestForm form)
    {
       return form.toString();
    }
 
-   @Path("/foo/bar/{baz}")
-   public SubResource getBar(@PathParam("baz") String baz)
+   public SubResource getBar(String baz)
    {
       return new SubResource(baz);
    }
 
-   @GET
-   @Path("/foo/{isoDate}")
-   public long convertPathParam(@PathParam("isoDate") GregorianCalendar isoDate)
+   public long convertPathParam(GregorianCalendar isoDate)
    {
       return isoDate.getTime().getTime();
    }
 
-   @GET
-   @Path("/foo/unsupported")
    public String throwException()
    {
       throw new UnsupportedOperationException("foo");
    }
 
-   @GET
-   @Path("/foo/commaseparated")
-   @Produces("text/csv")
    public List<String[]> getCommaSeparated() {
       assert headers.getAcceptableMediaTypes().size() == 2;
       assert headers.getAcceptableMediaTypes().get(0).toString().equals("text/plain");
       assert headers.getAcceptableMediaTypes().get(1).toString().equals("text/csv");
-      List<String[]> csv = new ArrayList();
-      csv.add(new String[]{"foo", "bar"});
-      csv.add(new String[]{"asdf", "123"});
-      return csv;
+      return new TestComponent().getCommaSeparated();
    }
 
 

Modified: branches/community/Seam_2_1/examples/restbay/src/org/jboss/seam/example/restbay/test/BasicServiceTest.java
===================================================================
--- branches/community/Seam_2_1/examples/restbay/src/org/jboss/seam/example/restbay/test/BasicServiceTest.java	2009-04-24 13:13:27 UTC (rev 10627)
+++ branches/community/Seam_2_1/examples/restbay/src/org/jboss/seam/example/restbay/test/BasicServiceTest.java	2009-04-24 13:29:32 UTC (rev 10628)
@@ -4,6 +4,7 @@
 import org.jboss.seam.example.restbay.test.fwk.MockHttpServletResponse;
 import org.jboss.seam.example.restbay.test.fwk.MockHttpServletRequest;
 import org.testng.annotations.Test;
+import org.testng.annotations.DataProvider;
 import static org.testng.Assert.assertEquals;
 
 import javax.servlet.http.Cookie;
@@ -23,10 +24,20 @@
       }};
    }
 
-   @Test
-   public void testExeptionMapping() throws Exception
+   @DataProvider(name = "queryPaths")
+   public Object[][] getData()
    {
-      new ResourceRequest(Method.GET, "/restv1/test/foo/unsupported")
+      return new String[][] {
+            { "/restv1/plainTest" },
+            { "/restv1/eventComponentTest" },
+            { "/restv1/statelessEjbTest" }
+      };
+   }
+
+   @Test(dataProvider = "queryPaths")
+   public void testExeptionMapping(final String resourcePath) throws Exception
+   {
+      new ResourceRequest(Method.GET, resourcePath + "/trigger/unsupported")
       {
 
          @Override
@@ -40,22 +51,22 @@
 
    }
 
-   @Test
-   public void testPlainResources() throws Exception
+   @Test(dataProvider = "queryPaths")
+   public void testEchos(final String resourcePath) throws Exception
    {
-      new ResourceRequest(Method.GET, "/restv1/test/echouri")
+      new ResourceRequest(Method.GET, resourcePath + "/echouri")
       {
 
          @Override
          protected void onResponse(MockHttpServletResponse response)
          {
             assert response.getStatus() == 200;
-            assert response.getContentAsString().equals("/test/echouri");
+            assert response.getContentAsString().endsWith("/echouri");
          }
 
       }.run();
 
-      new ResourceRequest(Method.GET, "/restv1/test/echoquery")
+      new ResourceRequest(Method.GET, resourcePath + "/echoquery")
       {
 
          @Override
@@ -75,7 +86,7 @@
 
       }.run();
 
-      new ResourceRequest(Method.GET, "/restv1/test/echoheader")
+      new ResourceRequest(Method.GET, resourcePath + "/echoheader")
       {
 
          @Override
@@ -93,7 +104,7 @@
 
       }.run();
 
-      new ResourceRequest(Method.GET, "/restv1/test/echocookie")
+      new ResourceRequest(Method.GET, resourcePath + "/echocookie")
       {
 
          @Override
@@ -111,7 +122,7 @@
 
       }.run();
 
-      new ResourceRequest(Method.GET, "/restv1/test/foo/bar/asdf")
+      new ResourceRequest(Method.GET, resourcePath + "/foo/bar/asdf")
       {
 
          @Override
@@ -124,12 +135,24 @@
 
       }.run();
 
+      new ResourceRequest(Method.GET, resourcePath + "/echotwoparams/foo/bar")
+      {
+
+         @Override
+         protected void onResponse(MockHttpServletResponse response)
+         {
+            assert response.getStatus() == 200;
+            assert response.getContentAsString().equals("foobar");
+         }
+
+      }.run();
+
    }
 
-   @Test
-   public void testEncoding() throws Exception
+   @Test(dataProvider = "queryPaths")
+   public void testEncoding(final String resourcePath) throws Exception
    {
-      new ResourceRequest(Method.GET, "/restv1/test/echoencoded/foo bar")
+      new ResourceRequest(Method.GET, resourcePath + "/echoencoded/foo bar")
       {
 
          @Override
@@ -142,10 +165,10 @@
       }.run();
    }
 
-   @Test
-   public void testFormHandling() throws Exception
+   @Test(dataProvider = "queryPaths")
+   public void testFormHandling(final String resourcePath) throws Exception
    {
-      new ResourceRequest(Method.POST, "/restv1/test/echoformparams")
+      new ResourceRequest(Method.POST, resourcePath + "/echoformparams")
       {
 
          @Override
@@ -164,7 +187,7 @@
 
       }.run();
 
-      new ResourceRequest(Method.POST, "/restv1/test/echoformparams2")
+      new ResourceRequest(Method.POST, resourcePath + "/echoformparams2")
       {
 
          @Override
@@ -183,7 +206,7 @@
 
       }.run();
 
-      new ResourceRequest(Method.POST, "/restv1/test/echoformparams3")
+      new ResourceRequest(Method.POST, resourcePath + "/echoformparams3")
       {
 
          @Override
@@ -205,13 +228,13 @@
 
    }
 
-   @Test
-   public void testStringConverter() throws Exception
+   @Test(dataProvider = "queryPaths")
+   public void testStringConverter(final String resourcePath) throws Exception
    {
       final String ISO_DATE = "2007-07-10T14:54:56-0500";
       final String ISO_DATE_MILLIS = "1184097296000";
 
-      new ResourceRequest(Method.GET, "/restv1/test/foo/" + ISO_DATE)
+      new ResourceRequest(Method.GET, resourcePath + "/convertDate/" + ISO_DATE)
       {
 
          @Override
@@ -225,11 +248,11 @@
 
    }
 
-   @Test
-   public void testProvider() throws Exception
+   @Test(dataProvider = "queryPaths")
+   public void testProvider(final String resourcePath) throws Exception
    {
 
-      new ResourceRequest(Method.GET, "/restv1/test/foo/commaseparated")
+      new ResourceRequest(Method.GET, resourcePath + "/commaSeparated")
       {
 
          @Override

Modified: branches/community/Seam_2_1/src/resteasy/org/jboss/seam/resteasy/Application.java
===================================================================
--- branches/community/Seam_2_1/src/resteasy/org/jboss/seam/resteasy/Application.java	2009-04-24 13:13:27 UTC (rev 10627)
+++ branches/community/Seam_2_1/src/resteasy/org/jboss/seam/resteasy/Application.java	2009-04-24 13:29:32 UTC (rev 10628)
@@ -23,8 +23,8 @@
 public class Application extends javax.ws.rs.core.Application
 {
 
-   private Map<Class<?>, Set<Component>> providerClasses = new HashMap<Class<?>, Set<Component>>();
-   private Map<Class<?>, Set<Component>> resourceClasses = new HashMap<Class<?>, Set<Component>>();
+   final private Map<Class<?>, Set<Component>> providerClasses = new HashMap<Class<?>, Set<Component>>();
+   final private Map<Class<?>, Set<Component>> resourceClasses = new HashMap<Class<?>, Set<Component>>();
    
     private List<String> providerClassNames = new ArrayList<String>();
     private List<String> resourceClassNames = new ArrayList<String>();

Modified: branches/community/Seam_2_1/src/resteasy/org/jboss/seam/resteasy/ResourceHome.java
===================================================================
--- branches/community/Seam_2_1/src/resteasy/org/jboss/seam/resteasy/ResourceHome.java	2009-04-24 13:13:27 UTC (rev 10627)
+++ branches/community/Seam_2_1/src/resteasy/org/jboss/seam/resteasy/ResourceHome.java	2009-04-24 13:29:32 UTC (rev 10628)
@@ -42,7 +42,6 @@
 
 import org.jboss.resteasy.core.Headers;
 import org.jboss.resteasy.core.StringParameterInjector;
-import org.jboss.resteasy.spi.ResteasyProviderFactory;
 import org.jboss.seam.Entity;
 import org.jboss.seam.annotations.Create;
 import org.jboss.seam.framework.Home;
@@ -59,7 +58,9 @@
  * @param <T> Entity class
  * @param <T2> Entity id class
  */
- at Path("")
+// Empty @Path because it's ignored by second-stage bootstrap if not subclassed or in components.xml
+// but we need it as a marker so we'll find components.xml declarations during first stage of bootstrap.
+ at Path("") 
 public class ResourceHome<T, T2> extends AbstractResource<T>
 {
    private EntityHomeWrapper<T> entityHome = null;
@@ -263,7 +264,7 @@
    private T unmarshallEntity(InputStream is)
    {
       Class<T> entityClass = getEntityClass();
-      MessageBodyReader<T> reader = ResteasyProviderFactory.getInstance().getMessageBodyReader(entityClass, entityClass, entityClass.getAnnotations(), requestContentType);
+      MessageBodyReader<T> reader = SeamResteasyProviderFactory.getInstance().getMessageBodyReader(entityClass, entityClass, entityClass.getAnnotations(), requestContentType);
       if (reader == null)
       {
          throw new RuntimeException("Unable to find MessageBodyReader for content type " + requestContentType);
@@ -288,7 +289,7 @@
     */
    private T2 unmarshallId(String id)
    {
-      StringParameterInjector injector = new StringParameterInjector(getEntityIdClass(), getEntityIdClass(), "id", PathParam.class, null, null, ResteasyProviderFactory.getInstance());
+      StringParameterInjector injector = new StringParameterInjector(getEntityIdClass(), getEntityIdClass(), "id", PathParam.class, null, null, SeamResteasyProviderFactory.getInstance());
       return (T2) injector.extractValue(id);
    }
 

Modified: branches/community/Seam_2_1/src/resteasy/org/jboss/seam/resteasy/ResourceQuery.java
===================================================================
--- branches/community/Seam_2_1/src/resteasy/org/jboss/seam/resteasy/ResourceQuery.java	2009-04-24 13:13:27 UTC (rev 10627)
+++ branches/community/Seam_2_1/src/resteasy/org/jboss/seam/resteasy/ResourceQuery.java	2009-04-24 13:29:32 UTC (rev 10628)
@@ -48,6 +48,8 @@
  * 
  * @param <T> entity type
  */
+// Empty @Path because it's ignored by second-stage bootstrap if not subclassed or in components.xml
+// but we need it as a marker so we'll find components.xml declarations during first stage of bootstrap.
 @Path("")
 public class ResourceQuery<T> extends AbstractResource<T>
 {
@@ -109,8 +111,7 @@
 
          public Type[] getActualTypeArguments()
          {
-            Type[] types = { getEntityClass() };
-            return types;
+            return new Type[] { getEntityClass() };
          }
       };
       return Response.ok(new GenericEntity(result, responseType)

Modified: branches/community/Seam_2_1/src/resteasy/org/jboss/seam/resteasy/ResteasyBootstrap.java
===================================================================
--- branches/community/Seam_2_1/src/resteasy/org/jboss/seam/resteasy/ResteasyBootstrap.java	2009-04-24 13:13:27 UTC (rev 10627)
+++ branches/community/Seam_2_1/src/resteasy/org/jboss/seam/resteasy/ResteasyBootstrap.java	2009-04-24 13:29:32 UTC (rev 10628)
@@ -2,12 +2,21 @@
 
 import org.jboss.seam.Component;
 import org.jboss.seam.ScopeType;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.Startup;
+import org.jboss.seam.annotations.AutoCreate;
+import org.jboss.seam.annotations.Install;
+import org.jboss.seam.annotations.Logger;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.Create;
 import org.jboss.seam.deployment.AnnotationDeploymentHandler;
 import org.jboss.seam.deployment.DeploymentStrategy;
-import org.jboss.seam.annotations.*;
 import org.jboss.seam.contexts.Contexts;
 import org.jboss.seam.log.Log;
 import org.jboss.seam.util.Reflections;
+import org.jboss.seam.util.EJB;
+import org.jboss.resteasy.core.ThreadLocalResteasyProviderFactory;
 
 import java.util.Collection;
 import java.util.HashSet;
@@ -17,6 +26,8 @@
 
 /**
  * Scans annoated JAX-RS resources and providers, optionally registers them as Seam components.
+ * It does so by populating the <tt>Application</tt> instance, which is then processed further
+ * by the <tt>ResteasyDispatcher</tt> during startup.
  *
  * @author Christian Bauer
  */
@@ -24,7 +35,7 @@
 @Scope(ScopeType.APPLICATION)
 @Startup
 @AutoCreate
- at Install(classDependencies = "org.jboss.resteasy.core.Dispatcher")
+ at Install(classDependencies = "org.jboss.resteasy.spi.ResteasyProviderFactory")
 public class ResteasyBootstrap
 {
 
@@ -34,11 +45,23 @@
     @In
     protected Application application;
 
+    private SeamResteasyProviderFactory providerFactory;
+    public SeamResteasyProviderFactory getProviderFactory()
+    {
+        return providerFactory;
+    }
+
     @Create
-    public void onStartup()
+    public void init()
     {
-        log.info("deploying RESTEasy providers and resources");
+        log.info("starting RESTEasy with custom SeamResteasyProviderFactory");
+        providerFactory = new SeamResteasyProviderFactory();
 
+        // Always use the "deployment sensitive" factory - that means it is handled through ThreadLocal, not static
+        SeamResteasyProviderFactory.setInstance(new ThreadLocalResteasyProviderFactory(getProviderFactory()));
+
+        log.info("deploying JAX-RS application");
+
         Collection<Class<?>> annotatedProviderClasses = null;
         Collection<Class<?>> annotatedResourceClasses = null;
         if (application.isScanProviders() || application.isScanResources())
@@ -162,9 +185,16 @@
                 log.debug("adding resource Seam components {0} for class {1}", components, resourceClass);
                 application.addResourceClass(resourceClass, components);
             }
+            // Check if it is a @Path annotated EJB interface
+            else if (resourceClass.isAnnotationPresent(EJB.LOCAL) ||
+                    resourceClass.isAnnotationPresent(EJB.REMOTE))
+            {
+                log.debug("ignoring @Path annotated EJB interface, add the bean " +
+                          "implementation to <resteasy:resource-class-names/>: " + resourceClass.getName());
+            }
             else
             {
-                log.debug("adding resource class with JAX-RS default lifecycle: " + resourceClass.getName());
+                log.debug("adding resource class: " + resourceClass.getName());
                 application.addResourceClass(resourceClass);
             }
         }

Modified: branches/community/Seam_2_1/src/resteasy/org/jboss/seam/resteasy/ResteasyDispatcher.java
===================================================================
--- branches/community/Seam_2_1/src/resteasy/org/jboss/seam/resteasy/ResteasyDispatcher.java	2009-04-24 13:13:27 UTC (rev 10627)
+++ branches/community/Seam_2_1/src/resteasy/org/jboss/seam/resteasy/ResteasyDispatcher.java	2009-04-24 13:29:32 UTC (rev 10628)
@@ -1,30 +1,35 @@
 package org.jboss.seam.resteasy;
 
-import java.util.Set;
-
+import org.jboss.resteasy.core.SynchronousDispatcher;
+import org.jboss.resteasy.plugins.providers.RegisterBuiltin;
+import org.jboss.resteasy.plugins.server.resourcefactory.POJOResourceFactory;
+import org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher;
+import org.jboss.resteasy.spi.ResourceFactory;
+import org.jboss.resteasy.spi.StringConverter;
 import org.jboss.seam.Component;
 import org.jboss.seam.ScopeType;
-import org.jboss.seam.contexts.Contexts;
+import org.jboss.seam.Seam;
+import org.jboss.seam.core.Init;
+import org.jboss.seam.util.EJB;
 import org.jboss.seam.annotations.AutoCreate;
 import org.jboss.seam.annotations.Create;
 import org.jboss.seam.annotations.In;
-import org.jboss.seam.annotations.Install;
 import org.jboss.seam.annotations.Logger;
 import org.jboss.seam.annotations.Name;
 import org.jboss.seam.annotations.Scope;
 import org.jboss.seam.annotations.Startup;
+import org.jboss.seam.annotations.JndiName;
 import org.jboss.seam.log.Log;
-import org.jboss.resteasy.core.AsynchronousDispatcher;
-import org.jboss.resteasy.core.PropertyInjectorImpl;
-import org.jboss.resteasy.plugins.providers.RegisterBuiltin;
-import org.jboss.resteasy.plugins.server.resourcefactory.POJOResourceFactory;
-import org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher;
-import org.jboss.resteasy.spi.*;
 
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import java.util.Set;
+
 /**
  * An extended version of the RESTEasy dispatcher, configured on Seam application
  * startup with a custom JAX RS <tt>Application</tt> instance. Registers custom resource
- * and provider lifecycle handlers.
+ * and provider lifecycle handlers with RESTEasy, depending on configured/detected resources
+ * from <tt>ResteasyBootstrap</tt>.
  *
  * @author Christian Bauer
  */
@@ -32,25 +37,41 @@
 @Scope(ScopeType.APPLICATION)
 @Startup(depends = "org.jboss.seam.resteasy.bootstrap")
 @AutoCreate
- at Install(classDependencies = "org.jboss.resteasy.core.Dispatcher")
 public class ResteasyDispatcher extends HttpServletDispatcher
 {
 
     @In
-    Application application;
+    protected Application application;
 
+    @In
+    protected ResteasyBootstrap bootstrap;
+
     @Logger
-    Log log;
+    private Log log;
 
     @Create
-    public void onStartup()
+    public void init()
     {
+        try
+        {
+            init(null);
+        }
+        catch (ServletException ex)
+        {
+            // Can never happen
+        }
+    }
+
+    @Override
+    public void init(ServletConfig servletConfig) throws ServletException
+    {
+        // We can skip the init, it only sets up the provider factory (did that already in bootstrap) and
+        // the other stuff we do in onStartup(). We also do NOT put the ResteasyProviderFactory, the Dispatcher,
+        // nor the Registry into the servlet context. Let's hope RESTEasy code is sane enough not to access the
+        // servlet context at runtime...
         log.debug("registering RESTEasy and JAX RS resources and providers");
+        setDispatcher(new SynchronousDispatcher(bootstrap.getProviderFactory()));
 
-        ResteasyProviderFactory providerFactory = new ResteasyProviderFactory();
-        ResteasyProviderFactory.setInstance(providerFactory); // This is really necessary
-        setDispatcher(new AsynchronousDispatcher(providerFactory));
-
         getDispatcher().setLanguageMappings(application.getLanguageMappings());
         getDispatcher().setMediaTypeMappings(application.getMediaTypeMappings());
 
@@ -58,145 +79,160 @@
         if (application.isUseBuiltinProviders())
         {
             log.info("registering built-in RESTEasy providers");
-            RegisterBuiltin.register(providerFactory);
+            RegisterBuiltin.register(getDispatcher().getProviderFactory());
         }
+
         for (Class providerClass : application.getProviderClasses())
         {
-            Set<Component> seamComponents = application.getProviderClassComponent(providerClass);
-            if (seamComponents != null)
+            Set<Component> components = application.getProviderClassComponent(providerClass);
+            if (components != null)
             {
-                for (Component seamComponent : seamComponents)
-                {
-                   if (ScopeType.STATELESS.equals(seamComponent.getScope()))
-                   {
-                      throw new RuntimeException(
-                               "Registration of STATELESS Seam components as RESTEasy providers not implemented!"
-                      );
-                   }
-                   else if (ScopeType.APPLICATION.equals(seamComponent.getScope()))
-                   {
-                      Object providerInstance = Component.getInstance(seamComponent.getName());
-                      boolean isStringConverter = false;
-                      for (Class componentIface : seamComponent.getBusinessInterfaces())
-                      {
-                         if (StringConverter.class.isAssignableFrom(componentIface))
-                         {
-                            isStringConverter = true;
-                            break;
-                         }
-                      }
-                      if (isStringConverter)
-                      {
-                         log.error("can't register Seam component as RESTEasy StringConverter, see: https://jira.jboss.org/jira/browse/JBSEAM-4020");
-                         //log.debug("registering Seam component as custom RESTEasy string converter provider: " + seamComponent.getName());
-                         //providerFactory.addStringConverter((StringConverter)providerInstance);
-                      }
-                      else
-                      {
-                         providerFactory.registerProviderInstance(providerInstance);
-                      }
-                   }
-                }
+                registerSeamComponentProviders(components, providerClass);
             }
             else
             {
-                // Just plain RESTEasy, no Seam component lookup or lifecycle
-                if (StringConverter.class.isAssignableFrom(providerClass))
-                {
-                    log.debug("registering as custom RESTEasy string converter provider class: " + providerClass);
-                    providerFactory.addStringConverter(providerClass);
-                }
-                else
-                {
-                    providerFactory.registerProvider(providerClass);
-                }
+                registerProvider(providerClass);
             }
         }
 
         // Resource registration
-        Registry registry = getDispatcher().getRegistry();
         for (final Class resourceClass : application.getClasses())
         {
-            log.debug("registering JAX RS resource class: " + resourceClass);
+            // First check if it's a class that is a Seam component
             Set<Component> components = application.getResourceClassComponent(resourceClass);
             if (components != null)
             {
-                log.debug("registering all {0} components of {1}", components.size(), resourceClass);
-                // Register every component
-                for (final Component seamComponent : components)
-                {
-                    // Seam component lookup when call is dispatched to resource
-                    ResourceFactory factory = new ResourceFactory()
-                        {
+                registerSeamComponentResources(components, resourceClass);
+            }
+            else
+            {
+                registerResource(resourceClass);
+            }
+        }
 
-                            public Class<?> getScannableClass()
-                            {
-                                return resourceClass;
-                            }
+    }
 
-                            public void registered(InjectorFactory factory)
-                            {
-                                // Wrap the Resteasy PropertyInjectorImpl in a Seam interceptor (for @Context injection)
-                                seamComponent.addInterceptor(
-                                        new ResteasyContextInjectionInterceptor(
-                                                new PropertyInjectorImpl(getScannableClass(), dispatcher.getProviderFactory())
-                                        )
-                                );
 
-                                // NOTE: Adding an interceptor to Component at this stage means that the interceptor is
-                                // always executed last in the chain. The sorting of interceptors of a Component occurs
-                                // only when the Component metadata is instantiated. This is OK in this case, as the
-                                // JAX RS @Context injection can occur last after all other interceptors executed.
+    protected void registerSeamComponentProviders(Set<Component> components, Class providerClass) {
+        for (Component seamComponent : components)
+        {
+            if (ScopeType.STATELESS.equals(seamComponent.getScope()))
+            {
+                throw new RuntimeException(
+                        "Registration of STATELESS Seam components as RESTEasy providers not implemented!"
+                );
+            }
+            else if (ScopeType.APPLICATION.equals(seamComponent.getScope()))
+            {
+                Object providerInstance = Component.getInstance(seamComponent.getName());
+                boolean isStringConverter = false;
+                for (Class componentIface : seamComponent.getBusinessInterfaces())
+                {
+                    if (StringConverter.class.isAssignableFrom(componentIface))
+                    {
+                        isStringConverter = true;
+                        break;
+                    }
+                }
+                if (isStringConverter)
+                {
+                    log.error("can't register Seam component as RESTEasy StringConverter, see: https://jira.jboss.org/jira/browse/JBSEAM-4020");
+                    //log.debug("registering Seam component as custom RESTEasy string converter provider: " + seamComponent.getName());
+                    //getDispatcher().getProviderFactory().addStringConverter((StringConverter)providerInstance);
+                }
+                else
+                {
+                    getDispatcher().getProviderFactory().registerProviderInstance(providerInstance);
+                }
+            }
+        }
+    }
 
-                            }
+    protected void registerSeamComponentResources(Set<Component> components, Class resourceClass) {
+        for (final Component seamComponent : components)
+        {
+            // Seam component lookup when call is dispatched to resource
+            ResourceFactory factory =
+                    new SeamResteasyResourceFactory(resourceClass, seamComponent, getDispatcher().getProviderFactory());
 
-                            public Object createResource(HttpRequest request, HttpResponse response, InjectorFactory factory)
-                            {
-                                // Push this onto event context so we have it available in ResteasyContextInjectionInterceptor
-                                Contexts.getEventContext().set(ResteasyContextInjectionInterceptor.RE_HTTP_REQUEST_VAR, request);
-                                Contexts.getEventContext().set(ResteasyContextInjectionInterceptor.RE_HTTP_RESPONSE_VAR, response);
-                                return Component.getInstance(seamComponent.getName());
-                            }
+            // Register component on specific path if the component is a ResourceHome or ResourceQuery component configured in components.xml
+            if (seamComponent.getBeanClass().equals(ResourceHome.class) || seamComponent.getBeanClass().equals(ResourceQuery.class))
+            {
 
-                            public void requestFinished(HttpRequest request, HttpResponse response, Object resource)
-                            {
-                            }
-
-                            public void unregistered()
-                            {
-                            }
-                        };
-                    // Register component on specific path if the component is a ResourceHome or ResourceQuery component configured in components.xml
-                    if (seamComponent.getBeanClass().equals(ResourceHome.class) || seamComponent.getBeanClass().equals(ResourceQuery.class))
-                    {
-                       AbstractResource instance = (AbstractResource) seamComponent.newInstance();
-                       String path = instance.getPath();
-                       if (instance.getPath() != null)
-                       {
-                          log.debug("registering resource {0} on path {1}", seamComponent.getName(), path);
-                          registry.addResourceFactory(factory, path);
-                       } else {
-                          log.warn("Unable to  register {0} resource on null path.", seamComponent.getName());
-                       }
-                    }
-                    else
-                    {
-                       log.debug("registering resource {0}", seamComponent.getName());
-                       registry.addResourceFactory(factory);
-                    }
+                // We can always instantiate this safely here because it can't have dependencies!
+                AbstractResource instance = (AbstractResource) seamComponent.newInstance();
+                String path = instance.getPath();
+                if (instance.getPath() != null)
+                {
+                    log.debug("registering resource {0} on path {1} with Seam component lifecycle", seamComponent.getName(), path);
+                    getDispatcher().getRegistry().addResourceFactory(factory, path);
                 }
+                else
+                {
+                    log.warn("Unable to register {0} resource on null path, check components.xml", seamComponent.getName());
+                }
             }
             else
             {
-               // ResourceHome and ResourceQuery won't be registered if not declared as a component
-               if (ResourceHome.class.equals(resourceClass) || ResourceQuery.class.equals(resourceClass))
-                  continue;
-               
-                // JAX-RS default lifecycle
-                log.info("registering resource {0} with default JAX-RS lifecycle", resourceClass);
-                registry.addResourceFactory(new POJOResourceFactory(resourceClass));
+                log.debug("registering resource {0} with Seam component lifecycle", seamComponent.getName());
+                getDispatcher().getRegistry().addResourceFactory(factory);
             }
         }
 
     }
+
+    protected void registerProvider(Class providerClass) {
+        // Just plain RESTEasy, no Seam component lookup or lifecycle
+        if (StringConverter.class.isAssignableFrom(providerClass))
+        {
+            log.debug("registering as custom RESTEasy string converter provider class: " + providerClass);
+            getDispatcher().getProviderFactory().addStringConverter(providerClass);
+        }
+        else
+        {
+            getDispatcher().getProviderFactory().registerProvider(providerClass);
+        }
+    }
+
+    protected void registerResource(Class resourceClass) {
+        // ResourceHome and ResourceQuery have an empty @Path("") and are supposed to be subclassed before use
+        // (or through components.xml) - they should be ignored if they are not proper Seam components
+        if (ResourceHome.class.equals(resourceClass) || ResourceQuery.class.equals(resourceClass))
+            return;
+
+        // Plain EJBs (not a Seam component) can be registered in RESTEasy by JNDI name
+        if (resourceClass.isAnnotationPresent(EJB.STATELESS) || resourceClass.isAnnotationPresent(EJB.STATEFUL))
+        {
+            String jndiName = getJndiName(resourceClass);
+            log.debug("registering plain EJB resource as RESTEasy JNDI resource name: " + jndiName);
+            getDispatcher().getRegistry().addJndiResource(jndiName);
+
+        }
+        else
+        {
+            // JAX-RS default lifecycle
+            log.debug("registering resource {0} with default JAX-RS lifecycle", resourceClass.getName());
+            getDispatcher().getRegistry().addResourceFactory(new POJOResourceFactory(resourceClass));
+        }
+    }
+
+    protected String getJndiName(Class<?> beanClass)
+    {
+        if (beanClass.isAnnotationPresent(JndiName.class))
+        {
+            return beanClass.getAnnotation(JndiName.class).value();
+        }
+        else
+        {
+            String jndiPattern = Init.instance().getJndiPattern();
+            if (jndiPattern == null)
+            {
+                throw new IllegalArgumentException(
+                        "You must specify org.jboss.seam.core.init.jndiPattern or use @JndiName: " + beanClass.getName()
+                );
+            }
+            return jndiPattern.replace("#{ejbName}", Seam.getEjbName(beanClass));
+        }
+    }
+
 }

Modified: branches/community/Seam_2_1/src/resteasy/org/jboss/seam/resteasy/ResteasyResourceAdapter.java
===================================================================
--- branches/community/Seam_2_1/src/resteasy/org/jboss/seam/resteasy/ResteasyResourceAdapter.java	2009-04-24 13:13:27 UTC (rev 10627)
+++ branches/community/Seam_2_1/src/resteasy/org/jboss/seam/resteasy/ResteasyResourceAdapter.java	2009-04-24 13:29:32 UTC (rev 10628)
@@ -5,11 +5,13 @@
 import org.jboss.seam.annotations.Logger;
 import org.jboss.seam.annotations.Name;
 import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.Create;
 import org.jboss.seam.annotations.intercept.BypassInterceptors;
 import org.jboss.seam.log.Log;
 import org.jboss.seam.servlet.ContextualHttpServletRequest;
 import org.jboss.seam.web.AbstractResource;
 import org.jboss.resteasy.core.SynchronousDispatcher;
+import org.jboss.resteasy.core.ThreadLocalResteasyProviderFactory;
 import org.jboss.resteasy.plugins.server.servlet.HttpServletInputMessage;
 import org.jboss.resteasy.plugins.server.servlet.HttpServletResponseWrapper;
 import org.jboss.resteasy.plugins.server.servlet.ServletSecurityContext;
@@ -19,7 +21,6 @@
 import org.jboss.resteasy.specimpl.UriInfoImpl;
 import org.jboss.resteasy.spi.HttpRequest;
 import org.jboss.resteasy.spi.HttpResponse;
-import org.jboss.resteasy.spi.ResteasyProviderFactory;
 import org.jboss.resteasy.util.PathHelper;
 
 import javax.servlet.ServletException;
@@ -28,6 +29,7 @@
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.PathSegment;
 import javax.ws.rs.core.SecurityContext;
+import javax.ws.rs.core.UriBuilder;
 import java.io.IOException;
 import java.net.MalformedURLException;
 import java.net.URI;
@@ -49,10 +51,21 @@
     @Logger
     Log log;
 
+    protected ResteasyDispatcher dispatcher;
+    protected Application application;
+
+    @Create
+    public void init()
+    {
+        // No injection, so lookup on first request
+        dispatcher = (ResteasyDispatcher) Component.getInstance(ResteasyDispatcher.class);
+        application = (Application) Component.getInstance(Application.class);
+    }
+
     @Override
     public String getResourcePath()
     {
-        Application appConfig = (Application)Component.getInstance(Application.class);
+        Application appConfig = (Application) Component.getInstance(Application.class);
         return appConfig.getResourcePathPrefix();
     }
 
@@ -61,13 +74,18 @@
             throws ServletException, IOException
     {
 
-        // Wrap in RESTEasy contexts
-        try {
+        try
+        {
             log.debug("processing REST request");
-            ResteasyProviderFactory.pushContext(HttpServletRequest.class, request);
-            ResteasyProviderFactory.pushContext(HttpServletResponse.class, response);
-            ResteasyProviderFactory.pushContext(SecurityContext.class, new ServletSecurityContext(request));
 
+            // Wrap in RESTEasy thread-local factory handling
+            ThreadLocalResteasyProviderFactory.push(dispatcher.getDispatcher().getProviderFactory());
+
+            // Wrap in RESTEasy contexts (this also puts stuff in a thread-local)
+            SeamResteasyProviderFactory.pushContext(HttpServletRequest.class, request);
+            SeamResteasyProviderFactory.pushContext(HttpServletResponse.class, response);
+            SeamResteasyProviderFactory.pushContext(SecurityContext.class, new ServletSecurityContext(request));
+
             // Wrap in Seam contexts
             new ContextualHttpServletRequest(request)
             {
@@ -76,51 +94,80 @@
                 {
 
                     HttpHeaders headers = ServletUtil.extractHttpHeaders(request);
-                    URI absolutePath;
-                    try
-                    {
-                        URL absolute = new URL(request.getRequestURL().toString());
+                    UriInfoImpl uriInfo = extractUriInfo(request);
 
-                        UriBuilderImpl builder = new UriBuilderImpl();
-                        builder.scheme(absolute.getProtocol());
-                        builder.host(absolute.getHost());
-                        builder.port(absolute.getPort());
-                        builder.path(absolute.getPath());
-                        builder.replaceQuery(absolute.getQuery());
-                        absolutePath = builder.build();
-                    }
-                    catch (MalformedURLException e)
-                    {
-                        throw new RuntimeException(e);
-                    }
+                    HttpResponse theResponse = new HttpServletResponseWrapper(
+                            response,
+                            dispatcher.getDispatcher().getProviderFactory()
+                    );
 
-                    String path = PathHelper.getEncodedPathInfo(absolutePath.getRawPath(), request.getContextPath());
+                    HttpRequest in = new HttpServletInputMessage(
+                            request,
+                            theResponse,
+                            headers,
+                            uriInfo,
+                            request.getMethod().toUpperCase(),
+                            (SynchronousDispatcher) dispatcher.getDispatcher()
+                    );
 
-                    Application appConfig = (Application)Component.getInstance(Application.class);
-                    if (appConfig.isStripSeamResourcePath()) {
-                        log.trace("removing SeamResourceServlet url-pattern and dispatcher prefix from request path");
-                        path = path.substring(path.indexOf(getResourcePath())+getResourcePath().length());
-                    }
-
-                    log.debug("final request path: " + path);
-                    List<PathSegment> pathSegments = PathSegmentImpl.parseSegments(path);
-                    UriInfoImpl uriInfo = new UriInfoImpl(absolutePath, path, request.getQueryString(), pathSegments);
-
-                    HttpRequest in;
-                    ResteasyDispatcher dispatcher =
-                            (ResteasyDispatcher) Component.getInstance(ResteasyDispatcher.class);
-                    HttpResponse theResponse =
-                            new HttpServletResponseWrapper(response, dispatcher.getDispatcher().getProviderFactory());
-                    in = new HttpServletInputMessage( 
-                          request, theResponse, headers, uriInfo, request.getMethod().toUpperCase(), (SynchronousDispatcher)dispatcher.getDispatcher());
-                    
                     dispatcher.getDispatcher().invoke(in, theResponse);
                 }
             }.run();
 
-        } finally {
-            ResteasyProviderFactory.clearContextData();
+        }
+        finally
+        {
+            // Clean up the thread-locals
+            SeamResteasyProviderFactory.clearContextData();
+            ThreadLocalResteasyProviderFactory.pop();
             log.debug("completed processing of REST request");
         }
     }
+
+    // Replaces the static ServletUtil.extractUriInfo(), removes the Seam-related sub-path
+    protected UriInfoImpl extractUriInfo(HttpServletRequest request)
+    {
+        String contextPath = request.getContextPath();
+        URI absolutePath;
+        try
+        {
+            URL absolute = new URL(request.getRequestURL().toString());
+
+            UriBuilderImpl builder = new UriBuilderImpl();
+            builder.scheme(absolute.getProtocol());
+            builder.host(absolute.getHost());
+            builder.port(absolute.getPort());
+            builder.path(absolute.getPath());
+            builder.replaceQuery(absolute.getQuery());
+            absolutePath = builder.build();
+        }
+        catch (MalformedURLException e)
+        {
+            throw new RuntimeException(e);
+        }
+
+        String path = PathHelper.getEncodedPathInfo(absolutePath.getRawPath(), contextPath);
+
+        if (application.isStripSeamResourcePath())
+        {
+            log.debug("removing SeamResourceServlet url-pattern and dispatcher prefix from request path");
+            path = path.substring(path.indexOf(getResourcePath()) + getResourcePath().length());
+        }
+
+        List<PathSegment> pathSegments = PathSegmentImpl.parseSegments(path);
+        URI baseURI = absolutePath;
+        if (!path.trim().equals(""))
+        {
+            String tmpContextPath = contextPath;
+            if (!tmpContextPath.endsWith("/")) tmpContextPath += "/";
+            baseURI = UriBuilder.fromUri(absolutePath).replacePath(tmpContextPath).build();
+        }
+
+        log.debug("UriInfo, absolute URI       : " + absolutePath);
+        log.debug("UriInfo, base URI           : " + baseURI);
+        log.debug("UriInfo, relative path/@Path: " + path);
+        log.debug("UriInfo, query string       : " + request.getQueryString());
+
+        return new UriInfoImpl(absolutePath, baseURI, path, request.getQueryString(), pathSegments);
+    }
 }

Added: branches/community/Seam_2_1/src/resteasy/org/jboss/seam/resteasy/SeamResteasyProviderFactory.java
===================================================================
--- branches/community/Seam_2_1/src/resteasy/org/jboss/seam/resteasy/SeamResteasyProviderFactory.java	                        (rev 0)
+++ branches/community/Seam_2_1/src/resteasy/org/jboss/seam/resteasy/SeamResteasyProviderFactory.java	2009-04-24 13:29:32 UTC (rev 10628)
@@ -0,0 +1,25 @@
+package org.jboss.seam.resteasy;
+
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
+
+/**
+ * TODO: We need to significantly extend and change that class so we can lookup provider instances
+ * through Seam at runtime. The original class has only been designed for registration of "singleton"
+ * providers during startup.
+ *
+ * @author Christian Bauer
+ */
+public class SeamResteasyProviderFactory extends ResteasyProviderFactory
+{
+
+    public static void setInstance(ResteasyProviderFactory factory)
+    {
+        ResteasyProviderFactory.setInstance(factory);
+    }
+
+    public static ResteasyProviderFactory getInstance()
+    {
+        return ResteasyProviderFactory.getInstance();
+    }
+
+}

Added: branches/community/Seam_2_1/src/resteasy/org/jboss/seam/resteasy/SeamResteasyResourceFactory.java
===================================================================
--- branches/community/Seam_2_1/src/resteasy/org/jboss/seam/resteasy/SeamResteasyResourceFactory.java	                        (rev 0)
+++ branches/community/Seam_2_1/src/resteasy/org/jboss/seam/resteasy/SeamResteasyResourceFactory.java	2009-04-24 13:29:32 UTC (rev 10628)
@@ -0,0 +1,73 @@
+package org.jboss.seam.resteasy;
+
+import org.jboss.resteasy.spi.ResourceFactory;
+import org.jboss.resteasy.spi.InjectorFactory;
+import org.jboss.resteasy.spi.HttpRequest;
+import org.jboss.resteasy.spi.HttpResponse;
+import org.jboss.resteasy.spi.ResteasyProviderFactory;
+import org.jboss.resteasy.core.PropertyInjectorImpl;
+
+import org.jboss.seam.contexts.Contexts;
+import org.jboss.seam.Component;
+import org.jboss.seam.log.Log;
+import org.jboss.seam.log.Logging;
+
+/**
+ * Looks up Seam component in Seam contexts when a JAX RS resource is requested.
+ *
+ * @author Christian Bauer
+ */
+public class SeamResteasyResourceFactory implements ResourceFactory
+{
+    Log log = Logging.getLog(SeamResteasyResourceFactory.class);
+
+    private final Class<?> resourceClass;
+    private final Component seamComponent;
+    private final ResteasyProviderFactory providerFactory;
+
+    public SeamResteasyResourceFactory(Class<?> resourceClass, Component seamComponent, ResteasyProviderFactory providerFactory)
+    {
+        this.resourceClass = resourceClass;
+        this.seamComponent = seamComponent;
+        this.providerFactory = providerFactory;
+    }
+
+    public Class<?> getScannableClass()
+    {
+        return resourceClass;
+    }
+
+    public void registered(InjectorFactory factory)
+    {
+        // Wrap the Resteasy PropertyInjectorImpl in a Seam interceptor (for @Context injection)
+        seamComponent.addInterceptor(
+                new ResteasyContextInjectionInterceptor(
+                        new PropertyInjectorImpl(getScannableClass(), providerFactory)
+                )
+        );
+
+        // NOTE: Adding an interceptor to Component at this stage means that the interceptor is
+        // always executed last in the chain. The sorting of interceptors of a Component occurs
+        // only when the Component metadata is instantiated. This is OK in this case, as the
+        // JAX RS @Context injection can occur last after all other interceptors executed.
+
+    }
+
+    public Object createResource(HttpRequest request, HttpResponse response, InjectorFactory factory)
+    {
+        // Push this onto event context so we have it available in ResteasyContextInjectionInterceptor
+        Contexts.getEventContext().set(ResteasyContextInjectionInterceptor.RE_HTTP_REQUEST_VAR, request);
+        Contexts.getEventContext().set(ResteasyContextInjectionInterceptor.RE_HTTP_RESPONSE_VAR, response);
+        log.debug("creating RESTEasy resource instance by looking up Seam component: " + seamComponent.getName());
+        return Component.getInstance(seamComponent.getName());
+    }
+
+    public void requestFinished(HttpRequest request, HttpResponse response, Object resource)
+    {
+    }
+
+    public void unregistered()
+    {
+    }
+
+}




More information about the seam-commits mailing list