Hello I am building an application using dropwizard, that is using jersey 2.16 internally as REST API framework.
For the whole application on all resource methods I
Method parameters are handled a little differently for injection. The component we need to implement for this, is the ValueFactoryProvider. Once you implement that, you also need to bind it in your AbstractBinder
.
Jersey has a pattern that it follows for implementing the ValueFactoryProvider
. This is the pattern used to handle parameters like @PathParam
and @QueryParam
. Jersey has a ValueFactoryProvider
for each one of those, as well as others.
The pattern is as follows:
Instead of implementing the ValueFactoryProvider
directly, we extend AbstractValueFactoryProvider
public static class TenantValueProvider extends AbstractValueFactoryProvider {
@Inject
public TenantValueProvider(MultivaluedParameterExtractorProvider mpep,
ServiceLocator locator) {
super(mpep, locator, Parameter.Source.UNKNOWN);
}
@Override
protected Factory> createValueFactory(Parameter parameter) {
if (!parameter.isAnnotationPresent(TenantParam.class)
|| !Tenant.class.equals(parameter.getRawType())) {
return null;
}
return new Factory() {
@Override
public Tenant provide() {
...
}
};
}
In this component, it has a method we need to implement that returns the Factory
that provides the method parameter value.
The InjectionResolver
is what is used to handle the custom annotation. With this pattern, instead of directly implementing it, as the OP has, we just extend ParamInjectionResolver
passing in our AbstractValueFactoryProvider
implementation class to super constructor
public static class TenantParamInjectionResolver
extends ParamInjectionResolver {
public TenantParamInjectionResolver() {
super(TenantValueProvider.class);
}
}
And that's really it. Then just bind the two components
public static class Binder extends AbstractBinder {
@Override
public void configure() {
bind(TenantParamInjectionResolver.class)
.to(new TypeLiteral>(){})
.in(Singleton.class);
bind(TenantValueProvider.class)
.to(ValueFactoryProvider.class)
.in(Singleton.class);
}
}
Below is a complete test using Jersey Test Framework. The required dependencies are listed in the javadoc comments. You can run the test like any other JUnit test
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.logging.Logger;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Response;
import org.glassfish.hk2.api.Factory;
import org.glassfish.hk2.api.InjectionResolver;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.hk2.api.TypeLiteral;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.filter.LoggingFilter;
import org.glassfish.jersey.server.ContainerRequest;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.internal.inject.AbstractContainerRequestValueFactory;
import org.glassfish.jersey.server.internal.inject.AbstractValueFactoryProvider;
import org.glassfish.jersey.server.internal.inject.MultivaluedParameterExtractorProvider;
import org.glassfish.jersey.server.internal.inject.ParamInjectionResolver;
import org.glassfish.jersey.server.model.Parameter;
import org.glassfish.jersey.server.spi.internal.ValueFactoryProvider;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* Stack Overflow https://stackoverflow.com/q/29145807/2587435
*
* Run this like any other JUnit test. Dependencies required are as the following
*
*
* org.glassfish.jersey.test-framework.providers
* jersey-test-framework-provider-grizzly2
* 2.22
* test
*
*
* org.glassfish.jersey.media
* jersey-media-json-jackson
* 2.22
* test
*
*
* @author Paul Samsotha
*/
public class TenantInjectTest extends JerseyTest {
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public static @interface TenantParam {
}
public static class User {
public String name;
}
public static class Tenant {
public String name;
public Tenant(String name) {
this.name = name;
}
}
public static class TenantValueProvider extends AbstractValueFactoryProvider {
@Inject
public TenantValueProvider(MultivaluedParameterExtractorProvider mpep,
ServiceLocator locator) {
super(mpep, locator, Parameter.Source.UNKNOWN);
}
@Override
protected Factory> createValueFactory(Parameter parameter) {
if (!parameter.isAnnotationPresent(TenantParam.class)
|| !Tenant.class.equals(parameter.getRawType())) {
return null;
}
return new AbstractContainerRequestValueFactory() {
// You can @Inject things here if needed. Jersey will inject it.
// for example @Context HttpServletRequest
@Override
public Tenant provide() {
final ContainerRequest request = getContainerRequest();
final String name
= request.getUriInfo().getQueryParameters().getFirst("tenent");
return new Tenant(name);
}
};
}
public static class TenantParamInjectionResolver
extends ParamInjectionResolver {
public TenantParamInjectionResolver() {
super(TenantValueProvider.class);
}
}
public static class Binder extends AbstractBinder {
@Override
public void configure() {
bind(TenantParamInjectionResolver.class)
.to(new TypeLiteral>(){})
.in(Singleton.class);
bind(TenantValueProvider.class)
.to(ValueFactoryProvider.class)
.in(Singleton.class);
}
}
}
@Path("test")
@Produces("text/plain")
@Consumes("application/json")
public static class TestResource {
@POST
public String post(User user, @TenantParam Tenant tenent) {
return user.name + ":" + tenent.name;
}
}
@Override
public ResourceConfig configure() {
return new ResourceConfig(TestResource.class)
.register(new TenantValueProvider.Binder())
.register(new LoggingFilter(Logger.getAnonymousLogger(), true));
}
@Test
public void shouldReturnTenantAndUserName() {
final User user = new User();
user.name = "peeskillet";
final Response response = target("test")
.queryParam("tenent", "testing")
.request()
.post(Entity.json(user));
assertEquals(200, response.getStatus());
assertEquals("peeskillet:testing", response.readEntity(String.class));
}
}
See Also: