users@jersey.java.net

[Jersey] Re: UriInfo injection at resource initialization time

From: Martynas Jusevicius <martynas.jusevicius_at_gmail.com>
Date: Wed, 9 Mar 2011 14:29:52 +0100

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
>>>
>>
>