How to mock a ContainerRequestContext? - junit4

How could you mock the ContainterRequestContext, to receive the HttpMethod(POST, GET, PATCH) from the resource class, in addition to the User Session?
I declare in this way:
Resource.java
#POST
#Produces("application/json")
#RightsFilter
public Response create(JsonObject jsonObject,
#Context UriInfo uriInfo,
#Context final SecurityContext context) {
(code)
return Response.status(Status.OK)
.entity(entity)
.type(MediaType.APPLICATION_JSON).build();
}
RightsFilterTest.java
private ContainerRequestContext requestContext;
private SecurityContext securityContext;
#Before
public void setup(){
requestContext = mock(ContainerRequestContext.class);
securityContext = mock(SecurityContext.class);
}
#Test
public void abort() throws Exception{
when(requestContext.getMethod()).thenReturn(Resource.class.getMethod("POST")
.toString());
}
and I get as error:
org.mockito.exceptions.base.MockitoException:
Mockito cannot mock this class: interface
javax.ws.rs.container.ContainerRequestContext.
Mockito can only mock non-private & non-final classes.
If you're not sure why you're getting this error, please report to the
mailing list.
I've seen:
https://stackoverflow.com/questions/27279370/how-to-mock-a-securitycontext
Tools: Junit4.12, mockito-core-2.13.0
Does anyone have any why you can't mock a ContainterRequestContext?
Thanks in advance.

since ContainerRequestContext is mock, you need to mock its methods
#Inject
private MyFilter myFilter;
#Mock
private ContainerRequestContext containerRequestContext;
#Mock
private UriInfo uriInfo;
#Test
public void shouldFilter() {
given(containerRequestContext.getUriInfo()).willReturn(uriInfo);
given(uriInfo.getPath()).willReturn("/v1/my/url");
myFilter.filter(containerRequestContext);
}

Related

Hibernate Envers : How to inject SecurityContext (REST) in RevisionListener?

I have a REST API (using wildfly 20 with microprofile-jwt) so I would like to audit changes with Hibernate Envers. Unfortunately I can't get my Principal object : the javax.ws.rs.core.SecurityContext is null.
So my question is : how can I inject the SecurityContext in my RevisionListener and get the Principal ?
import java.security.Principal;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.SecurityContext;
import org.hibernate.envers.RevisionListener;
public class CustomRevisionListener implements RevisionListener {
#Context
private SecurityContext context;
#Override
public void newRevision(Object o) {
CustomRevEntity e = (CustomRevEntity) o;
e.setLogin(getUser());
}
private String getUser() {
if(context == null) return "anonymous no context";
Principal principal = context.getUserPrincipal();
return principal == null ? "anonymous" : principal.getName();
}
}
That will not work since the creation of the bean is done outside of the container. If you want to use envers, you would need a way to communicate the principle from beans that are managed by the container to the RevisionListener-bean that is created and managed by the envers-extension.
A way could be to use public static ThreadLocal<Principal> threadlocalPrincipal = new ThreadLocal<>();.
In a bean using JPA or Hibernate, #Context SecurityContext context should work. Before calling any Dao or any other Hibernate-Using method fill the public static Variable using threadlocalPrincipal.set(...). Inside the listener use <Classname of Bean>threadlocalPrincipal.get() to access the current variable set in the current thread.
Make sure to clear the Threadlocal variable afterwards using threadLocalPrincipal.remove() inside a finally block, to avoid memory leaks.
I received the answer from hibernate forum : https://discourse.hibernate.org/t/how-to-get-username-envers-api-rest/5592
To be able to inject the SecurityContext into the RevisionListner (with #Inject), you need this filter :
#Provider
public class SecurityRequestFilter implements ContainerRequestFilter, ContainerResponseFilter {
private static final ThreadLocal<SecurityContext> THREAD_LOCAL = new ThreadLocal<>();
#RequestScoped
#Produces
public SecurityContext getSecurityContext() {
return THREAD_LOCAL.get();
}
#Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
throws IOException {
THREAD_LOCAL.remove();
}
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
THREAD_LOCAL.set(requestContext.getSecurityContext());
}
}

Integration Tests for RESTEasy Endpoint

I want to perform integration tests on my REST endpoint but am running into issues.
Below is my endpoint. NOTE: I cannot change this part of the code.
#Path("/people")
public class PersonResource {
private final PersonService personService;
#Inject
public PersonResource(final PersonService personService) {
this.personService = personService;
}
#GET
#Produces("application/json")
public List<Person> getPersonList() {
return personService.getPersonList();
}
}
From what I've been able to find online, I have the following basic structure for my test.
public class PersonResourceTest {
private Dispatcher dispatcher;
private POJOResourceFactory factory;
#Before
public void setup() {
dispatcher = MockDispatcherFactory.createDispatcher();
factory = new POJOResourceFactory(PersonResource.class);
dispatcher.getRegistry().addResourceFactory(factory);
}
#Test
public void testEndpoint() throws URISyntaxException {
MockHttpRequest request = MockHttpRequest.get("people");
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
System.out.print("\n\n\n\n\n" + response.getStatus() + "\n\n\n\n\n");
System.out.print("\n\n\n\n\n" + response.getContentAsString() + "\n\n\n\n\n");
}
}
However, this results in the following error on the last line of the setup method.
java.lang.RuntimeException: RESTEASY003190: Could not find constructor for class: my.path.PersonResource
I explored the Registry API and thought maybe I should have been using addSingletonResource instead, so I changed the last line of setup to dispatcher.getRegistry().addSingletonResource(personResource); and added the following.
#Inject
private PersonResource personResource;
But that results in a NullPointerException on the last line of setup.
The sparse documentation on the mocking isn't very helpful. Can anyone point out where I'm going wrong? Thanks.
You need to do two things
Add a no arguments constructor to your source class:
public PersonResource() {
this(null)
}
In the test class, initialize the PersonResource class with an instance of PersonService class:
dispatcher.getRegistry().addSingletonResource(new PersonResource(new PersonService()));
If needed, the PersonService class can be mocked:
private Dispatcher dispatcher;
#Mock
private PersonService service;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
dispatcher = MockDispatcherFactory.createDispatcher();
PersonResource resource= new PersonResource(service);
ispatcher.getRegistry().addSingletonResource(resource);
}
Hope it helps!

Resteasy 3 #Context HttpServletRequest always null

We were using Resteasy 2 but we are upgrading to Resteasy 3 and the HttpServletRequest injection is always null.
Our modified security interceptor/filter that looks like:
#Provider
#ServerInterceptor
#Precedence("SECURITY")
public class SecurityInterceptor implements ContainerRequestFilter, ContainerResponseFilter {
#Context
private HttpServletRequest servletRequest;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
// Need access to "servletRequest" but it is always null
if (!isTokenValid(pmContext, method)) {
requestContext.abortWith(ACCESS_DENIED);
}
}
#Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
// post processing
}
}
And the application class looks like:
#ApplicationPath("/")
public class RestApplication extends Application {
private Set<Object> singletons = new HashSet<Object>();
private Set<Class<?>> empty = new HashSet<Class<?>>();
public RestApplication() {
// Interceptors
this.singletons.add(new SecurityInterceptor());
// Services
this.singletons.add(new MyService());
}
public Set<Class<?>> getClasses() {
return this.empty;
}
public Set<Object> getSingletons() {
return this.singletons;
}
}
Sample API:
#Path("/test")
public class MyService extends BaseService {
#Context HttpServletRequest servletRequest;
#GET
#Path("/hello")
#Produces(MediaType.APPLICATION_JSON)
public Response hello() {
// Need access to HttpServletRequest but it's null
return Response.ok("hello").build();
}
}
However, looking at this and this posts, I don't see HttpServletRequest injection provider.
This leads me to believe that I may need an additional plugin. This is what is installed:
jose-jwt
resteasy-atom-provider
resteasy-cdi
resteasy-crypto
resteasy-jackson2-provider
resteasy-jackson-provider
resteasy-jaxb-provider
resteasy-jaxrs
resteasy-jettison-provider
resteasy-jsapi
resteasy-json-p-provider
resteasy-multipart-provider
resteasy-spring
resteasy-validator-provider-11
resteasy-yaml-provider
Any ideas?
Based on #peeskillet suggestion, modifying to return new class instances instead of singletons resolved my issue.
Thus my modified javax.ws.rs.core.Application file looks like:
#ApplicationPath("/")
public class RestApplication extends Application {
private Set<Object> singletons = new HashSet<Object>();
private Set<Class<?>> classes = new HashSet<Class<?>>();
public RestApplication() {
// Interceptors
this.classes.add(SecurityInterceptor.class);
// Services
this.classes.add(MyService.class);
}
public Set<Class<?>> getClasses() {
return this.classes;
}
public Set<Object> getSingletons() {
return this.singletons;
}
}
You could use the SecurityInterceptor's constructor to get these values:
///...
private HttpServletRequest request;
private ServletContext context;
public SecurityInterceptor(#Context HttpServletRequest request, #Context ServletContext context) {
this.request = request;
this.context = context;
}
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
// The "servletRequest" won't be null anymore
if (!isTokenValid(pmContext, method)) {
requestContext.abortWith(ACCESS_DENIED);
}
}
///...
This'll solve your problem

JEE6 REST Service #AroundInvoke Interceptor is injecting a null HttpServletRequest object

I have an #AroundInvoke REST Web Service interceptor that I would like to use for logging common data such as the class and method, the remote IP address and the response time.
Getting the class and method name is simple using the InvocationContext, and the remote IP is available via the HttpServletRequest, as long as the Rest Service being intercepted includes a #Context HttpServletRequest in its parameter list.
However some REST methods do not have a HttpServletRequest in their parameters, and I can not figure out how to get a HttpServletRequest object in these cases.
For example, the following REST web service does not have the #Context HttpServletRequest parameter
#Inject
#Default
private MemberManager memberManager;
#POST
#Path("/add")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Member add(NewMember member) throws MemberInvalidException {
return memberManager.add(member);
}
I have tried injecting it directly into my Interceptor, but (on JBoss 6.1) it is always null...
public class RestLoggedInterceptorImpl implements Serializable {
#Context
HttpServletRequest req;
#AroundInvoke
public Object aroundInvoke(InvocationContext ic) throws Exception {
logger.info(req.getRemoteAddr()); // <- this throws NPE as req is always null
...
return ic.proceed();
I would like advice of a reliable way to access the HttpServletRequest object - or even just the Http Headers ... regardless of whether a REST service includes the parameter.
After researching the Interceptor Lifecycle in the Javadoc http://docs.oracle.com/javaee/6/api/javax/interceptor/package-summary.html I don't think its possible to access any servlet context information other than that in InvocationContext (which is defined by the parameters in the underlying REST definition.) This is because the Interceptor instance has the same lifecycle as the underlying bean, and the Servlet Request #Context must be injected into a method rather than the instance. However the Interceptor containing #AroundInvoke will not deploy if there is anything other than InvocationContext in the method signature; it does not accept additional #Context parameters.
So the only answer I can come up with to allow an Interceptor to obtain the HttpServletRequest is to modify the underlying REST method definitons to include a #Context HttpServletRequest parameter (and HttpServletResponse if required).
#Inject
#Default
private MemberManager memberManager;
#POST
#Path("/add")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Member add(NewMember member, #Context HttpServletRequest request, #Context HttpServletResponse response) throws MemberInvalidException {
...
}
The interceptor can then iterate through the parameters in the InvocationContext to obtain the HttpServletRequest
#AroundInvoke
public Object aroundInvoke(InvocationContext ic) throws Exception {
HttpServletRequest req = getHttpServletRequest(ic);
...
return ic.proceed();
}
private HttpServletRequest getHttpServletRequest(InvocationContext ic) {
for (Object parameter : ic.getParameters()) {
if (parameter instanceof HttpServletRequest) {
return (HttpServletRequest) parameter;
}
}
// ... handle no HttpRequest object.. e.g. log an error, throw an Exception or whatever
Another work around to avoid creating additional parameters in every REST method is creating a super class for all REST services that use that kind of interceptors:
public abstract class RestService {
#Context
private HttpServletRequest httpRequest;
// Add here any other #Context fields & associated getters
public HttpServletRequest getHttpRequest() {
return httpRequest;
}
}
So the original REST service can extend it without alter any method signature:
public class AddService extends RestService{
#POST
#Path("/add")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Member add(NewMember member) throws MemberInvalidException {
return memberManager.add(member);
}
...
}
And finally in the interceptor to recover the httpRequest:
public class RestLoggedInterceptorImpl implements Serializable {
#AroundInvoke
public Object aroundInvoke(InvocationContext ic) throws Exception {
// Recover the context field(s) from superclass:
HttpServletRequest req = ((RestService) ctx.getTarget()).getHttpRequest();
logger.info(req.getRemoteAddr()); // <- this will work now
...
return ic.proceed();
}
...
}
I'm using Glassfish 3.1.2.2 Jersey
For http header this works for me:
#Inject
#HeaderParam("Accept")
private String acceptHeader;
To get UriInfo you can do this:
#Inject
#Context
private UriInfo uriInfo;

ServletContext.log() not logging

Log output of my RemoteServiceServlet (GWT) is not shown in Logfiles or Stdout when using getServletContext().log("anything");
For dependency injection I use Google Guice. For my own log output I use slf4j-jdk14. I tried this in Tomcat 6 as well as in Jetty (GWT devmode).
To make it clear, my Servlet:
#Singleton
public class MyServiceServlet extends RemoteServiceServlet implements MyService {
private static final Logger log = LoggerFactory.getLogger(MyServiceServlet.class);
private final ADependency dep;
#Inject
public MyServiceServlet(ADependency dep) {
getServletContext().log("THIS IS NOT SHOWN IN MY LOGS");
log.error("THIS IS SHOWN IN MY LOGS");
this.dep = dep;
}
}
So, where can I find the missing log output or where can I configure the ServletContext-Log?
The ServletContext.log method behavior is container specific. The method I have used to make it consistent is to wrap the ServletConfig passed in through init() in order to create a wrapped ServletContext which uses our own provided logger (Slf4j in this case).
public class Slf4jServletConfigWrapper implements ServletConfig {
private final ServletConfig config;
private final Logger log;
public Slf4jServletConfigWrapper(Logger log, ServletConfig config) {
this.log = log;
this.config = config;
}
public ServletContext getServletContext() {
return new ServletContext() {
public void log(String message, Throwable throwable) {
log.info(message, throwable);
}
public void log(Exception exception, String msg) {
log.info(msg, exception);
}
public void log(String msg) {
log.info(msg);
}
...
Full Slf4jServletConfigWrapper.java code
In your Servlet override the init() method to use the ServletConfig wrapper
public void init(final ServletConfig config) throws ServletException {
super.init(new Slf4jServletConfigWrapper(log, config));
}