users@jersey.java.net

[Jersey] Re: UriInfo injection at resource initialization time

From: Martin Matula <martin.matula_at_oracle.com>
Date: Wed, 09 Mar 2011 14:46:55 +0100

Hi,
I think all you need to do is remove the singleton annotation and let
Ontology.get() handle the performance optimizations (i.e. if a new
instance of an individual is returned, or instances are cached).
Martin

On 9.3.2011 14:29, Martynas Jusevicius wrote:
> Markus& Martin,
>
> thanks for your help, but although I thought I was getting somewhere,
> now I can see that I'm still not...
>
> First of all, let me clarify -- in RDF environment there are no
> "Directory" or "File" Resources. *All* Resources (basically anything
> that has a URI) are/can have Individual and therefore must follow the
> same interface, and this is easiest achieved by introducing an
> abstract superclass which deals with the Individual interface.
> Some of these Resources are singletons in the REST sense, some of them not.
>
> Let me give a simplified example - in this case Individual is not
> implemented (it contains a lot of methods) but simply contained by the
> superclass:
>
> abstract public class Resource // implements Individual
> {
> private Individual individual = null;
>
> public Resource(UriInfo uriInfo)
> {
> System.out.println(uriInfo.getAbsolutePath().toString());
> // possible Individual initialization
> //setIndividual(Ontology.get(uriInfo.getAbsolutePath());
> }
>
> public Individual getIndividual()
> {
> return individual;
> }
>
> private void setIndividual(Individual individual)
> {
> this.individual = individual;
> }
> }
>
> Individual has to be instantiated using Resource's absolute URI at
> some point, ideally in the superclass constructor.
>
> Then, there is root TestResource at @Path("") and subresource
> TestSubResource at @Path("sub").
> Both of them should be singletons, since their path never changes and
> only one instance is needed.
>
> @Path(TestRootResource.PATH)
> @Singleton
> public class TestRootResource extends Resource
> {
> public static final String PATH = "";
>
> public TestRootResource(@Context UriInfo uriInfo)
> {
> super(uriInfo);
> }
>
> @GET
> @Produces("text/plain")
> public String doGet()
> {
> return "TestResource.doGet()";
> }
>
> @Path(TestSubResource.PATH)
> public TestSubResource getSubResource(@Context UriInfo uriInfo)
> {
> return new TestSubResource(uriInfo);
> }
> }
>
> @Singleton
> public class TestSubResource extends Resource
> {
> public static final String PATH = "sub";
>
> TestSubResource(UriInfo uriInfo)
> {
> super(uriInfo);
> }
>
> @GET
> @Produces("text/plain")
> public String doGet()
> {
> return "TestSubResource.doGet()";
> }
> }
>
> This is probably closest to what I'd like to achieve, and doesn't seem
> that different from Martin's example - but of course it doesn't work
> since I get the aforementioned IllegalStateException.
>
> UriBuilder usage like fromResource(TestSubResource.class).build()
> gives me nothing new here, since I just as well can use
> TestSubResource.PATH.
>
> The example seems JAX-RS compliant to me, but can you suggest how this
> can be refactored to work with Jersey?
> Most importantly, how and where can the absolute URI be extracted and
> used to initialize Individual?
>
> Thanks again,
>
> Martynas
> semantic-web.dk
>
> On Wed, Mar 9, 2011 at 12:16 PM, Markus Karg<karg_at_quipsy.de> wrote:
>>> UriInfo injection in constructor is used in an example in "Overview of
>>> JAX-RS 1.0 Features"
>>> http://wikis.sun.com/display/Jersey/Overview+of+JAX-RS+1.0+Features#OverviewofJAX-RS1.0Features->ConditionalGETsandReturning304%28NotModified%29Responses
>>> It also says "Notice that in this example the constructor of a
>>> resource class can be used perform actions that may otherwise have to
>>> be duplicated to invoked for each resource method".
>>> That makes perfect sense to me - but are you saying this wouldn't
>>> work in Jersey?
>>> I see a good specification/implementation letting me be flexible with
>>> my code, instead of making me bend my code to fit it.
>> Apparently it is not working in Jersey as you experienced and was explained already. See that this functionality is not mandatory by the JAX-RS 1.1 specification, so relying on that possibility would bind your code to a particular implementation, which is not what you want. If you need just a static URI, check Martin's solution. If you need a dynamic URI and do insist on getting it injected into the resource, you should file a RFE for the upcoming JAX-RS 2.0 standard, because in that case, JAX-RS 2.0 must forbid singletons (with all the negative side effects then).
>>
>>> I had implemented the same functionality with a simple HttpServlet, so
>>> I'm still not sure if the transition to JAX-RS is worth the trouble.
>> JAX-RS is not in contrast to servlets. Servlets are for doing anything you like with http. JAX-RS is for doing explicitly REST. If you need REST, you're possibly better off with JAX-RS. If you don't, then I do not see why you actually want to use JAX-RS.
>>
>>>> What is the problem with injecting it at invocation? You could have a facade resource just creating instances of your resource, passing in @Context for example.
>>> That seems like a workaround, but I could give it a shot. Could you
>>> please give an example of what you have in mind?
>> Actually it is not a workaround but it is exactly how JAX-RS wants it to be like, since a directory is not a plain file and vice versa. It is a difference between a resource (= a directory containing files) and its content (= a plaint file). So it makes rather sense to have *two* resources:
>>
>> http://localhost/files<-- The directory
>>
>> http://localhost/files/123<-- The file (can be sub directory)
>>
>> "Directory" Resource contains Individuals:
>>
>> @GET @Path("individuals/{id}")
>> public Individual getIndividual(@Context UriInfo uriInfo) {
>> return new Individual(uriInfo);
>> }
>>
>> "File" ("Subdirectory") Resource implements Individual:
>>
>> @GET
>> public ChildOfIndividual() {
>> return new ChildOfIndividual();
>> }
>>
>> I do neither see that this looks like a workaround nor being in any case uncommon in the sense of OOP. See that one is plural (@Path("individuals")), while the other is singular (implements Individual), so it obviously are two different things.
>>
>>>> It is possible to make your resource implement your Individual interface, but you have to take care about the process>>sequence at runtime: You have no guarantee *when* the constructor of the resource is invoked, as the JAX-RS spec allows>>providers to use different custom life cycles. So it is valid in JAX-RS that your constructor receives null for @Context>>since it might be bound to e. g. @Path("/path") while it got invoked due to a request upon /path/123. If the resource got>>created per application (as per JAX-RS spec's default), obviously it wouldn't "see" the trailing "123" (as it was created>>already at that point in time). But it will "see" trailing "123" for the field injection, since that happens not at resource>>creation but at actual invocation. That explains the behaviour you have seen. There is no pure JAX-RS way to overcome that.
>>> Well then JAX-RS doesn't make much sense to me in this case... I think
>>> if Resource has a static URI like "/path" (which probably means it is
>>> a singleton of a root class) then it should be able to access it at
>>> any time (its own URI, not the request URI).
>> You can access the static path, just check Martin's mail to get the code line.
>>
>> Regards
>> Markus
>>
>>> -----Original Message-----
>>> From: Martynas Jusevicius [mailto:martynas.jusevicius_at_gmail.com]
>>> Sent: Mittwoch, 9. März 2011 10:16
>>> To: users_at_jersey.java.net
>>> Cc: Markus Karg
>>> Subject: Re: [Jersey] Re: UriInfo injection at resource initialization time
>>>
>>> Hey Markus,
>>>
>>> what if I want getIndividual() as a simple getter, not a resource method?
>>>
>>> It's not that Resource returns or contains Individuals, in essence
>>> Resource *is* (or could be) an Individual as well -- both REST and RDF
>>> object, since they share the same URI.
>>> I was thinking about making Resource implement Individual interface
>>> using the individual field, but that means it should be instantiated
>>> at (or right after) construction time - and I couldn't find a JAX-RS
>>> solution to that yet. Does that make sense?
>>>
>>> I can look up Individual using Resource URI - but has to be not null
>>> at that time.
>>>
>>> Martynas
>>>
>>> On Wed, Mar 9, 2011 at 8:21 AM, Markus Karg<karg_at_quipsy.de> wrote:
>>>> Try this one:
>>>>
>>>> @GET
>>>> @Path("individuals")
>>>> public Individual getIndividual(@Context UriInfo uriInfo) {
>>>> return new Individual(uriInfo);
>>>> }
>>>>
>>>> Regards
>>>> Markus
>>>>
>>>> -----Original Message-----
>>>> From: Martynas Jusevicius [mailto:martynas.jusevicius_at_gmail.com]
>>>> Sent: Mittwoch, 9. März 2011 02:40
>>>> To: users_at_jersey.java.net
>>>> Subject: [Jersey] UriInfo injection at resource initialization time
>>>>
>>>> Hey list,
>>>>
>>>> I'm new here and would like to start with a question :)
>>>>
>>>> I have Resource classes implementing JAX-RS, however I also want to
>>>> combine it with RDF using Jena API.
>>>> Each Resource should have a reference to Individual (RDF resource) -
>>>> and they share the same absolute URI.
>>>> This is a root Resource class:
>>>>
>>>> @Path("")
>>>> @Singleton
>>>> public class TestResource
>>>> {
>>>> private Individual individual = null;
>>>>
>>>> ...
>>>> }
>>>>
>>>> This means Individual has to be initiated at some point, and at that
>>>> point the Resource needs to know its URI to be able to look up
>>>> Individual in the RDF model.
>>>>
>>>> What I've tried (the code goes inside the TestResource defined above):
>>>>
>>>> 1. Injected class field
>>>>
>>>> @Context UriInfo uriInfo;
>>>>
>>>> public TestResource()
>>>> {
>>>> System.out.println("TestResource.uriInfo: " + uriInfo);
>>>> }
>>>>
>>>> Doesn't work - returns null
>>>>
>>>> 2. Injected constructor parameter
>>>>
>>>> public TestResource(@Context UriInfo uriInfo)
>>>> {
>>>> System.out.println("TestResource(uriInfo): " + uriInfo);
>>>> }
>>>>
>>>> Doesn't work - I get
>>>> java.lang.IllegalStateException
>>>> com.sun.jersey.server.impl.ThreadLocalHttpContext.getUriInfo
>>>> as described in
>>>> http://jersey.576304.n2.nabble.com/injecting-UriInfo-in-constructor-td4510740.html
>>>> Same message suggests "access the reference when a request is in
>>>> scope" -- but this is not good enough.
>>>>
>>>> However constructor param is used in JAX-RS tutorial:
>>>> http://wikis.sun.com/display/Jersey/Overview+of+JAX-RS+1.0+Features#OverviewofJAX-RS1.0Features-ConditionalGETsandReturning304%28NotModified%29Responses
>>>>
>>>> 3. @PostConstruct method
>>>>
>>>> @PostConstruct
>>>> public void init()
>>>> {
>>>> System.out.println("@PostConstruct UriInfo: " + uriInfo);
>>>> }
>>>>
>>>> Doesn't work - never gets executed
>>>>
>>>> Am I missing something? It seems as such a common use case - why isn't
>>>> there a simple solution?
>>>> I see no reason why root Resources with static @Path could not have
>>>> access to their UriInfo at any time - their URI is known right from
>>>> the start?
>>>> And why does @PostConstruct not work? I'm running Tomcat 6.0.26.
>>>>
>>>> Thanks,
>>>>
>>>> Martynas
>>>> semantic-web.dk
>>>>