Hi Arul,
A session is per-client. If you have multiple clients then each will
have a session and their own set of session attributes.
Thus if you use a singleton as you propose (and it did what you
expected) it would not give the behavior you require for multiple
clients.
Consider this:
1) Client 1 makes a first request to the service. The singleton gets
instantiated. The client 1 session does not exist, it is
created and initiated.
2) Client 2 makes the second request to the service. The singleton is
already instantiated. The client 2 session does
not exist and is not initiated.
A way you can test this scenario in a browser is to delete cookies.
Make a request (client 1), delete the cookies, make another request
(client 2).
So IIUC what you require is a resource class instance created per-
session.
@PerSession
public static class ModelResource {
String currentModel = "defaultModel";
@Produces("text/plain")
@GET
public String get() {
return currentModel;
}
}
An instance of ModelResource will be stored in the session
attributes. Notice that you do not need to utilize the servlet
session itself. Of course if other resources utilize session you
could obtain the ModelResource from the session as well (using the
class name as the key). Care also has to be taken that such classes
are serializable such that they can be used in clustering set ups.
Note that i think there are ways one can do this without utilizing
sessions making use of URIs/resources for users and the default
model, and using HTTP authentication with SSL. But i can understand
why utilizing session is so convenient. I think it would be
interesting to develop an such an example as this could help
developers evaluate better which directions to take.
Paul.
On Aug 25, 2008, at 7:15 PM, Arul Dhesiaseelan wrote:
> Hi Paul,
>
> Thanks for revisiting this post.
>
> As per my understanding, @Singleton enforces that there can be only
> one instance of this resource in the web application context. So, I
> added this annotation to my resource so multiple instances do not
> get created for each client request.
>
> Let me explain my use case briefly so that you can correct me if I
> am doing something fundamentally wrong.
>
> We have defined a set of backend REST APIs which works on our model
> (list/create/delete/update). The web application front end is the
> primary consumer of this REST API. The web application and RESTful
> resources are deployed into Jersey as a WAR.
> In addition to performing CRUD operations, the web application also
> operates on each model (for ex: if the model is secure, it can
> perform login/logout on the model). The users can select the model
> they wish to operate and the model will be set in session for that
> user until they decide to choose a different model to work with. At
> application startup, we need to select a default model and set it
> in session so that the user can use this default model at first
> access. This is where my requirement comes into picture. When I
> start my resources, I need to somehow set the default model in
> session if there is one so that the user is presented with this
> default model.
>
> So, I try to set this in the constructor by injecting a HTTP
> servlet request and fetch the session from the request as shown
> below which seems to be an illegal scenario.
>
> @Singleton
> public static class ModelResource {
> @Context
> HttpServletRequest r;
>
> public ModelResource(@Context HttpServletRequest req) {
> req.getSession().setAttribute("currentmodel", "defaultmodel");
> }
>
> @Produces("text/plain")
> @GET
> public String get() {
> return (String) r.getAttribute("currentmodel");
> }
> }
>
> I appreciate your inputs.
>
> -Arul
>
> Paul Sandoz wrote:
>> Hi Arul,
>>
>> I realized there is something wrong with the way you are doing
>> things in that you assume a singleton instance is associated with
>> a session. This will not be the case for multiple clients, thus
>> your service is likely to fail in that case.
>>
>> So... i have given up resisting providing life-cycle support for
>> HTTP servlet sessions, and have implemented such support with a
>> new annotation:
>>
>> com.sun.jersey.spi.container.servlet.PerSession
>>
>> And you can use it like this:
>>
>> @Path("resource")
>> @PerSession
>> public class Resource {
>> int i = 0;
>>
>> @GET
>> public String get() {
>> i++;
>> return "" + i;
>> }
>> }
>>
>> Instances are stored as attributes on the session using the class
>> name as the key. It requires that you are using a servlet as the
>> HTTP container.
>>
>> Use it wisely with the knowledge that you are breaking an
>> important RESTful constraint [1].
>>
>> Paul.
>>
>> [1] http://www.crummy.com/2008/08/19/2
>>
>> Arul Dhesiaseelan wrote:
>>> Hi Paul,
>>>
>>> I am still having issue with constructor injection in singleton
>>> resources in Jersey 0.9. If I comment
>>> SingletonConstructorResource in the below code, I can run other 3
>>> scenarios just fine which was working fine in 0.8 too. If I
>>> uncomment SingletonConstructorResource, then none of the
>>> scenarios work in 0.9. Am I missing something? I remember you had
>>> fixed this issue earlier.
>>>
>>> I am getting the following exception when trying to invoke any of
>>> the resources:
>>>
>>> com.sun.jersey.api.container.ContainerException: Unable to create
>>> resource
>>> at com.sun.jersey.impl.resource.SingletonProvider.init
>>> (SingletonProvider.java:88)
>>> at
>>> com.sun.jersey.spi.resource.ResourceProviderFactory.createProvider
>>> (ResourceProviderFactory.java:132)
>>> at com.sun.jersey.impl.model.ResourceClass.init
>>> (ResourceClass.java:180)
>>> at
>>> com.sun.jersey.impl.application.WebApplicationImpl.getResourceClass(
>>> WebApplicationImpl.java:267)
>>> at
>>> com.sun.jersey.impl.application.WebApplicationImpl.processRootResour
>>> ces(WebApplicationImpl.java:796)
>>> at com.sun.jersey.impl.application.WebApplicationImpl.initiate
>>> (WebApplicationImpl.java:668)
>>> at com.sun.jersey.impl.application.WebApplicationImpl.initiate
>>> (WebApplicationImpl.java:485)
>>> at
>>> com.sun.jersey.spi.container.servlet.ServletContainer.initiate
>>> (ServletContainer.java:559)
>>> at com.sun.jersey.spi.container.servlet.ServletContainer.load
>>> (ServletContainer.java:483)
>>> at com.sun.jersey.spi.container.servlet.ServletContainer.init
>>> (ServletContainer.java:165)
>>> at org.mortbay.jetty.servlet.ServletHolder.initServlet
>>> (ServletHolder.java:433)
>>> at org.mortbay.jetty.servlet.ServletHolder.getServlet
>>> (ServletHolder.java:342)
>>> at org.mortbay.jetty.servlet.ServletHolder.handle
>>> (ServletHolder.java:463)
>>> at org.mortbay.jetty.servlet.ServletHandler.handle
>>> (ServletHandler.java:362)
>>> at org.mortbay.jetty.servlet.SessionHandler.handle
>>> (SessionHandler.java:181)
>>> at org.mortbay.jetty.handler.ContextHandler.handle
>>> (ContextHandler.java:729)
>>> at org.mortbay.jetty.handler.HandlerWrapper.handle
>>> (HandlerWrapper.java:152)
>>> at org.mortbay.jetty.Server.handle(Server.java:324)
>>> at org.mortbay.jetty.HttpConnection.handleRequest
>>> (HttpConnection.java:505)
>>> at org.mortbay.jetty.HttpConnection
>>> $RequestHandler.headerComplete(HttpConnection.java:829)
>>> at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:513)
>>> at org.mortbay.jetty.HttpParser.parseAvailable
>>> (HttpParser.java:211)
>>> at org.mortbay.jetty.HttpConnection.handle
>>> (HttpConnection.java:380)
>>> at org.mortbay.jetty.bio.SocketConnector$Connection.run
>>> (SocketConnector.java:228)
>>> at org.mortbay.thread.QueuedThreadPool$PoolThread.run
>>> (QueuedThreadPool.java:488)
>>> Caused by: java.lang.reflect.InvocationTargetException
>>> at sun.reflect.NativeConstructorAccessorImpl.newInstance0
>>> (Native Method)
>>> at sun.reflect.NativeConstructorAccessorImpl.newInstance
>>> (NativeConstructorAccessorImpl.java:39)
>>> at sun.reflect.DelegatingConstructorAccessorImpl.newInstance
>>> (DelegatingConstructorAccessorImpl.java:27)
>>> at java.lang.reflect.Constructor.newInstance(Constructor.java:
>>> 494)
>>> at com.sun.jersey.impl.application.WebApplicationImpl
>>> $DefaultResourceComponentProvider.getInstance
>>> (WebApplicationImpl.java:465)
>>> at com.sun.jersey.impl.resource.SingletonProvider.init
>>> (SingletonProvider.java:84)
>>> ... 24 more
>>> Caused by: java.lang.NullPointerException
>>> at jersey.Inject$SingletonConstructorResource.(Inject.java:42)
>>> ... 30 more
>>>
>>>
>>>
>>>
>>> public class Inject {
>>> @Path("singleton")
>>> @Singleton
>>> public static class SingletonResource {
>>> @Context
>>> HttpServletRequest r;
>>>
>>> @Produces("text/plain")
>>> @GET
>>> public String get() {
>>> return r.getPathInfo();
>>> }
>>> }
>>>
>>> // @Path("singleton/constructor")
>>> // @Singleton
>>> // public static class SingletonConstructorResource {
>>> // @Context HttpServletRequest r;
>>> //
>>> // public SingletonConstructorResource(@Context
>>> HttpServletRequest r) {
>>> // this.r = r;
>>> // r.getSession().setAttribute("test", "true");
>>> // }
>>> //
>>> // @Produces("text/plain")
>>> // @GET public String get() {
>>> // return (String)r.getAttribute("test");
>>> // }
>>> // }
>>>
>>> @Path("perrequest")
>>> public static class PerRequestResource {
>>> @Context
>>> HttpServletRequest r;
>>>
>>> @Produces("text/plain")
>>> @GET
>>> public String get() {
>>> return r.getPathInfo();
>>> }
>>> }
>>>
>>> @Path("perrequest/constructor")
>>> public static class PerRequestConstructorResource {
>>> String path;
>>>
>>> public PerRequestConstructorResource(@Context
>>> HttpServletRequest r) {
>>> path = r.getPathInfo();
>>> }
>>>
>>> @Produces("text/plain")
>>> @GET
>>> public String get() {
>>> return path;
>>> }
>>> }
>>>
>>> public static void main(String[] args) throws Exception {
>>> ServletHolder sh = new ServletHolder(ServletContainer.class);
>>> sh.setInitParameter("com.sun.jersey.config.property.packages",
>>> "jersey");
>>> Server server = new Server(9999);
>>> org.mortbay.jetty.servlet.Context context = new
>>> org.mortbay.jetty.servlet.Context(server, "/",
>>> org.mortbay.jetty.servlet.Context.SESSIONS);
>>> context.addServlet(sh, "/*");
>>> server.start();
>>> }
>>>
>>> }
>>>
>>> Thanks!
>>> Arul
>>>
>>>
>>> --------------------------------------------------------------------
>>> -
>>> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
>>> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>>>
>>
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>