How to embed request level objects to #Context - java

In embedded Jersey I can register a Binder to put in some resources that I can eventually access using #Context
However, those things I put in are more global and not on a per request level. I do know I can do it with some property mapping, but I would rather do it through #Context with class like Response foo(#Context HttpServletRequest)
I tried the setRequestScopedInitializer() but it does not put them in as expected and following their example with the Ref gives me a null pointer exception
Is there any way of doing this?

Here's how I eventually did it, but I don't like that I used a named property
RoutingContext was the type I wanted to inject
public class RoutingContextFactory implements
Supplier<RoutingContext> {
#Inject
private ContainerRequest request;
#Override
public RoutingContext get() {
return (RoutingContext) request.getProperty(RoutingContext.class.getName());
}
}
My binder
public class MyBinder extends AbstractBinder {
#Override
protected void configure() {
bindFactory(RoutingContextFactory.class)
.to(RoutingContext.class)
.proxy(true)
.proxyForSameScope(false)
.in(RequestScoped.class);
}
}
Initialized by
final ResourceConfig resourceConfig = ResourceConfig.forApplicationClass(applicationClass);
resourceConfig.register(new MyBinder());
Loaded by
final ContainerRequest request = new ContainerRequest(...
request.setProperty(RoutingContext.class.getName(), routingContext);
Used by
#GET
#Produces(MediaType.TEXT_PLAIN)
public String hello(
#Context final RoutingContext routingContext) {
return "Hello"
+ routingContext;
}
I still wish there was a way for me to just go request.register(routingContext). I opened up https://github.com/jersey/jersey/issues/3682 for this.

Related

ClientRequestFilter get PathParameter

I have a JAX-RS rest client with following definition.
#PUT
#Path("/payments/{paymentId}/operation")
void call(#PathParam("paymentId") String paymentId, Object request);
I would like to intercept this outgoing request and read the paymentId value in a ClientRequestFilter.
#Provider
public class TracingInterceptor implements ClientRequestFilter {
public static final String PAYMENT_ID = "paymentId";
#Context
UriInfo info;
#Override
public void filter(ClientRequestContext requestContext) {
// read paymentId
}
}
I tried with UriInfo which works for ContainerRequestFilter but the context is not available in a ClientRequestFilter.
How can I read a specific path parameter in a ClientRequestFilter?
Use ClientRequestContext#getUri: https://docs.oracle.com/javaee/7/api/javax/ws/rs/client/ClientRequestContext.html#getUri.

Creating and injecting a per request scoped variable

I would like to have a variable that follows along the full lifecycle of a request in java EE.
For example it could be for a logging function, so that I can filter all log entries by request.
The key part that I want to get at is that it must be relatively easy to implement in an already existing application so if possible some sort of dependency injection that gets the variable related to the specific request.
I've tried injectiong a #RequestScoped variable, but it doesn't work since it is only scoped to the container. I would need to be able to inject the same object to different containers. Is this at all possible?
EDIT: I want something along the lines of this:
#RequestScoped
public class RequestVariables {
public String id;
}
#Stateless
public class Logger {
#Inject
private RequestVariables requestVariables;
public void log(String message) {
System.out.println(requestVariables.id + ":" + message);
}
}
#Stateless
public class Service {
#Inject
private Logger logger;
#Inject
private RequestVariables requestVariables;
public void save(String data) {
logger.log("Save");
session.save(data + requestVariables.id); //Maybe add request parameter to save aswell
}
}
public class API {
#Inject
private Service service;
#Inject
private Logger logger;
#Inject
private RequestVariables requestVariables;
#Path("/1")
#GET
public Response get(#QueryParam("data") String data) {
requestVariables.id = UUID.randomUUID().toString()
service.save(data);
logger.log("Get");
return Response.status(204).build();
}
}
Currently this is what I have experimented with:
#RequestScoped
public class RequestScope {
private int test = 0;
public RequestScope(int test) {
this.test = test;
}
public RequestScope(){}
public int getTest() {
return test;
}
public void setTest(int test) {
this.test = test;
}
}
#Provider
public class RequestScopeFilter implements ContainerRequestFilter {
#Inject
private javax.inject.Provider<RequestScope> requestScopeProvider;
#Context
private HttpServletRequest request;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
requestScopeProvider.get().setTest(42);
request.setAttribute("test", "superTest");
}
}
#Stateless
#TransactionManagement(TransactionManagementType.BEAN)
#TransactionAttribute(value=TransactionAttributeType.NOT_SUPPORTED)
public class Service {
#Context
private HttpServletRequest httpServletRequest;
#Inject
private Provider<RequestScope> requestScopeProvider;
public void test() {
RequestScope scope = requestScopeProvider.get();
String test = (String)httpServletRequest.getAttribute("test");
}
}
So when I get the scope from my service then it is a new object with test set to 0, and then it throws an NPE since httpServletRequest is null
option #1
Implement an Interceptor and set the request id as HttpServletRequest attribute:
#AroundInvoke
public Object setRequestId(InvocationContext ic) throws Exception {
HttpServletRequest request = [..] // getHttpServletRequest(ic);
request.setAttribute("request-id", UUID.randomUUID().toString());
return ic.proceed();
}
Then use HttpServletRequest everywhere you need it
#Context
private HttpServletRequest httpRequest;
option #2
If want just to filter your logs by an unique id, you can configure your Logger to print the thread name: [%t]
Example: Log4j PatternLayout
option #3
Use a custom java bean to encapsulate the request data (query param, request id etc.) and pass this bean across your application services.
public class API {
#Inject
private Service service;
#Path("/1")
#GET
public Response get(MyCustomRequestBean data) {
service.doSomejob(data);
return Response.status(204).build();
}
}
Set the request id and query param in ParamConverter:
Jax-RS ParamConverter - ParamConverterProvider method return type mismatch
You can inject a provider in your service:
#Inject
Provider<RequestVariables> vars
And then call get () to get the instance. If you try to get () in a thread outside a request scope context you'll get an exception. I would however try to structure in a way that would not allow this to happen
A solution that I found is to use ThreadLocal variables. It seems rather dirty, but it works since each request is executed on it's own thread(as far as I am aware). So this is what I got:
public class RequestScope {
private static final ThreadLocal<String> id = ThreadLocal.withInitial(() -> UUID.randomUUID().toString());
public static String get() {
return id.get();
}
}
With that I can also easily exchange the ThreadLocal to return something more specific if so desired.
And I can get the variables from pretty much anywhere, assuming that the request is not starting a different thread

how to inject headers in a `#context HttpServletRequest`?

Let's say I have this code:
#ApplicationPath("...")
public class MyApp extends ResourceConfig {
public SalesLayerApplication() {
this.register(HeaderInjecterFilter.class);
this.register(Test.class);
}
}
#PreMatching
public class HeaderInjecterFilter implements ContainerRequestFilter {
#Override
public void filter(final ContainerRequestContext crc) throws IOException {
crc.getHeaders().add("foo", "bar");
}
}
#Path("/test")
public class Test {
#GET
#Produces(MediaType.TEXT_PLAIN)
public String dump(#Context final HttpServletRequest request) {
return request.getHeader("foo");
}
}
I was expecting to call the rest entry point /test and to retrieve the string bar.
But all I see is null
If I use #HeaderParam("foo") I correctly retrieve the variable, but I need to access throug the #Context HttpServletRequest.
Why would you expect that adding headers to the ContainerRequestContext would also add it to the HttpServletRequest? These are completely unrelated entities. Try injecting HttpHeaders or you can also inject the ContainerRequestContext directly.

How to mock a SecurityContext

Endpoints with Jersey.
I want to secure an endpoint with a ContainerRequestFilter
#Provider
#Secured
public class AuthorizationRequestFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
final SecurityContext securityContext =
requestContext.getSecurityContext();
//TODO: on logger here...
System.out.printf("Filtering %s request... AuthorizationRequestFilter\n", requestContext.getMethod());
requestContext.getHeaders().add("X-Secured-By", "Jersey >_<");
System.out.printf("SecurityContext: %s (%s).\n", securityContext, securityContext.getAuthenticationScheme());
if (securityContext == null || !securityContext.isUserInRole("privileged")) {
requestContext.abortWith(new UnauthorizedResponse().getResponse());
}
}
}
The annotation #Secured:
#NameBinding
#Retention(RetentionPolicy.RUNTIME)
public #interface Secured {}
So I can do this:
#Path("foobar")
public class FooResource {
//...
#Context
SecurityContext securityContext;
//...
#GET
#Secured
#Path(value = "foo")
#Produces(MediaType.APPLICATION_JSON)
public Response getFoo(#Context SecurityContext sc, #Context UriInfo ui, #Context HttpHeaders hh) {
// ...
}
//...
And I'm doing it right (I think), because with my test I don't even pass through the getFoo endpoint but is the ContainerRequestFilter that kicks me out. In fact I receive this (the "X-Secured-By" header is hand-made):
Headers: {X-Secured-By=[Jersey >_< kicked you out!], Content-Length=[97], Date=[Wed, 03 Dec 2014 17:46:50 GMT], Content-Type=[application/json], X-Powered-By=[Jersey ^_^]}
Response: InboundJaxrsResponse{ClientResponse{method=GET, uri=http://localhost:9998/urler/test, status=401, reason=Unauthorized}}
Now it would be nice to mock the SecurityContext.
This is what I'm doing... and if I'm here, it's obviously silly and/or wrong.
public class UrlerResourceTest extends JerseyTest {
//....
#Override
public TestContainerFactory getTestContainerFactory() {
GrizzlyTestContainerFactory grizzlyTestContainerFactory = new GrizzlyTestContainerFactory();
System.out.printf("The GrizzlyTestContainerFactory: %s ", grizzlyTestContainerFactory);
// just for debugging...
return grizzlyTestContainerFactory;
}
#Test
public void testSecuredEndpoint() throws JSONException {
SecurityContext securityContext = Mockito.mock(SecurityContext.class);
Mockito.when(securityContext.isUserInRole(anyString())).thenReturn(true);
Mockito.when(securityContext.getAuthenticationScheme()).thenReturn("Just Mocking...");
ReflectionTestUtils.setField(resource, "securityContext", securityContext, SecurityContext.class);
final Response response = target("foobar")
.path("foo")
.request(MediaType.APPLICATION_JSON)
.get();
System.out.println(getFormattedStringResponseInfo(response));
JSONObject entity = new JSONObject(response.readEntity(String.class));
assertTrue(entity.get("secured").equals(true));
assertTrue(response.getHeaders().containsKey("X-Secured-By"));
assertEquals(Status.OK.getStatusCode(), response.getStatus());
}
How can I mock the SecurityContext in my tests?
Thank you so much in advance.
Disclaimer: I'm not really a Mockito user, but from what I understand, mocking is used for situations where you have injected class dependencies (fields), and you mock those dependencies. In which case you still need to set the field with the mocked object. For example
public class TestClass {
TestService testService;
public void doTest() {
System.out.println(testService.getString());
}
public void setTestService(TestService testService) {
this.testService = testService;
}
}
public class TestService {
public String getString() {
return "Hello world";
}
}
#Test
public void toTest() {
TestService testService = Mockito.mock(TestService.class);
Mockito.when(testService.getString()).thenReturn("Hello Squirrel");
TestClass testClass = new TestClass();
testClass.setTestService(testService);
testClass.doTest();
}
You can see we are setting the the TestService in the TestClass with the mocked object. It's not greatest example, as we could simple instantiate TestService, but it shows, from my understanding, how the mocking should work.
That being said, I don't see how it is possible to do this with the AuthorizationRequestFilter, as it's handled by the test container, and we are not instantiating it for a unit test. Even if we were, it would seem intrusive (and redundant) to add a SecurityContext field.
So without a full integration test, where we are starting the server, and using the server's authentication capabilities, it will be difficult to handle the SecurityContext per this use case, as the SecurityContext is created by the container, taking information from the underlying servlet containers authentication mechanism.
One way you can achieve this though (which IMO doesn't seem very elegant - but works), without a full integration test, is to create a a filter which performs before your AuthorizationRequestFilter, and set the SecurityContext from there. Testing aside, this is actually pretty common in cases where we need to implement outr own custom authentication mechanism.
An example of how you might do this for your unit test, might be something like:
public class UrlerResourceTest extends JerseyTest {
...
#Override
public Application configure() {
return new ResourceConfig(FooResource.class)
.register(AuthorizationRequestFilter.class)
.register(AuthenticationFilter.class);
}
#Provider
#Priority(Priorities.AUTHENTICATION)
public static class AuthenticationFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
requestContext.setSecurityContext(new SecurityContext() {
#Override
public Principal getUserPrincipal() {
return new Principal() {
#Override
public String getName() {
return "Stackoverflow";
}
};
}
#Override
public boolean isUserInRole(String string) {
return "privileged".equals(string);
}
#Override
public boolean isSecure() { return true; }
#Override
public String getAuthenticationScheme() { return "BASIC"; }
});
}
}
...
}
This filter will perform before the AuthorizationRequestFilter because of the #Priority annotation. We've set it to Priorities.AUTHENTICATION which will before before any other filter without such annotation. (See Priorities API and Priorities with Jersey. Also the SecurityContext will be passed along between filters and also be injected into your resource class.
As I said, I don't think this is very elegant to have to create another filter, but it works for this purpose. Also I am not too familiar with the Jersey Test Framework, as I'm still beginning with it, but there are many configuration options for deployment within a servlet context. I don't know if we can configure the needed authentication mechanism for this case, but it might be something worth looking into.
Edit: In the beginning I explained about setting the field for the test object, but we can also pass the mocked object to a method. For example we could mock the ContainterRequestContext in the filter method, and call filter ourselves, passing the mocked ContainerRequestContext. But this is only useful when we are actually unit testing the filter class and instantiating it ourselves, which is not the case here.

Custom provider can't inject into filter

I have a POJO that I'd like to inject into resources and filters:
public final class MyObject { }
I implemented a custom provider for it:
#Provider
public final class MyProvider
extends AbstractHttpContextInjectable<MyObject>
implements InjectableProvider<Context, Type> {
#Context private HttpServletRequest request;
#Override
public Injectable<MyObject> getInjectable(
ComponentContext componentContext,
Context annotation,
Type type
) {
if (type.equals(MyObject.class)) {
return this;
}
return null;
}
#Override
public ComponentScope getScope() {
return ComponentScope.PerRequest;
}
#Override
public MyObject getValue(HttpContext httpContext) {
//in reality, use session info from injected request to create MyObject
return new MyObject();
}
}
The object is successfully injected into my resource:
#Path("/test")
#ResourceFilters(MyFilter.class)
public final class MyResource {
#Context private HttpServletRequest request;
#Context private MyObject myObject;
#GET
public String execute() {
System.out.println(request != null); //true
System.out.println(myObject != null); //true
return "data";
}
}
But Jersey fails to inject it into my filter:
public final class MyFilter implements ResourceFilter {
#Context private HttpServletRequest request;
#Context private MyObject myObject;
#Override
public ContainerRequestFilter getRequestFilter() {
return new ContainerRequestFilter() {
#Override
public ContainerRequest filter(ContainerRequest containerRequest) {
System.out.println(request != null); //true
System.out.println(myObject != null); //false
return containerRequest;
}
};
}
#Override
public ContainerResponseFilter getResponseFilter() {
return null;
}
}
I'm guessing the difference has to do with the fact that in MyFilter the injection is done using proxies that defer to thread-local instances - this is because the fields annotated with #Context are declared in the outer class, which is instantiated once, but they are used to inject objects on a per-request basis. When I step through filter during debugging, I can see that MyFilter.request points to a proxy wrapping an instance of com.sun.jersey.server.impl.container.servlet.ThreadLocalInvoker.
What is my custom provider (or implementation otherwise) missing that it needs to do custom injection into my filter?
Note that I'm currently stuck with Jersey 1.1.4.1 (sorry).
EDIT: Using Jersey 1.17, I get an exception at startup instead:
SEVERE: Missing dependency for field: private mypackage.MyObject mypackage.MyFilter.myObject
I found a workaround using the Providers injectable interface from JSR-311. First, I had to make my provider implement ContextResolver:
#Provider
public final class MyProvider
extends AbstractHttpContextInjectable<MyObject>
implements InjectableProvider<Context, Type>, ContextResolver<MyObject> {
...
#Override
public MyObject getContext(Class<?> type) {
//in reality, using the same logic as before
return new MyObject();
}
}
Then I injected a Providers instance into my filter instead. When filter is called, I use it to look up the ContextResolver for MyObject and retrieve it dynamically:
public final class MyFilter implements ResourceFilter {
#Context private HttpServletRequest request;
#Context private Providers providers;
#Override
public ContainerRequestFilter getRequestFilter() {
return new ContainerRequestFilter() {
#Override
public ContainerRequest filter(ContainerRequest containerRequest) {
final ContextResolver<MyObject> myObjectResolver =
providers.getContextResolver(MyObject.class, null);
final MyObject myObject =
myObjectResolver.getContext(MyObject.class);
System.out.println(request != null); //true
System.out.println(myObject != null); //true
return containerRequest;
}
};
}
...
}
Credit goes to this answer for tipping me off about Providers. The solution works, but it isn't a pretty one. I'd still like to inject MyObject anywhere and just have it work, like HttpServletRequest - and I'd like to know what it is my provider's missing that it needs to make that happen.
I came across this question trying to achieve the same goal (i.e. inject something provided by a custom Provider implementation) and found that custom injection works nicely if you go by way of a filter factory.
So instead of putting the #Context MyClass myObj annotation into the Filter class, place an annotated field of that type in the filter factory responsible for creating the filter and have the factory pass "myObj" as a regular parameter.
I'm not sure this will help in your case and I haven't investigated the implications of using this with a per-request provider (mine is singleton-scoped), so YMMV.

Categories