HK2 is not injecting the HttpServletRequest with jersey - java

I am trying to follow the example located here to create a factory in order to inject my HttpSession. Unfortunately no matter what I try it is not working. Not sure what could be the issue.
I have tried injecting just the HttpServletRequest and a provider. Here is my example using a provider. The error is a null pointer exception when trying to access the provider in the provide method. If I try to inject the HttpServletRequest I get no object available for injection. I am running this inside the GrizzlyTestContainer using JerseyTest. Is there something I need to add to my binder in order to bind the HttpServletRequest? I cannot seem to find an example.
public class HttpSessionFactory implements Factory<HttpSession> {
private final HttpServletRequest request;
#Inject
public HttpSessionFactory(Provider<HttpServletRequest> requestProvider) {
this.request = requestProvider.get();
}
#Override
public HttpSession provide() {
return request.getSession();
}
#Override
public void dispose(HttpSession t) {
}
}

You should #Override protected DeploymentContext configureDeployment() in the JerseyTest to return a ServletDeploymentContext. For example
import javax.inject.Inject;
import javax.inject.Provider;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;
import org.glassfish.hk2.api.Factory;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.servlet.ServletContainer;
import org.glassfish.jersey.test.DeploymentContext;
import org.glassfish.jersey.test.JerseyTest;
import org.glassfish.jersey.test.ServletDeploymentContext;
import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
import org.glassfish.jersey.test.spi.TestContainerException;
import org.glassfish.jersey.test.spi.TestContainerFactory;
import org.junit.Test;
public class ServletTest extends JerseyTest {
#Path("/session")
public static class SessionResource {
#Inject
HttpSession session;
#GET
public Response getSessionId() {
return Response.ok(session.getId()).build();
}
}
public static class HttpSessionFactory implements Factory<HttpSession> {
private final HttpServletRequest request;
#Inject
public HttpSessionFactory(Provider<HttpServletRequest> requestProvider) {
this.request = requestProvider.get();
}
#Override
public HttpSession provide() {
return request.getSession();
}
#Override
public void dispose(HttpSession t) {
}
}
#Override
protected TestContainerFactory getTestContainerFactory() {
return new GrizzlyWebTestContainerFactory();
}
#Override
protected DeploymentContext configureDeployment() {
ResourceConfig config = new ResourceConfig(SessionResource.class);
config.register(new AbstractBinder() {
#Override
protected void configure() {
bindFactory(HttpSessionFactory.class).to(HttpSession.class);
}
});
return ServletDeploymentContext.forServlet(
new ServletContainer(config)).build();
}
#Test
public void test() {
System.out.println(target("session").request().get(String.class));
}
}
You can see more examples in the source code tests

Related

Inject SecurityContext info and Request Body into #BeanParam

Currently I'm rendering a command object in a MessageBodyReader but I'd like to be able to do this in a #BeanParam:
Inject a field derived from the SecurityContext (Is there somewhere to hook in the conversion?).
have a field inject that has been materialised by a MessageBodyReader.
Is this possible ?
Note: Go Down to UPDATE. I guess it is possible to use #BeanParam. Though you need to inject the SecurityContext into the bean and extract the name info.
There's no way to achieve this with #BeanParam corrected. You could use a MessageBodyReader the way you are doing, but IMO that's more of a hack than anything. Instead, the way I would achieve it is to use the framework components the way they are supposed to be used, which involves custom parameter injection.
To achieve this, you need two things, a ValueFactoryProvider to provide parameter values, and a InjectionResolver with your own custom annotation. I won't do much explaining for the example below, but you can find a good explanation in
Jersey 2.x Custom Injection Annotation With Attributes
You can run the below example like any JUnit test. Everything is included into the one class. These are the dependencies I used.
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
<version>2.19</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.19</version>
<scope>test</scope>
</dependency>
And here is the test
import java.io.IOException;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.security.Principal;
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.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.PreMatching;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
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.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 static org.junit.Assert.assertEquals;
import org.junit.Test;
public class CustomInjectionTest extends JerseyTest {
#Target(ElementType.PARAMETER)
#Retention(RetentionPolicy.RUNTIME)
public static #interface CustomParam {
}
public static class CustomModel {
public String name;
public RequestBody body;
}
public static class RequestBody {
public String message;
}
public static class CustomParamValueFactory
extends AbstractContainerRequestValueFactory<CustomModel> {
#Override
public CustomModel provide() {
ContainerRequest request = getContainerRequest();
String name = request.getSecurityContext().getUserPrincipal().getName();
RequestBody body = request.readEntity(RequestBody.class);
CustomModel model = new CustomModel();
model.body = body;
model.name = name;
return model;
}
}
public static class CustomValueFactoryProvider extends AbstractValueFactoryProvider {
#Inject
public CustomValueFactoryProvider(MultivaluedParameterExtractorProvider multiProvider,
ServiceLocator locator) {
super(multiProvider, locator, Parameter.Source.UNKNOWN);
}
#Override
protected Factory<?> createValueFactory(Parameter parameter) {
if (CustomModel.class == parameter.getType()
&& parameter.isAnnotationPresent(CustomParam.class)) {
return new CustomParamValueFactory();
}
return null;
}
}
public static class CustomParamInjectionResolver extends ParamInjectionResolver<CustomParam> {
public CustomParamInjectionResolver() {
super(CustomValueFactoryProvider.class);
}
}
private static class CustomInjectBinder extends AbstractBinder {
#Override
protected void configure() {
bind(CustomValueFactoryProvider.class)
.to(ValueFactoryProvider.class)
.in(Singleton.class);
bind(CustomParamInjectionResolver.class)
.to(new TypeLiteral<InjectionResolver<CustomParam>>(){})
.in(Singleton.class);
}
}
private static final String PRINCIPAL_NAME = "peeskillet";
#PreMatching
public static class SecurityContextFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
requestContext.setSecurityContext(new SecurityContext(){
public Principal getUserPrincipal() {
return new Principal() {
public String getName() { return PRINCIPAL_NAME; }
};
}
public boolean isUserInRole(String role) { return false; }
public boolean isSecure() { return true;}
public String getAuthenticationScheme() { return null; }
});
}
}
#Path("test")
public static class TestResource {
#POST
#Produces(MediaType.TEXT_PLAIN)
#Consumes(MediaType.APPLICATION_JSON)
public String post(#CustomParam CustomModel model) {
return model.name + ":" + model.body.message;
}
}
#Override
public ResourceConfig configure() {
return new ResourceConfig(TestResource.class)
.register(SecurityContextFilter.class)
.register(new CustomInjectBinder());
}
#Test
public void should_return_name_with_body() {
RequestBody body = new RequestBody();
body.message = "Hello World";
Response response = target("test").request()
.post(Entity.json(body));
assertEquals(200, response.getStatus());
String responseBody = response.readEntity(String.class);
assertEquals(PRINCIPAL_NAME + ":" + body.message, responseBody);
System.out.println(responseBody);
}
}
Note that I read the request body from the ContainerRequest inside the CustomParamValueFactory. It is the same RequestBody that I sent in JSON from the request in the #Test.
UPDATE
So to my surprise, it is possible to use #BeanParam. Here is the following bean I used to test
public static class CustomModel {
#Context
public SecurityContext securityContext;
public RequestBody body;
}
public static class RequestBody {
public String message;
}
The difference from the previous test is that instead of the name from the SecurityContext.Principal, we need to inject the entire SecurityContext. There's just no way for the inject to get the name from the Principal, So we will just do it manually.
The thing that surprised me the most though, is that we are able to inject the RequestBody entity. I didn't know this was possible.
Here is the complete test
import java.io.IOException;
import java.security.Principal;
import javax.ws.rs.BeanParam;
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.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.PreMatching;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
public class CustomInjectTestTake2 extends JerseyTest {
private static final String PRINCIPAL_NAME = "peeskillet";
private static final String MESSAGE = "Hello World";
private static final String RESPONSE = PRINCIPAL_NAME + ":" + MESSAGE;
public static class CustomModel {
#Context
public SecurityContext securityContext;
public RequestBody body;
}
public static class RequestBody {
public String message;
}
#PreMatching
public static class SecurityContextFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
requestContext.setSecurityContext(new SecurityContext(){
public Principal getUserPrincipal() {
return new Principal() {
public String getName() { return PRINCIPAL_NAME; }
};
}
public boolean isUserInRole(String role) { return false; }
public boolean isSecure() { return true;}
public String getAuthenticationScheme() { return null; }
});
}
}
#Path("test")
public static class TestResource {
#POST
#Produces(MediaType.TEXT_PLAIN)
#Consumes(MediaType.APPLICATION_JSON)
public String post(#BeanParam CustomModel model) {
return model.securityContext.getUserPrincipal().getName()
+ ":" + model.body.message;
}
}
#Override
public ResourceConfig configure() {
return new ResourceConfig(TestResource.class)
.register(SecurityContextFilter.class);
}
#Test
public void should_return_name_with_body() {
RequestBody body = new RequestBody();
body.message = "Hello World";
Response response = target("test").request()
.post(Entity.json(body));
assertEquals(200, response.getStatus());
String responseBody = response.readEntity(String.class);
assertEquals(RESPONSE, responseBody);
System.out.println(responseBody);
}
}
See Also:
Custom Injection and Lifecycle Management

Jaxrs Filter at Resource Locator

I'm trying to intercept requests to my jaxrs apis basead on annotations, my filter is simple:
#Provider
public class Filter implements ContainerRequestFilter {
#Context
private ResourceInfo info;
#Override
public void filter(ContainerRequestContext crc) throws IOException {
// here I'm trying to get the annotate resource class or method.
info.getResourceClass().isAnnotationPresent(MyCustomAnnotation.class);
}
}
this works fine with a simple resource like this: (works both in class and method)
#Path("/")
public class SimpleResource {
#GET
#MyCustomAnnotation
public String test() {
return "test";
}
}
But in my real application, I have scenarios like this:
#Path("/")
public class RootResource {
#Inject
ChildResource childResource;
#Path("child")
public ChildResource child () {
return childResource;
}
}
So, I wanna put my custom annotation only on ResourceLocator and on the fly verify that the final resource contains the annotation.
#Path("/")
#CustomAnnotation
public class RootResource {
#Inject
ChildResource childResource;
#Path("child")
public ChildResource child () {
return childResource;
}
}
is it possible? or i can only get information about the matched resource?
"In jersey how would be this?"
With Jersey you have access to the resource model, and ways to traverse the model. You can see jersey server introspectionmodeller not public in v2.0? for some explanation and examples of how to traverse the model and Resource and ResourceMethod. Other than that, there is not much documentation these APIs.
Below is a complete example Using Jersey Test Framework. You can run the class like any other JUnit test. You just need this one dependency to run it
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
<version>2.19</version>
<scope>test</scope>
</dependency>
And here's the test.
import java.io.IOException;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.List;
import java.util.logging.Logger;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;
import static junit.framework.Assert.assertEquals;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.filter.LoggingFilter;
import org.glassfish.jersey.server.ExtendedUriInfo;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.model.Resource;
import org.glassfish.jersey.server.model.ResourceMethod;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;
public class ResourceModelTest extends JerseyTest {
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
public static #interface ResourceAnnotation {
String value();
}
#Path("root")
#ResourceAnnotation("SomeValue")
public static class ParentResource {
#Path("sub")
public ChildResource getChild() {
return new ChildResource();
}
#GET
public String get() {
return "ROOT";
}
}
public static class ChildResource {
#GET
public String get() {
return "CHILD";
}
}
#Provider
public static class ResourceFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext requestContext,
ContainerResponseContext responseContext) throws IOException {
ExtendedUriInfo info = (ExtendedUriInfo) requestContext.getUriInfo();
List<ResourceMethod> resourceLocators = info.getMatchedResourceLocators();
if (!resourceLocators.isEmpty()) {
Resource parent = resourceLocators.get(0).getParent();
Class<?> parentClass = parent.getHandlerClasses().iterator().next();
ResourceAnnotation anno = parentClass.getAnnotation(ResourceAnnotation.class);
if (anno != null) {
responseContext.getHeaders().putSingle("X-SubResource-Header", anno.value());
}
}
}
}
#Override
public ResourceConfig configure() {
return new ResourceConfig(ParentResource.class)
.register(ResourceFilter.class);
}
#Override
public void configureClient(ClientConfig config) {
config.register(new LoggingFilter(Logger.getAnonymousLogger(), true));
}
#Test
public void get_child_resource() {
Response response = target("root/sub").request().get();
assertEquals(200, response.getStatus());
assertEquals("SomeValue", response.getHeaderString("X-SubResource-Header"));
}
}

can't inject Providers mock in JerseyTest

I'm getting Providers from context in my filter to get defined ObjectMapper
public class Filter implements ContainerRequestFilter, ContainerResponseFilter {
#Context
private Providers providers;
#Context
private HttpServletRequest request;
private ObjectMapper getObjectMapper() {
ContextResolver<ObjectMapper> contextResolver = providers.getContextResolver(ObjectMapper.class, MediaType.APPLICATION_JSON_TYPE);
if (contextResolver == null) {
return new ObjectMapper();
}
return contextResolver.getContext(null);
}
}
but in test I can't inject mock in this filter using abstract binder with HttpServletRequest it works fine but Providers isn't mock. Example of test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({ "..." })
#PrepareForTest({ ... })
public class Test extends JerseyTest {
#Rule
public PowerMockRule rule = new PowerMockRule();
private HttpServletRequest request;
private Providers providers;
#Override
protected Application configure() {
ResourceConfig config = new ResourceConfig(TestResource.class, Filter.class);
providers = mock(Providers.class);
request = mock(HttpServletRequest.class);
config.register(new AbstractBinder() {
#Override
protected void configure() {
bind(providers).to(Providers.class);
}
});
config.register(new AbstractBinder() {
#Override
protected void configure() {
bind(request).to(HttpServletRequest.class);
}
});
return config;
}
Why HttpServletRequest is mock in filter but Providers is not?
Providers shouldn't have to be mocked. It is handled by the framework. Any providers you want added, just register with the ResourceConfig. I don't know what you care doing wrong in your attempt at this, but below is a complete working example where the ContextResolver is discovered just fine.
If you still can't figure it out, please provide a full working single class example (without any mock or Spring stuff) like I have done.
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.ext.Providers;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Assert;
import org.junit.Test;
public class ContextResolverTest extends JerseyTest {
#Provider
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public static class OMContextResolver implements ContextResolver<ObjectMapper> {
private final ObjectMapper mapper = new ObjectMapper();
#Override
public ObjectMapper getContext(Class<?> type) {
return mapper;
}
}
#Provider
public static class Filter implements ContainerRequestFilter {
#Context
private Providers providers;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
ContextResolver<ObjectMapper> contextResolver
= providers.getContextResolver(ObjectMapper.class,
MediaType.APPLICATION_JSON_TYPE);
if (contextResolver == null) {
requestContext.abortWith(
Response.serverError().entity("no resolver").build());
} else {
ObjectMapper mapper = contextResolver.getContext(null);
if (mapper == null) {
requestContext.abortWith(
Response.serverError().entity("no mapper").build());
return;
}
requestContext.abortWith(
Response.ok("resolver found").build());
}
}
}
#Path("test")
public static class TestResource {
#GET
public String dummyGet() {
return "Boo";
}
}
#Override
public Application configure() {
ResourceConfig config = new ResourceConfig();
config.register(TestResource.class);
config.register(OMContextResolver.class);
config.register(Filter.class);
return config;
}
#Test
public void contextResolverIsOk() {
Response response = target("test").request().get();
Assert.assertEquals(200, response.getStatus());
Assert.assertEquals("resolver found", response.readEntity(String.class));
response.close();
}
}

Jersey - How to mock service

I am using "Jersey Test Framework" for unit testing my webservice.
Here is my resource class :
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
// The Java class will be hosted at the URI path "/helloworld"
#Path("/helloworld")
public class class HelloWorldResource {
private SomeService service;
#GET
#Produces("text/plain")
public String getClichedMessage() {
// Return some cliched textual content
String responseFromSomeService = service.getSomething();
return responseFromSomeService;
}
}
How can I mock SomeService in unit tests ?
See Update below: You don't need a Factory
If you are using Jersey 2, one solution would be to use Custom Injection and Lifecycle Management feature (with HK2 - which comes with the Jersey dist). Also required would be a Mocking framework of course. I'm going to use Mockito.
First create a Factory with mocked instance:
public static interface GreetingService {
public String getGreeting(String name);
}
public static class MockGreetingServiceFactory
implements Factory<GreetingService> {
#Override
public GreetingService provide() {
final GreetingService mockedService
= Mockito.mock(GreetingService.class);
Mockito.when(mockedService.getGreeting(Mockito.anyString()))
.thenAnswer(new Answer<String>() {
#Override
public String answer(InvocationOnMock invocation)
throws Throwable {
String name = (String)invocation.getArguments()[0];
return "Hello " + name;
}
});
return mockedService;
}
#Override
public void dispose(GreetingService t) {}
}
Then use the AbstractBinder to bind the factory to the interface/service class, and register the binder. (It's all described in the link above):
#Override
public Application configure() {
AbstractBinder binder = new AbstractBinder() {
#Override
protected void configure() {
bindFactory(MockGreetingServiceFactory.class)
.to(GreetingService.class);
}
};
ResourceConfig config = new ResourceConfig(GreetingResource.class);
config.register(binder);
return config;
}
Seems like a lot, but it's just an option. I'm not too familiar with the test framework, or if it has an mocking capabilities for injection.
Here is the full test:
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.glassfish.hk2.api.Factory;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
public class ServiceMockingTest extends JerseyTest {
#Path("/greeting")
public static class GreetingResource {
#Inject
private GreetingService greetingService;
#GET
#Produces(MediaType.TEXT_PLAIN)
public String getGreeting(#QueryParam("name") String name) {
return greetingService.getGreeting(name);
}
}
public static interface GreetingService {
public String getGreeting(String name);
}
public static class MockGreetingServiceFactory
implements Factory<GreetingService> {
#Override
public GreetingService provide() {
final GreetingService mockedService
= Mockito.mock(GreetingService.class);
Mockito.when(mockedService.getGreeting(Mockito.anyString()))
.thenAnswer(new Answer<String>() {
#Override
public String answer(InvocationOnMock invocation)
throws Throwable {
String name = (String)invocation.getArguments()[0];
return "Hello " + name;
}
});
return mockedService;
}
#Override
public void dispose(GreetingService t) {}
}
#Override
public Application configure() {
AbstractBinder binder = new AbstractBinder() {
#Override
protected void configure() {
bindFactory(MockGreetingServiceFactory.class)
.to(GreetingService.class);
}
};
ResourceConfig config = new ResourceConfig(GreetingResource.class);
config.register(binder);
return config;
}
#Test
public void testMockedGreetingService() {
Client client = ClientBuilder.newClient();
Response response = client.target("http://localhost:9998/greeting")
.queryParam("name", "peeskillet")
.request(MediaType.TEXT_PLAIN).get();
Assert.assertEquals(200, response.getStatus());
String msg = response.readEntity(String.class);
Assert.assertEquals("Hello peeskillet", msg);
System.out.println("Message: " + msg);
response.close();
client.close();
}
}
Dependencies for this test:
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
<version>2.13</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.0</version>
</dependency>
UPDATE
So in most cases, you really don't need a Factory. You can simply bind the mock instance with its contract:
#Mock
private Service service;
#Override
public ResourceConfig configure() {
MockitoAnnotations.initMocks(this);
return new ResourceConfig()
.register(MyResource.class)
.register(new AbstractBinder() {
#Override
protected configure() {
bind(service).to(Service.class);
}
});
}
#Test
public void test() {
when(service.getSomething()).thenReturn("Something");
// test
}
Much simpler!
Here is how I did it with Jersey 2.20, Spring 4.1.4 RELEASE, Mockito 1.10.8, and TestNG 6.8.8.
#Test
public class CasesResourceTest extends JerseyTestNg.ContainerPerMethodTest {
#Mock
private CaseService caseService;
#Mock
private CaseConverter caseConverter;
#Mock
private CaseRepository caseRepository;
private CasesResource casesResource;
#Override
protected Application configure() {
MockitoAnnotations.initMocks(this);
casesResource = new CasesResource();
AbstractBinder binder = new AbstractBinder() {
#Override
protected void configure() {
bindFactory(new InstanceFactory<CaseConverter>(caseConverter)).to(CaseConverter.class);
bindFactory(new InstanceFactory<CaseService>(caseService)).to(CaseService.class);
}
};
return new ResourceConfig()
.register(binder)
.register(casesResource)
.property("contextConfigLocation", "solve-scm-rest/test-context.xml");
}
public void getAllCases() throws Exception {
when(caseService.getAll()).thenReturn(Lists.newArrayList(new solve.scm.domain.Case()));
when(caseConverter.convertToApi(any(solve.scm.domain.Case.class))).thenReturn(new Case());
Collection<Case> cases = target("/cases").request().get(new GenericType<Collection<Case>>(){});
verify(caseService, times(1)).getAll();
verify(caseConverter, times(1)).convertToApi(any(solve.scm.domain.Case.class));
assertThat(cases).hasSize(1);
}
}
You also need this class which makes the binding code above a bit easier:
public class InstanceFactory<T> implements Factory<T> {
private T instance;
public InstanceFactory(T instance) {
this.instance = instance;
}
#Override
public void dispose(T t) {
}
#Override
public T provide() {
return instance;
}
}
Edited as pr. request. This is the contents of my test-context.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
It turns out that my test-context.xml does not instantiate any beans nor scan any packages, in fact, it does not do anything at all. I guess I just put it there in case I might need it.

Servlet context injection fail while using jersey test framework

I'm beginning with jersey and trying to get freemarker working with it using TDD. I want to make a ViewProcessor for my templates, but fail to inject the servlet context in the class.
Here is the class code :
#Provider
public class myProcessor implements ViewProcessor<Template> {
[...]
#Context
public ServletContext myContext;
[...]
freemarkerConfiguration.setTemplateLoader(
new WebappTemplateLoader(myContext,
myContext.getInitParameter("freemarker.template.path")));
[...]
}
And here is the test code :
public class myProcessorTest extends JerseyTest {
public static myProcessor mp;
public myProcessorTest() throws Exception{
super(new WebAppDescriptor.Builder("com.domain").build());
}
#Test
public void firstTest(){
mp = new myProcessor();
String path = new String("test.ftl");
Template template = mp.resolve(path);
assertNotNull(template);
}
}
I use maven with dependencies as follow:
<dependency>
<groupId>com.sun.jersey.jersey-test-framework</groupId>
<artifactId>jersey-test-framework-grizzly</artifactId>
<version>1.5-SNAPSHOT</version>
<scope>test</scope>
</dependency>
My code runs fine when I deploy to my local jetty server. But if I want to test the code in my IDE, it failed to inject the servlet context (#Context) : myContext is null when I run the test :/
I think I'm missing something, but I'm a complete beginner with servlet world.
Here's a technique for testing a specific resource class, using Jersey Test Framework, with servlet support. Also demonstrates how to customize the ServletContext.
import javax.servlet.ServletContext;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Feature;
import javax.ws.rs.core.FeatureContext;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.servlet.ServletContainer;
import org.glassfish.jersey.test.DeploymentContext;
import org.glassfish.jersey.test.JerseyTest;
import org.glassfish.jersey.test.ServletDeploymentContext;
import org.glassfish.jersey.test.TestProperties;
import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
import org.glassfish.jersey.test.spi.TestContainerException;
import org.glassfish.jersey.test.spi.TestContainerFactory;
import static org.mockito.Mockito.mock;
/**
* A base class for testing web resources.
*/
public abstract class WebResourceTest extends JerseyTest {
/**
* Creates a JAX-RS resource configuration for test purposes.
*/
#Override
protected abstract ResourceConfig configure();
/**
* Creates a test container factory with servlet support.
*/
#Override
protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
return new GrizzlyWebTestContainerFactory();
}
/**
* Configures a deployment context for JAX-RS.
*/
#Override
protected DeploymentContext configureDeployment() {
ResourceConfig app = configure();
app.register(new Feature() {
#Context
ServletContext servletContext;
#Override
public boolean configure(FeatureContext context) {
servletContext.setAttribute("example", new Object());
return true;
}
});
return ServletDeploymentContext.forServlet(new ServletContainer(app)).build();
}
}
A usage example:
import org.glassfish.jersey.server.ResourceConfig;
import javax.ws.rs.core.Context;
import javax.ws.rs.Path;
import javax.ws.rs.GET;
import javax.ws.rs.core.Response;
import static org.mockito.Mockito.spy;
import static org.testng.Assert.assertEquals;
import static org.junit.Assert.*;
public class MyResourceTest extends WebResourceTest {
private MyResource resource;
#Override
protected ResourceConfig configure() {
resource = spy(new MyResource());
return new ResourceConfig().register(resource);
}
#Test
public void testSomething() {
Response r = target("/myresource").request().get();
assertEquals(200, r.getStatus());
assertEquals(1, resource.count);
}
}
#Path("/myresource")
public class MyResource {
int count = 0;
#Context
protected ServletContext servletContext;
#GET
public void get() {
Object attr = servletContext.getAttribute("example");
count++;
}
}
There's a couple of ways to do it. Remove the constructor and implement a configure() method like this:
public class myProcessorTest extends JerseyTest {
public static myProcessor mp;
#Override
protected AppDescriptor configure() {
return new WebAppDescriptor.Builder("com.domain")
.contextParam("contextConfigLocation", "classpath:/applicationContext.xml")
.contextPath("/").servletClass(SpringServlet.class)
.contextListenerClass(ContextLoaderListener.class)
.requestListenerClass(RequestContextListener.class)
.build();
}
or alternatively you can annotate your test with the spring context:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("classpath:applicationContext.xml")
public class MyProcessorTest extends JerseyTest {
public static myProcessor mp;
There is a solution to this problem that does not require spring, assuming you are using the default/standard Grizzy2 test framework provider. According to this answer the jersey-test-framework-provider-grizzly2 framework provider does not utilize a servlet environment in constructing the application context. Your symptoms result from there being no ServletContext instance to inject.
The workaround is to provide the test container for the unit tests yourself. First, modify your dependencies:
<!--<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
<version>2.25</version>
<scope>test</scope>
</dependency>-->
<dependency>
<groupId>org.glassfish.jersey.test-framework</groupId>
<artifactId>jersey-test-framework-core</artifactId>
<version>2.25</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-grizzly2-servlet</artifactId>
<version>2.25</version>
</dependency>
Then, modify your test to provide a Grizzy servlet container:
#Override
protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
return (final URI baseUri, final DeploymentContext deploymentContext) ->
new TestContainer() {
private HttpServer server = null;
#Override
public ClientConfig getClientConfig() {
return null;
}
#Override
public URI getBaseUri() {
return baseUri;
}
#Override
public void start() {
try {
this.server = GrizzlyWebContainerFactory.create(baseUri, Collections
.singletonMap("jersey.config.server.provider.packages", "<your-package-name>"));
} catch (final ProcessingException | IOException cause) {
throw new TestContainerException(cause);
}
}
#Override
public void stop() {
this.server.shutdownNow();
}
};
}
I assume that you are going to use this in multiple unit tests, so it may be wise to extend JerseyTest so this common configuration may be performed automatically. Additionally, it may be worth reviewing org.glassfish.jersey.test.grizzly.GrizzlyTestContainerFactory to see if there is any functionality provided by the test container that you wish to emulate/preserve. The example provided should be able to be dropped into your test to at least confirm this is a fix.
EDIT: In my own implementation, I required the ability to still supply a ResourceConfig when generating the server. I suspect that this is likely to be the common case for other Jersey Test Framework users. A working example of the proposed TestContainerFactory follows.
import java.io.IOException;
import java.net.URI;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.ServletContext;
import javax.ws.rs.ProcessingException;
import javax.ws.rs.core.UriBuilder;
import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.grizzly.servlet.WebappContext;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
import org.glassfish.jersey.test.DeploymentContext;
import org.glassfish.jersey.test.spi.TestContainer;
import org.glassfish.jersey.test.spi.TestContainerException;
import org.glassfish.jersey.test.spi.TestContainerFactory;
import org.glassfish.jersey.test.spi.TestHelper;
public class RestTestContainerFactory implements TestContainerFactory {
public static class RestTestContainer implements TestContainer {
private static final Logger LOGGER = Logger.getLogger(RestTestContainer.class.getName());
private URI baseUri = null;
private final HttpServer server;
public RestTestContainer(final URI baseUri, final DeploymentContext context) {
this.baseUri = UriBuilder.fromUri(baseUri).path(context.getContextPath()).build();
if(LOGGER.isLoggable(Level.INFO)) {
LOGGER.info("Creating RestRestContainer configured at the base URI "+TestHelper.zeroPortToAvailablePort(baseUri));
}
try {
final WebappContext webContext = new WebappContext("TestContext", context.getContextPath());
context.getResourceConfig()
.register(new AbstractBinder() {
#Override
protected void configure() {
bind(webContext).to(ServletContext.class);
}
});
this.server = GrizzlyHttpServerFactory.createHttpServer(this.baseUri, context.getResourceConfig(), false);
webContext.deploy(this.server);
} catch (final ProcessingException cause) {
throw new TestContainerException(cause);
}
}
#Override
public ClientConfig getClientConfig() {
return null;
}
#Override
public URI getBaseUri() {
return baseUri;
}
#Override
public void start() {
if(server.isStarted()) {
LOGGER.warning("Ignoring start request - RestTestContainer is already started");
} else {
LOGGER.fine("Starting RestTestContainer...");
try {
server.start();
if(baseUri.getPort() == 0) {
baseUri = UriBuilder.fromUri(baseUri)
.port(server.getListener("grizzly").getPort())
.build();
LOGGER.info("Started GrizzlyTestContainer at the base URI "+baseUri);
}
}
catch(final ProcessingException | IOException cause) {
throw new TestContainerException(cause);
}
}
}
#Override
public void stop() {
if(server.isStarted()) {
LOGGER.fine("Stopping RestTestContainer...");
server.shutdownNow();
} else {
LOGGER.warning("Ignoring stop request - RestTestContainer is already stopped");
}
}
}
#Override
public TestContainer create(final URI baseUri, final DeploymentContext context) {
return new RestTestContainer(baseUri,context);
}
}
Frustratingly, grizzly's GrizzlyWebContainerFactory will provide a servlet context, but not configure with a resource config. Inversely, GrizzlyHttpServerFactory will configure an application with a ResourceConfig, but will not provide a web context.
We can work around this by creating the WebappContext (extends ServletContext) manually, configuring it, and then injecting it into the resource config by means of an AbstractBinder.

Categories