camel - how to propagate a correlationId through producerTemplates - java

This post relates to this get exchange from within pojo without changing it's interface
and the solution to this problem was to use MDC.
here is the code to do so: (taken from camel test code)
given this interface for instance
public interface IEcho {
String echo(String param);
}
and configuration like so :
import org.apache.camel.CamelContext;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.DefaultCamelContext;
import org.junit.Test;
import org.slf4j.MDC;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import static org.junit.Assert.assertEquals;
public class RetrieveCorrelationIdTest {
#Test
public void testCamel() throws Exception {
DefaultCamelContext ctx = new DefaultCamelContext();
try {
ctx.setUseMDCLogging(true);
ctx.addRoutes(createRouteBuilder());
ctx.start();
Object body = ctx.createProducerTemplate().requestBody("direct:a", "text in body");
assertEquals("TEXT IN BODY", body);
} finally {
ctx.stop();
}
}
protected RouteBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
IEcho proxy = (IEcho) ReflectHelper.simpleProxy(IEcho.class, new Proxybean(getContext()));
from("direct:a").routeId("route-a")
.setHeader("CUSTOM-CORRELATION-ID", constant("correlationIdsetInHeader"))
.process(exchange -> MDC.put("CUSTOM-HEADER-MDC", "correlationIdsetWithMDC"))
.bean(proxy, "echo(${body})");
from("direct:b").routeId("route-b")
.process(exchange -> {
String customHeader = (String) exchange.getIn().getHeader("CUSTOM-CORRELATION-ID");
String mdcHeader = MDC.get("CUSTOM-HEADER-MDC");
assertEquals(customHeader, mdcHeader);
exchange.getIn().setBody(((String)exchange.getIn().getBody()).toUpperCase());
})
.to("mock:result");
}
};
}
class Proxybean implements InvocationHandler {
CamelContext ctx;
Proxybean(CamelContext ctx) {
this.ctx = ctx;
}
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("echo")) {
// how to get the CUSTOM-CORRELATION-ID here ????µ
// only possible with MDC ?
String mdcHeader = MDC.get("CUSTOME-HEADER-MDC");
String result =
(String) ctx.createProducerTemplate()
.requestBodyAndHeader("direct:b", args[0], "CUSTOME-HEADER", mdcHeader);
return result;
}
return null;
}
}
}
see comments in code:
// how to get the CUSTOM-CORRELATION-ID here ????µ
// only possible with MDC ?
I can access some data even without the exchange using MDC
so my questions:
if I want to get a correlationId spanning from an endpoint calling beans method that calls other method exposed through a proxy that uses a producerTemplate to make the call etc etc...
Is that possible without MDC, does camel offers another way to retrieve this information?

Related

JUnit Test with Mockito - How to Mock javax.ws.rs.client.ClientRequestContext?

I have a class that implements javax.ws.rs.client.ClientRequestFilter:
public class CustomFilter implements ClientRequestFilter {
#Override
public void filter(ClientRequestContext context) throws IOException {
URI newUri = ... //replace a new uri
context.setUri(URI.create(newUri));
if (context.getMethod == "POST") {
context.setMethod("GET");
context.getHeaders().putSingle("ID","some string");
}
}
What I want is somehow to mock the ClientRequestContext. I want to compare that after calling the filter() function:
The new uri is set correctly.
The new http method is set correctly.
A new header "ID" is set with "some string" for the context.
As I tried to figure out, I can only mock the getter methods, and I do not know how to mock ClientRequestContext properly and use my CustomerFilter class to call the real function filter() to change values of the ClientRequestContext object since it is an interface. Could you help me to achieve the 3 requirements?
The class ClientRequestFilter is an interface, so you can mock it either using the static Mockito.mock method or annotating the field as #Mock in the test. So, if you want to check if the setUri method is called, you should do the following in your test method:
CustomFilter customFilter = new CustomFilter();
customFilter.filter(context);
Mockito.verify(context, Mockito.once()).setUri(ArgumentMatchers.any(URI.class));
For older Mockito versions:
CustomFilter customFilter = new CustomFilter();
customFilter.filter(context);
Mockito.verify(context, Mockito.once()).setUri(Matchers.any());
You don't have to verify that the underlying implementation is working. Since you are using an interface you will trust that the implementation that you will have at runtime is correct, because it is not necessary to test you dependencies. You simply have to be sure that the code you wrote is working and is forwarding requests to other classes.
In similar way you can test the other requirement:
Mockito.when(context.getMethod()).thenReturn("POST");
MultivaluedMap headers = Mockito.mock(MultivaluedMap.class);
Mockito.when(context.getHeaders()).thenReturn(headers);
CustomFilter customFilter = new CustomFilter();
customFilter.filter(context);
Mockito.verify(context, Mockito.once()).setUri(Matchers.any());
Mockito.verify(context, Mockito.once()).setMethod(Matchers.any());
Mockito.verify(context, Mockito.once()).getHeaders();
You can use argument mockito matchers and/or argument captors. Or you cat write a stub for request context and spy on it:
package test;
import org.junit.Test;
import javax.ws.rs.client.ClientRequestContext;
import javax.ws.rs.client.ClientRequestFilter;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;
import java.io.IOException;
import java.net.URI;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.CoreMatchers.*;
import static org.mockito.Mockito.*;
public class ClientRequestContextTest {
abstract static class ClientRequestContextStub implements ClientRequestContext {
MultivaluedMap<String, Object> headers = new MultivaluedHashMap<>();
URI uri = null;
String method = null;
ClientRequestContextStub(){}
#Override public String getMethod() { return method; }
#Override public void setMethod(String method) { this.method = method; }
#Override public URI getUri() { return uri; }
#Override public void setUri(URI uri) { this.uri = uri; }
#Override public MultivaluedMap<String, Object> getHeaders() { return headers; }
}
static class CustomFilter implements ClientRequestFilter {
private String newUri = null;
CustomFilter(String newUri) { this.newUri = newUri; }
#Override
public void filter(ClientRequestContext context) throws IOException {
context.setUri(URI.create(newUri));
if (context.getMethod().equals("POST")) {
context.setMethod("GET");
context.getHeaders().putSingle("ID", "some string");
}
}
}
#Test
public void checkCustomFilter() throws IOException {
URI newUriValue = URI.create("https://user:password#localhost:12345/suffix");
ClientRequestContext context = spy(ClientRequestContextStub.class);
context.setUri(URI.create("localhost:8080"));
context.setMethod("POST");
assertThat(context.getMethod(), equalTo("POST"));
assertThat(context.getUri().toString(), equalTo("localhost:8080"));
assertThat(context.getHeaders().size(), equalTo(0));
new CustomFilter(newUriValue.toString()).filter(context);
assertThat(context.getMethod(), equalTo("GET"));
assertThat(context.getUri(), equalTo(newUriValue));
assertThat(context.getHeaders().size(), equalTo(1));
assertThat(context.getHeaders().getFirst("ID").toString(), is("some string"));
}
}

How to test java.net.http (Java 11) requests BodyPublisher?

I'm trying to test my code which uses the new Java 11 java.net.http.HttpClient.
In my production code I have something like this:
HttpClient httpClient = ... (gets injected)
HttpRequest request = HttpRequest.newBuilder().uri(URI.create("http://localhost:1234"))
.POST(HttpRequest.BodyPublishers.ofByteArray("example".getBytes()))
.build();
return httpClient.send(request, HttpResponse.BodyHandlers.ofByteArray());
And in my test I mock the HttpClient and so get the java.net.http.HttpRequest. How do I get/test its request body (= my "example")? I can call request.bodyPublisher() to get a HttpRequest.BodyPublisher, but then I'm stuck.
I've tried to cast it to jdk.internal.net.http.RequestPublishers.ByteArrayPublisher (which it actually is), but it won't compile because the corresponding package is not exported by the module.
I've checked the available methods in the HttpRequest.BodyPublisher-interface (.contentLength(), .subscribe(subscriber)) but I guess it's not possible with them.
I've tried to just create a new BodyPublisher and compare them using .equals(), but there is no real implementation of it and so the comparison was always false.
If you are interested in how body will look like in handler you can know it with help of HttpRequest.BodyPublisher Subscriber. We call subscription.request in order to receive all body items and collect them.
Our custrom subscriber:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Flow;
public class FlowSubscriber<T> implements Flow.Subscriber<T> {
private final CountDownLatch latch = new CountDownLatch(1);
private List<T> bodyItems = new ArrayList<>();
public List<T> getBodyItems() {
try {
this.latch.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return bodyItems;
}
#Override
public void onSubscribe(Flow.Subscription subscription) {
//Retrieve all parts
subscription.request(Long.MAX_VALUE);
}
#Override
public void onNext(T item) {
this.bodyItems.add(item);
}
#Override
public void onError(Throwable throwable) {
this.latch.countDown();
}
#Override
public void onComplete() {
this.latch.countDown();
}
}
Usage in the test:
#Test
public void test() {
byte[] expected = "example".getBytes();
HttpRequest.BodyPublisher bodyPublisher =
HttpRequest.BodyPublishers.ofByteArray(expected);
FlowSubscriber<ByteBuffer> flowSubscriber = new FlowSubscriber<>();
bodyPublisher.subscribe(flowSubscriber);
byte[] actual = flowSubscriber.getBodyItems().get(0).array();
Assert.assertArrayEquals(expected, actual);
}

How to write a test case for Jersey Rest resouce + Grizzly+mockito

I am trying to write integration test case for Jersey Using Grizzly and Mockito, Spring, I am not able to mock the service Class. how can I mock the service class which is injected in my Resource class with #AutoWired
#AutoWired
MyFirstService myFirstServiceImpl;
#AutoWired
MySecondService mySecondServiceImpl;
#GET
#Path("/abc")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response getDetails(#QueryParam("xyz") String xyz,
#QueryParam("pqr") String pqr) {
Gson gson = new Gson();
Map<String, Object> someMap= new HashMap<String, Object>();
try {
map.put("a", myFirstServiceImpl.getSomeDetails(xyz);
map.put("b", mySecondService.getSomeMoreDetails(pqr);
} catch (Exception e) {
e.printStackTrace();
}
return Response.status(200).entity(gson.toJson(someMap)).build();
}
Test Class:
#Mock
private static MySecondService mySecondServiceImpl;;
#Mock
private static MyFirstService myFirstServiceImpl;
#Before
public void initMocks() {
resource = new MyResource();
MockitoAnnotations.initMocks(this);
resource.setMyFirstService (firstService);
resource.setSecondService(secondService);
}
#Override
protected TestContainerFactory getTestContainerFactory() {
return new GrizzlyWebTestContainerFactory();
}
private DeploymentContext getRestResourcesWithFilter() {
System.setProperty("jersey.config.test.container.port", "8104");
ServletDeploymentContext context =
ServletDeploymentContext
.forServlet(
new ServletContainer(new ResourceConfig(MyResource.class).property(
ServerProperties.RESPONSE_SET_STATUS_OVER_SEND_ERROR, "true")))
.addListener(ContextLoaderListener.class).addListener(RequestContextListener.class)
.initParam("contextConfigLocation", "classpath:applicationContext.xml")
.build();
return context;
#Test
public void test() throws Exception {
SomeOBject object= new SomeObject();
Object2 obj= new Object2();
when(myFirstServiceImpl.getSomeDetails(any(String.class))).thenReturn(object);
when(mySecondService.getSomeMoreDetails(pqr)).thenReturn(obj);
Response response = target("v1/abc").request().get();
}
This Test case is passing but the service class which I mocked are not mocking I am getting null pointer exception when ever code hits that line
So there are a couple problems
The #Before method. JerseyTest already implements a #Before method, where it creates the test container. So your mocks won't be created in time and the services will be null. Best thing to do is to just create the services in the configureDeployment() method, where you are initializing the Jersey application. A new container will be created for each test case, so you will have new mocks for each test.
You are simple passing the class to the ResourceConfig constructor, which will cause the Jersey runtime to create the instance of the resource class. So after you create the resource class, instead of new ResourceConfig(MyResource.class), do new ResourceConfig().register(resource).
So the configureDeployment() method should look more like
#Override
public DeploymentContext configureDeployment() {
resource = new MyResource();
MockitoAnnotations.initMocks(this);
resource.setMyFirstService(firstService);
resource.setSecondService(secondService);
ServletDeploymentContext context
= ServletDeploymentContext.forServlet(
new ServletContainer(new ResourceConfig().register(resource).property(
ServerProperties.RESPONSE_SET_STATUS_OVER_SEND_ERROR, "true")))
.build();
return context;
}
Another problem is that you are not actually passing any query parameters in the request. So in your resource method, the parameters will be null. Your request should look more like
target(...).queryParam(key1, value1).queryParam(key2, value2)
Here is a complete test
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;
import junit.framework.Assert;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.ServerProperties;
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.TestContainerFactory;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
public class MockitoTest extends JerseyTest {
public static interface Service {
String getMessage(String name);
}
#Mock
private Service service;
#Path("mock")
public static class MyResource {
private Service service;
public void setService(Service service) {
this.service = service;
}
#GET
public String get(#QueryParam("name") String name) {
return service.getMessage(name);
}
}
#Override
public DeploymentContext configureDeployment() {
MyResource resource = new MyResource();
MockitoAnnotations.initMocks(this);
resource.setService(service);
ServletDeploymentContext context
= ServletDeploymentContext.forServlet(
new ServletContainer(new ResourceConfig().register(resource).property(
ServerProperties.RESPONSE_SET_STATUS_OVER_SEND_ERROR, "true")))
.build();
return context;
}
#Override
protected TestContainerFactory getTestContainerFactory() {
return new GrizzlyWebTestContainerFactory();
}
#Test
public void doTest() {
Mockito.when(service.getMessage(Mockito.anyString())).thenAnswer(new Answer<String>(){
#Override
public String answer(InvocationOnMock invocation) throws Throwable {
return "Hello " + (String)invocation.getArguments()[0];
}
});
Response response = target("mock").queryParam("name", "peeskillet").request().get();
Assert.assertEquals(200, response.getStatus());
String message = response.readEntity(String.class);
Assert.assertEquals("Hello peeskillet", message);
System.out.println(message);
}
}

Jersey custom method parameter injection with inbuild injection

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 need some information so to parse that information I defined a custom filter like below
#java.lang.annotation.Target(ElementType.PARAMETER)
#java.lang.annotation.Retention(RetentionPolicy.RUNTIME)
public #interface TenantParam {
}
The tenant factory is defined below
public class TenantFactory implements Factory<Tenant> {
private final HttpServletRequest request;
private final ApiConfiguration apiConfiguration;
#Inject
public TenantFactory(HttpServletRequest request, #Named(ApiConfiguration.NAMED_BINDING) ApiConfiguration apiConfiguration) {
this.request = request;
this.apiConfiguration = apiConfiguration;
}
#Override
public Tenant provide() {
return null;
}
#Override
public void dispose(Tenant tenant) {
}
}
I haven't actually implemented the method but structure is above. There is also a TenantparamResolver
public class TenantParamResolver implements InjectionResolver<TenantParam> {
#Inject
#Named(InjectionResolver.SYSTEM_RESOLVER_NAME)
private InjectionResolver<Inject> systemInjectionResolver;
#Override
public Object resolve(Injectee injectee, ServiceHandle<?> serviceHandle) {
if(Tenant.class == injectee.getRequiredType()) {
return systemInjectionResolver.resolve(injectee, serviceHandle);
}
return null;
}
#Override
public boolean isConstructorParameterIndicator() {
return false;
}
#Override
public boolean isMethodParameterIndicator() {
return true;
}
}
Now in my resource method I am doing like below
#POST
#Timed
public ApiResponse create(User user, #TenantParam Tenant tenant) {
System.out.println("resource method invoked. calling service method");
System.out.println("service class" + this.service.getClass().toString());
//DatabaseResult<User> result = this.service.insert(user, tenant);
//return ApiResponse.buildWithPayload(new Payload<User>().addObjects(result.getResults()));
return null;
}
Here is how I am configuring the application
#Override
public void run(Configuration configuration, Environment environment) throws Exception {
// bind auth and token param annotations
environment.jersey().register(new AbstractBinder() {
#Override
protected void configure() {
bindFactory(TenantFactory.class).to(Tenant.class);
bind(TenantParamResolver.class)
.to(new TypeLiteral<InjectionResolver<TenantParam>>() {})
.in(Singleton.class);
}
});
}
The problem is during application start I am getting below error
WARNING: No injection source found for a parameter of type public void com.proretention.commons.auth.resources.Users.create(com.proretention.commons.api.core.Tenant,com.proretention.commons.auth.model.User) at index 0.
and there is very long stack error stack and description
Below is the declaration signature of user pojo
public class User extends com.company.models.Model {
No annotations on User class. Model is a class that defines only single property id of type long and also no annotations on model class
When I remove the User parameter from above create resource method it works fine and when I removed TenantParam it also works fine. The problem only occurs when I use both User and TenantParam
What I am missing here ? how to resolve this error ?
EDITED
I just tried with two custom method param injection, that is also not working
#POST
#Path("/login")
#Timed
public void validateUser(#AuthParam AuthToken token, #TenantParam Tenant tenant) {
}
What I am missing here ? Is this a restriction in jersey ?
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<Tenant>() {
#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<TenantParam> {
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<InjectionResolver<TenantParam>>(){})
.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
*
* <dependency>
* <groupId>org.glassfish.jersey.test-framework.providers</groupId>
* <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
* <version>2.22</version>
* <scope>test</scope>
* </dependency>
* <dependency>
* <groupId>org.glassfish.jersey.media</groupId>
* <artifactId>jersey-media-json-jackson</artifactId>
* <version>2.22</version>
* <scope>test</scope>
* </dependency>
*
* #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<Tenant>() {
// 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<TenantParam> {
public TenantParamInjectionResolver() {
super(TenantValueProvider.class);
}
}
public static class Binder extends AbstractBinder {
#Override
public void configure() {
bind(TenantParamInjectionResolver.class)
.to(new TypeLiteral<InjectionResolver<TenantParam>>(){})
.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:
Jersey 2.x Custom Injection Annotation With Attributes
My Comment in the Dropwizard issue: "No injection source found for a parameter"

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