Author: christian.bauer(a)jboss.com
Date: 2009-05-08 03:46:31 -0400 (Fri, 08 May 2009)
New Revision: 10839
Modified:
branches/community/Seam_2_1/doc/Seam_Reference_Guide/en-US/Webservices.xml
branches/community/Seam_2_1/src/resteasy/org/jboss/seam/resteasy/Application.java
branches/community/Seam_2_1/src/resteasy/org/jboss/seam/resteasy/ResteasyResourceAdapter.java
branches/community/Seam_2_1/src/resteasy/org/jboss/seam/resteasy/resteasy-2.1.xsd
Log:
JBSEAM-3264, prevent anemic sessions on REST requests
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-05-07
18:04:50 UTC (rev 10838)
+++ branches/community/Seam_2_1/doc/Seam_Reference_Guide/en-US/Webservices.xml 2009-05-08
07:46:31 UTC (rev 10839)
@@ -232,7 +232,7 @@
<para>
First, get the RESTEasy libraries and the
<literal>jaxrs-api.jar</literal>, deploy them with the
other libraries of your application. Also deploy the integration library,
- <literal>jboss-seam-resteasy.jar</literal>
+ <literal>jboss-seam-resteasy.jar</literal>.
</para>
<para>
@@ -350,6 +350,12 @@
</resteasy:application>]]></programlisting>
<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.
+ </para>
+
+ <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>
@@ -362,12 +368,6 @@
</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.
- </para>
-
- <para>
Finally, you can configure media type and language URI extensions:
</para>
@@ -426,27 +426,54 @@
<para>
An instance of <literal>customerResource</literal> is now handled
by Seam when a request hits the
server. This is a Seam JavaBean component that is
<literal>EVENT</literal>-scoped, hence no different
- than the default JAX-RS lifecycle. However, you get full Seam injection
support and all other Seam
- components and contexts are available to you. Currently also supported are
<literal>SESSION</literal>,
- <literal>APPLICATION</literal>, and
<literal>STATELESS</literal> resource components. Remember that any
- HTTP request has to transmit a valid session identifier (cookie, URI path
parameter) for correct handling
- of the server-side session context.
+ than the default JAX-RS lifecycle. You get full Seam injection support and
all other Seam
+ components and contexts are available to you. Currently also supported are
<literal>APPLICATION</literal>
+ and <literal>STATELESS</literal> resource Seam components. These
scopes allow you to create an effectively
+ stateless Seam middle-tier HTTP request-processing application.
</para>
<para>
- Conversation-scoped resource components and mapping of conversations is
currently not supported but will
- be available soon.
+ You can use <literal>SESSION</literal>-scoped Seam components. By
default, the session will however be shortened
+ to a single request. In other words, when an HTTP request is being processed
by the RESTEasy integration code,
+ an HTTP session will be created so that Seam components can utilize that
context. When the request has
+ been processed, Seam will look at the session and decide if the session was
created only to serve that
+ single request (no session identifier has been provided with the request, or
no session existed for the request).
+ If the session has been created only to serve this request, the session will
be destroyed after the request!
+ Assuming that your Seam application only uses event, application, or stateless
components, this procedure
+ prevents exhaustion of available HTTP sessions on the server. The RESTEasy
integration with Seam assumes
+ by default that sessions are not used, hence anemic sessions would add up as
every REST request would start
+ a session that will only be removed when timed out.
</para>
<para>
- Provider classes can also be Seam components, they must be
<literal>APPLICATION</literal>-scoped
- or <literal>STATELESS</literal>.
+ If your RESTful Seam application has to preserve session state across REST
HTTP requests, disable this
+ behavior in your configuration file:
</para>
+ <programlisting role="XML"><![CDATA[<resteasy:application
destroy-session-after-request="false"/>]]></programlisting>
+
<para>
- Resources and providers can be EJBs or JavaBeans, like any other Seam
component.
+ Every RESTful HTTP request will now create a new session that will only be
removed by timeout or explicit
+ invalidation in your code through
<literal>Session.instance().invalidate()</literal>.
+ It is your responsibility to pass a valid session identifier along with your
HTTP requests, if you want
+ to utilize the session context across requests.
</para>
+ <para>
+ <literal>CONVERSATION</literal>-scoped resource components and
mapping of conversations to temporary HTTP
+ resources and paths is planned but currently not supported.
+ </para>
+
+ <para>
+ EJB Seam components are currently <emphasis>NOT</emphasis>
supported! However, as explained above, you
+ can map plain stateless EJBs (with no Seam lifecycle or injection) as REST
resources.
+ </para>
+
+ <para>
+ Provider classes can also be Seam components, currently only
<literal>APPLICATION</literal>-scoped
+ provider components are supported.
+ </para>
+
</sect2>
<sect2>
@@ -463,8 +490,8 @@
<para>
Exception handling within Seam requires that the Seam filter is executed for
your HTTP request. Ensure that
you do filter <emphasis>all</emphasis> requests in your
<literal>web.xml</literal>, not - as
- some Seam examples might show - a request URI pattern that doesn't cover
your REST requests. The following
- example intercepts <emphasis>all</emphasis> HTTP requests and
enables Seam exception handling:
+ some Seam examples might show - a request URI pattern that doesn't cover
your REST request paths.
+ The following example intercepts <emphasis>all</emphasis> HTTP
requests and enables Seam exception handling:
</para>
<programlisting role="XML"><![CDATA[<filter>
@@ -485,7 +512,7 @@
<programlisting role="XML"><![CDATA[<exception
class="java.lang.UnsupportedOperationException">
<http-error error-code="501">
- <message>The request operation is not supported</message>
+ <message>The requested operation is not supported</message>
</http-error>
</exception>]]></programlisting>
@@ -495,7 +522,7 @@
<programlisting role="XML"><![CDATA[<exception
class="my.CustomException" log="false">
<http-error error-code="503">
- <message>The service is currently not available:
#{org.jboss.seam.handledException.message}</message>
+ <message>Service not available:
#{org.jboss.seam.handledException.message}</message>
</http-error>
</exception>]]></programlisting>
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-05-07
18:04:50 UTC (rev 10838)
+++
branches/community/Seam_2_1/src/resteasy/org/jboss/seam/resteasy/Application.java 2009-05-08
07:46:31 UTC (rev 10839)
@@ -35,6 +35,7 @@
private boolean scanProviders = true;
private boolean scanResources = true;
private boolean useBuiltinProviders = true;
+ private boolean destroySessionAfterRequest = true;
private String resourcePathPrefix = "/rest";
private boolean stripSeamResourcePath = true;
@@ -183,6 +184,16 @@
this.useBuiltinProviders = useBuiltinProviders;
}
+ public boolean isDestroySessionAfterRequest()
+ {
+ return destroySessionAfterRequest;
+ }
+
+ public void setDestroySessionAfterRequest(boolean destroySessionAfterRequest)
+ {
+ this.destroySessionAfterRequest = destroySessionAfterRequest;
+ }
+
public String getResourcePathPrefix()
{
return resourcePathPrefix;
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-05-07
18:04:50 UTC (rev 10838)
+++
branches/community/Seam_2_1/src/resteasy/org/jboss/seam/resteasy/ResteasyResourceAdapter.java 2009-05-08
07:46:31 UTC (rev 10839)
@@ -10,6 +10,7 @@
import org.jboss.seam.log.Log;
import org.jboss.seam.servlet.ContextualHttpServletRequest;
import org.jboss.seam.web.AbstractResource;
+import org.jboss.seam.web.Session;
import org.jboss.resteasy.core.SynchronousDispatcher;
import org.jboss.resteasy.core.ThreadLocalResteasyProviderFactory;
import org.jboss.resteasy.plugins.server.servlet.HttpServletInputMessage;
@@ -65,8 +66,7 @@
@Override
public String getResourcePath()
{
- Application appConfig = (Application) Component.getInstance(Application.class);
- return appConfig.getResourcePathPrefix();
+ return application.getResourcePathPrefix();
}
@Override
@@ -111,6 +111,15 @@
);
dispatcher.getDispatcher().invoke(in, theResponse);
+
+ // Prevent anemic sessions clog up the server
+ if (request.getSession().isNew()
+ && application.isDestroySessionAfterRequest()
+ && !Session.instance().isInvalid())
+ {
+ log.debug("Destroying HttpSession after REST
request");
+ Session.instance().invalidate();
+ }
}
}.run();
Modified:
branches/community/Seam_2_1/src/resteasy/org/jboss/seam/resteasy/resteasy-2.1.xsd
===================================================================
---
branches/community/Seam_2_1/src/resteasy/org/jboss/seam/resteasy/resteasy-2.1.xsd 2009-05-07
18:04:50 UTC (rev 10838)
+++
branches/community/Seam_2_1/src/resteasy/org/jboss/seam/resteasy/resteasy-2.1.xsd 2009-05-08
07:46:31 UTC (rev 10839)
@@ -107,6 +107,14 @@
</xs:documentation>
</xs:annotation>
</xs:attribute>
+ <xs:attribute name="destroy-session-after-request"
type="components:boolean">
+ <xs:annotation>
+ <xs:documentation>
+ Destroy the HttpSession after a REST request if it was created for
that request (it is a
+ new session), defaults to 'true'.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
<xs:attribute name="resource-path-prefix"
type="components:string">
<xs:annotation>
<xs:documentation>