Wheezil
Wheezil

Reputation: 3462

JAX-RS / jersey: client proxy: how to inject path parameter from context instead of method argument?

I am use the proxy pattern to call web services using JAX-RS and Jersey. I would like to conceptually do something like... add @Context annotation to the interface like:

@Path("foo/{instance}")
public interface Foo 
{
    @Context private UriInfo uriInfo;

    @POST
    @Path("doit")
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    public Thing1 doit(Thing2 arg);
}

But of course one cannot do that, it is an interface not a class. But an interface is what is needed for using the proxy pattern. I create a client using the proxy pattern:

ClientBuilder builder = ClientBuilder.newBuilder().register(JacksonJsonProvider.class);
MyRequestFilter filter = new MyRequestFilter();
builder = builder.register(filter);
WebTarget target = builder.build().target(url);
Foo serviceClient = WebResourceFactory.newResource(Foo.class, target);

However, calling serviceClient.doit(...) results in IllegalStateException: The template variable 'instance' has no value. I tried using a ClientRequestFilter to inject the instance variable, but my filter method is not even called before this exception is thrown. Is there any way to inject a path parameter from the client without adding it as a @PathParam argument to every method? The reason is, I already have an interface defined which I want my proxy client to implement, and adding a parameter changes the signatures.

Upvotes: 1

Views: 1282

Answers (2)

Wheezil
Wheezil

Reputation: 3462

Ultimately, there seems no way to do this directly. I've not found any way to inject the "instance" from a context in a Jersey client proxy. Even when Foo is class, and a ClientRequestFilter is installed, the filter is not called before the error is issued. As a workable solution, I basically took what @andrewjames suggested and created an interface with the extra instance argument. This satisfied the requirement to be able to create a proxy with a WebTarget. Then I created a class that implements the base interface (the one without the instance parameter in the methods), wraps the proxy instance, and delegates all method calls to the proxy. This isn't pretty, but since all of this is coming from a code-generation tool, neither is it especially tedious or error-prone.

Upvotes: 1

andrewJames
andrewJames

Reputation: 21910

In your JAX-WS client's Foo interface, add a @PathParam for the missing template value:

@Path("foo/{instance}")
public interface Foo 
{
    @POST
    @Path("doit")
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_JSON)
    public Thing1 doit(@PathParam("instance") String instance, Thing2 arg);
}

You will obviously also need to change how your client calls doit(), to accommodate this.

Thing1 thing1 = serviceClient.doit("anything", arg);

Your server resource (on which the client interface is based) does not need to change.


Caveat

This feels like a hack/workaround. It may not be the best solution, and I am a bit surprised it works at all. But it's the only way I know of cajoling the proxy into having a non-empty template value.

Upvotes: 2

Related Questions