users@jersey.java.net

[Jersey] Re: UriInfo injection at resource initialization time

From: Martynas Jusevicius <martynas.jusevicius_at_gmail.com>
Date: Thu, 10 Mar 2011 16:23:42 +0100

Hey Martin,

I achieved my desired output

frontend.controller.resource.FrontPageResource@1188af http://localhost:8084/
frontend.controller.resource.ontology.OntologyListResource_at_124d7c5
http://localhost:8084/ontologies
frontend.controller.resource.ontology.OntologyResource_at_1972c3b
http://localhost:8084/ontologies/sioc
frontend.controller.resource.ontology.OntologyClassListResource_at_ca5cbd
http://localhost:8084/ontologies/sioc/classes
frontend.controller.resource.class_.ClassResource_at_9c7d55
http://localhost:8084/ontologies/sioc/classes/Item

using good old recursive solution from my own framework (simplified):

abstract public class Resource
{
    private Resource parent = null;

    public Resource(Resource parent, @Context UriInfo uriInfo)
    {
        super(parent);
        this.uriInfo = uriInfo;
        System.out.println(this + " " + getURI()); // now I can use getURI() for RDF!!!
    }

    public final UriBuilder getUriBuilder()
    {
        if (hasParent()) return getParent().getUriBuilder().path(getPath());
        else return UriBuilder.fromPath(getPath());
    }

    public abstract String getPath();

    public String getAbsolutePath()
    {
        return getUriBuilder().build().toString();
    }

    public boolean hasParent()
    {
        return getParent() != null;
    }

    public Resource getParent()
    {
        return parent;
    }
}

This finally gives me getURI() that makes sense for use with RDF. It
also requires implementing getPath() in all subclasses - but this is
no problem for me since I have them already.
I don't see how UriBuilder.fromResource() helps here since the
solution has to work on a Resource tree of arbitrary depth, not only
root resources and its subresources. And its only possible if I retain
the parent Resource.

I see this as a normal (if not common) RESTful use case, so I'm pretty
unimpressed that JAX-RS doesn't help out here :)

Martynas
semantic-web.dk

On Thu, Mar 10, 2011 at 3:37 PM, Martin Matula <martin.matula_at_oracle.com> wrote:
> If you are accessing UriInfo when matching is not fully done - i.e. from a
> sub-resource locator, you can detect it is not fully matched yet by
> checking:
> uriInfo.getPath().equals(uriInfo.getMatchedURIs().get(0))
>
> Once the above returns true, the matched resource can be obtained from
> uriInfo.getMatchedResources().get(0).
>
> Martin
>
> On 10.3.2011 14:34, Martynas Jusevicius wrote:
>>
>> Let me ask this way - how do you check that Resource is a leaf of the
>> matching Resources tree (per-request)? I.e. request URI matches its
>> URI template and will not match any of its subresources.
>>
>> On Thu, Mar 10, 2011 at 2:03 PM, Martin Matula<martin.matula_at_oracle.com>
>>  wrote:
>>>
>>> Hi,
>>> The solution I've attached below should not have the issue you are
>>> mentioning.
>>> Regarding your comment:
>>>>>
>>>>> I think JAX-RS lacks means to retrieve URIs of (matching) resources,
>>>>> as opposed to request URI.
>>>
>>> It is impossible to do in general. Note the set of URI's matching a
>>> resource
>>> can be infinite.
>>> Martin
>>>
>>> public abstract class AbstractResource {
>>>    private Individual i;
>>>    AbstractResource() {
>>>    }
>>>
>>>    AbstractResource(URI uri) {
>>>        initIndividual(uri);
>>>    }
>>>
>>>    void initIndividual(URI uri) {
>>>        System.out.println(uri);
>>>        i = ///
>>>    }
>>> }
>>>
>>> @Path("/")
>>> @Singleton
>>> public class TestRootResource extends AbstractResource {
>>>    private TestSubResource subResource;
>>>
>>>    private synchronized void init(UriInfo uriInfo) {
>>>        if (subResource == null) {
>>>            initIndividual(uriInfo.getBaseUri());
>>>            subResource = new
>>>
>>> TestSubResource(uriInfo.getBaseUriBuilder().path(TestSubResource.PATH).build());
>>>        }
>>>    }
>>>
>>>    @GET
>>>    @Produces("text/plain")
>>>    public String doGet(@Context UriInfo uriInfo) {
>>>        init(uriInfo);
>>>        return "TestResource.doGet()";
>>>    }
>>>
>>>    @Path(TestSubResource.PATH)
>>>    public TestSubResource getSubResource(@Context UriInfo uriInfo) {
>>>        init(uriInfo);
>>>        return subResource;
>>>    }
>>> }
>>>
>>> // no annotations needed here
>>> public class TestSubResource extends AbstractResource {
>>>    public static final String PATH = "sub";
>>>
>>>    TestSubResource(URI uri) {
>>>        super(uri);
>>>    }
>>>
>>>    @GET
>>>    @Produces("text/plain")
>>>    public String doGet() {
>>>        return "TestSubResource.doGet()";
>>>    }
>>> }
>>>
>>>
>>> On 10.3.2011 13:42, Martynas Jusevicius wrote:
>>>>
>>>> Martin,
>>>>
>>>> I ran into another issue. As UriInfo in Resource constructors
>>>> represents request URI and not Resource URI, and it gets passed up the
>>>> subresource chain, I can only use it for initialization when I know
>>>> that this is actually the matching Resource (the end of the chain).
>>>>
>>>> public Resource(Resource parent, @Context UriInfo uriInfo)
>>>> {
>>>>   super(parent);
>>>>   this.uriInfo = uriInfo;
>>>>   System.out.println(this + " " + uriInfo.getAbsolutePath());
>>>>   // do some initialization using absolute Resource URI
>>>>   // setIndividual(uriInfo.getAbsolutePath());
>>>> }
>>>>
>>>> frontend.controller.resource.FrontPageResource_at_be843c
>>>> http://localhost:8084/ontologies/sioc/classes/Item
>>>> frontend.controller.resource.ontology.OntologyListResource_at_c6173f
>>>> http://localhost:8084/ontologies/sioc/classes/Item
>>>> frontend.controller.resource.ontology.OntologyResource_at_1698552
>>>> http://localhost:8084/ontologies/sioc/classes/Item
>>>> frontend.controller.resource.ontology.OntologyClassListResource_at_974121
>>>> http://localhost:8084/ontologies/sioc/classes/Item
>>>> frontend.controller.resource.class_.ClassResource_at_1b7a9c1
>>>> http://localhost:8084/ontologies/sioc/classes/Item
>>>>
>>>> The ClassResource is the one that matches the URI. So would it be a
>>>> solution to discard the previous constructor calls by checking
>>>>
>>>> if (this == uriInfo.getMatchedResources().get(0))
>>>> setIndividual(uriInfo.getAbsolutePath())
>>>>
>>>> But debugging I can't really see if the matching Resource goes into
>>>> matchedResources, so maybe it's better like this if it doesn't
>>>>
>>>> if (!uriInfo.getMatchedResources().contains(this))
>>>> setIndividual(uriInfo.getAbsolutePath())
>>>>
>>>> I hope you get what I mean :)
>>>>
>>>> I think JAX-RS lacks means to retrieve URIs of (matching) resources,
>>>> as opposed to request URI.
>>>>
>>>> Martynas
>>>>
>>>> On Wed, Mar 9, 2011 at 7:06 PM, Martin Matula<martin.matula_at_oracle.com>
>>>>  wrote:
>>>>>
>>>>> I see, if you have a property with the base uri, then it is probably
>>>>> easiest
>>>>> to do everything in the constructor of your root resource.
>>>>> Martin
>>>>>
>>>>> On 9.3.2011 18:59, Martynas Jusevicius wrote:
>>>>>>
>>>>>> Thanks.
>>>>>>
>>>>>> I would have to do the whole model in one go, because the
>>>>>> representation methods of the root resource are already going to run
>>>>>> queries on it.
>>>>>>
>>>>>> Do you think it's a better idea than storing base URI in some property
>>>>>> file and using say ServletContextListener for initialization?
>>>>>>
>>>>>> On Wed, Mar 9, 2011 at 6:42 PM, Martin
>>>>>> Matula<martin.matula_at_oracle.com>
>>>>>>  wrote:
>>>>>>>
>>>>>>> Either you can lazily initialize the Individuals in your resources -
>>>>>>> i.e.
>>>>>>> getIndividual() method can take UriInfo as a parameter and can lazily
>>>>>>> initialize the individual instance variable if it is null, and return
>>>>>>> its
>>>>>>> content otherwise (this would have to be in a synchronized block):
>>>>>>> public abstract class AbstractResource {
>>>>>>>    private Individual i;
>>>>>>>
>>>>>>>    public synchronized Individual getIndividual(UriInfo uriInfo) {
>>>>>>>        if (i == null) {
>>>>>>>            i = new Individual(uriInfo.getAbsolutePath());
>>>>>>>        }
>>>>>>>        return i;
>>>>>>>    }
>>>>>>> }
>>>>>>>
>>>>>>> @Path("/")
>>>>>>> @Singleton
>>>>>>> public class TestRootResource extends AbstractResource {
>>>>>>>    @GET
>>>>>>>    @Produces("text/plain")
>>>>>>>    public String doGet(@Context UriInfo uriInfo) {
>>>>>>>        return "TestResource.doGet() - Individual: " +
>>>>>>> getIndividual(uriInfo);
>>>>>>>    }
>>>>>>> }
>>>>>>>
>>>>>>> @Path("/sub")
>>>>>>> @Singleton
>>>>>>> public class TestSubResource extends AbstractResource {
>>>>>>>    @GET
>>>>>>>    @Produces("text/plain")
>>>>>>>    public String doGet(@Context UriInfo uriInfo) {
>>>>>>>        return "TestSubResource.doGet() - Individual: " +
>>>>>>> getIndividual(uriInfo);
>>>>>>>    }
>>>>>>> }
>>>>>>>
>>>>>>> Or you can do eager initialization of the whole model on the first
>>>>>>> request
>>>>>>> and use subresource locators - e.g. this should work:
>>>>>>>
>>>>>>> public abstract class AbstractResource {
>>>>>>>    private Individual i;
>>>>>>>    AbstractResource() {
>>>>>>>    }
>>>>>>>
>>>>>>>    AbstractResource(URI uri) {
>>>>>>>        initIndividual(uri);
>>>>>>>    }
>>>>>>>
>>>>>>>    void initIndividual(URI uri) {
>>>>>>>        System.out.println(uri);
>>>>>>>        i = ///
>>>>>>>    }
>>>>>>> }
>>>>>>>
>>>>>>> @Path("/")
>>>>>>> @Singleton
>>>>>>> public class TestRootResource extends AbstractResource {
>>>>>>>    private TestSubResource subResource;
>>>>>>>
>>>>>>>    private synchronized void init(UriInfo uriInfo) {
>>>>>>>        if (subResource == null) {
>>>>>>>            initIndividual(uriInfo.getAbsolutePath());
>>>>>>>            subResource = new
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> TestSubResource(uriInfo.getAbsolutePathBuilder().path(TestSubResource.PATH).build());
>>>>>>>        }
>>>>>>>    }
>>>>>>>
>>>>>>>    @GET
>>>>>>>    @Produces("text/plain")
>>>>>>>    public String doGet(@Context UriInfo uriInfo) {
>>>>>>>        init(uriInfo);
>>>>>>>        return "TestResource.doGet()";
>>>>>>>    }
>>>>>>>
>>>>>>>    @Path(TestSubResource.PATH)
>>>>>>>    public TestSubResource getSubResource(@Context UriInfo uriInfo) {
>>>>>>>        init(uriInfo);
>>>>>>>        return subResource;
>>>>>>>    }
>>>>>>> }
>>>>>>>
>>>>>>> // no annotations needed here
>>>>>>> public class TestSubResource extends AbstractResource {
>>>>>>>    public static final String PATH = "sub";
>>>>>>>
>>>>>>>    TestSubResource(URI uri) {
>>>>>>>        super(uri);
>>>>>>>    }
>>>>>>>
>>>>>>>    @GET
>>>>>>>    @Produces("text/plain")
>>>>>>>    public String doGet() {
>>>>>>>        return "TestSubResource.doGet()";
>>>>>>>    }
>>>>>>> }
>>>>>>>
>>>>>>> Hope this helps.
>>>>>>> Martin
>>>>>>>
>>>>>>> On 9.3.2011 18:06, Martynas Jusevicius wrote:
>>>>>>>>
>>>>>>>> Sorry Martin, you're right! Base URI was set on each request...
>>>>>>>>
>>>>>>>> I was so fixated on getting UriInfo to work and use it to lookup
>>>>>>>> resources in the RDF model that I forgot that model had to be
>>>>>>>> initialized in the first place.
>>>>>>>>
>>>>>>>> Now I see what you're saying - since I need absolute URIs to
>>>>>>>> initialize RDF and they cannot be resolved without base URI, this
>>>>>>>> needs to be done within the request context. This is not even JAX-RS
>>>>>>>> specific, it's just that I'm implementing RDF and JAX-RS at the same
>>>>>>>> time..
>>>>>>>>
>>>>>>>> And when I have the base URI of the request, I can build an absolute
>>>>>>>> one using UriBuilder.fromResource(class).build(), right?
>>>>>>>> The question is, where should I put the initialization? In @GET of
>>>>>>>> the
>>>>>>>> root Resource and mark it with some flag so it only runs once?
>>>>>>>> Or is there a better hook for that?
>>>>>>>>
>>>>>>>> Martynas
>>>>>>>>
>>>>>>>> On Wed, Mar 9, 2011 at 5:01 PM, Martin
>>>>>>>> Matula<martin.matula_at_oracle.com>
>>>>>>>>  wrote:
>>>>>>>>>
>>>>>>>>> I still don't see how that gets you any more further than
>>>>>>>>> UriBuilder.fromResource(class).build().
>>>>>>>>> The root resource needs to figure out the server name, port,
>>>>>>>>> context
>>>>>>>>> path
>>>>>>>>> and server mapping. How did you get that without the request
>>>>>>>>> context?
>>>>>>>>> If you figured out how to get the "base URI" it for your servlet
>>>>>>>>> outside
>>>>>>>>> of
>>>>>>>>> the request context, you can do the same thing for your JAX-RS
>>>>>>>>> resources
>>>>>>>>> and
>>>>>>>>> then just do
>>>>>>>>> UriBuilder.fromUri(baseUri).path(Resource.class).build().
>>>>>>>>> Martin
>>>>>>>>>
>>>>>>>>> On 9.3.2011 16:49, Martynas Jusevicius wrote:
>>>>>>>>>>
>>>>>>>>>> I had a recursive Resource.getAbsolutePath() which went up the
>>>>>>>>>> parent
>>>>>>>>>> tree concatenating getPath().
>>>>>>>>>>
>>>>>>>>>> For singletons I could get the URI like this:
>>>>>>>>>> SearchResource.getInstance().getAbsolutePath().
>>>>>>>>>>
>>>>>>>>>> For other Resources the mapping happened within the request.
>>>>>>>>>>
>>>>>>>>>> On Wed, Mar 9, 2011 at 4:31 PM, Martin
>>>>>>>>>> Matula<martin.matula_at_oracle.com>
>>>>>>>>>>  wrote:
>>>>>>>>>>>
>>>>>>>>>>> Hi Martynas,
>>>>>>>>>>>
>>>>>>>>>>> On 9.3.2011 15:59, Martynas Jusevicius wrote:
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> I had achieved the same using abstract Resource superclass
>>>>>>>>>>>>>> (like
>>>>>>>>>>>>>> in
>>>>>>>>>>>>>> my
>>>>>>>>>>>>>> last example) with
>>>>>>>>>>>>>> - getPath() and getAbsolutePath() equivalents
>>>>>>>>>>>>>> - constructor Resource(Resource parent) - effectively building
>>>>>>>>>>>>>> a
>>>>>>>>>>>>>> parent/child tree of Resource instances
>>>>>>>>>>>>>> - Servlet-like doGet(), doPost() etc methods
>>>>>>>>>>>>>> and the real HttpServlet mapping request URIs to Resource
>>>>>>>>>>>>>> instances
>>>>>>>>>>>>>> and executing the appropriate do..() methods.
>>>>>>>>>>>
>>>>>>>>>>> I mean, how did you get the absolute URI outside of the request
>>>>>>>>>>> context?
>>>>>>>>>>> I
>>>>>>>>>>> am guessing all the above initialization happened upon the first
>>>>>>>>>>> request
>>>>>>>>>>> within it's context, no?
>>>>>>>>>>> Martin
>>>>>>>>>>>
>>>>>>>>>>>
>