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

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;

Related

How to mock a ContainerRequestContext?

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);
}

Same QueryParams In All JAX-RS Endpoints

I have a requirement that a few QueryParams should be present in absolutely all JAX-RS endpoints of my application.
Is there a way to specify somewhere, only once, these parameters? Or do I have to repeat myself in all method endpoints?
Thank you!
I would implement a ContainerRequestFilter and handle the parameters there. You can add the result to the ContainerRequestContext:
#Provider
public class MyFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
Object result = // handle the parameter
requestContext.setProperty("myParam", result);
}
}
Your implementation will of course depend on your needs.
You can inject the context into your resource classes like:
#Context
private ContainerRequestContext containerRequestContext;
See also:
Jersey 2 filter uses Container Request Context in Client Request Filter

Spring boot how to get resource method in request filter

I am building a REST API with Spring Boot, and now trying to create a custom filter class where I need to access the resource method that would be invoked by the request. I need that in order to check if the method is annotated with a certain annotation, e.g.
#Component
#ApplicationScope
public class MyFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
Method method = // get the target method somehow
if (method.isAnnotationPresent(MyAnnotation.class)) {
// business logic here
}
filterChain.doFilter(request, response);
}
}
With RESTEasy I would do something like this
#Context
private ResourceInfo resourceInfo;
where ResourceInfo has methods to get the resource class and method that is the target of the request. Is there a similar class in Spring Boot that would do the same job?

Combining verbs in Apache CXF JAX-RS

We would usually define POST and PUT verbs as different service APIs.
#POST
#Path("/getbook")
#Produces({"application/xml","application/json"})
#Consumes({"application/xml","application/json","application/x-www-form-urlencoded"})
public Response getBucket() {
... }
#PUT
#Path("/getbook/{name}")
#Produces({"application/xml","application/json"})
#Consumes({"application/xml","application/json","application/x-www-form-urlencoded"})
public Response getBucket(#PathParam("name") String name) {
... }
Would there be a way to combine these verbs into a single method - and then drive different logic based on the type of the verb ?
Hypothetically
#POST
#PUT
#Path("/getbook/{name}")
#Produces({"application/xml","application/json"})
#Consumes({"application/xml","application/json","application/x-www-form-urlencoded"})
public Response getBucket(#PathParam("name") String name) {
if(verb=POST){
... }
else{
}
}
You may try like this using MessageContext. You need the context injected into the service method like below for updateCustomer method and then you can check for the method type as you like (here I am checking for PUT):
#Path("/customer")
public class CustomerService {
#Context
private org.apache.cxf.jaxrs.ext.MessageContext mc;
#PUT
public Response updateCustomer(#Context MessageContext context, Customer c) {
HttpServletRequest request = context.getHttpServletRequest();
boolean isPut = "PUT".equals(request.getMethod());
}
}

How to get the url of called method resteasy

I making one Rest Service with Restaeasy (java) that have to return the same URL that was called but with one new string
Example Call service:
Post => mybase/myservice/somewrite with some JSON
| Reponse => mybase/myservice/somewrite/123456
So i want to make the mybase/myservice/somewrite url with one generic logic, because if i put String returnURL="mybase/myservice/somewrite"; and i change for example the name of mybase the reponse will not be good
I want somthing like this
someLogicService(JSON);
id=getId();
URL=getContextCallURL();
return URL+\/+id;
But i dont know if this is possible to do it, and less how to do it
You could also inject an instance of type UriInfo using the annotation Context within your resource, as described below:
#Context
private UriInfo uriInfo;
#POST
#Path("/")
#Consumes(MediaType.APPLICATION_JSON)
public Response makeContact(Contact contact) {
String requestUri = uriInfo.getRequestUri();
(...)
}
Hope it helps you,
Thierry
I found the answer to my problem, i put inject with #context the httpRequest to my function and call absolutPath :
#POST
#Path("/")
#Consumes(MediaType.APPLICATION_JSON)
public Response makeContact(Contact contact, #Context HttpRequest request) {
return Response.ok().header("location", request.getUri().getAbsolutePath().getPath() + contactService.makeContact(contactJSON)).build();
}