Access iriInfo in called Method - java

This code works to access the uriInfo:
#Path("/testing")
public class Testing {
#javax.ws.rs.core.Context UriInfo uriInfo;
#POST
#Path("/test2")
#Produces(MediaType.TEXT_PLAIN)
public Response test2(
#FormParam("sessionId") String sessionId ) {
String currentUserId = Utils.setup(sessionId);
String accessPath = uriInfo.getAbsolutePath().toASCIIString();
System.out.println("The client used this URI to reach this resource method: " + uriInfo.getAbsolutePath().toASCIIString());
// Utils.test3("print this");
return Response.ok("Test 2 ended").build();
}
When I try to access the uriInfo in the called method Utils.test3("print this"); Here:
public class Utils {
#javax.ws.rs.core.Context static UriInfo uriInfo;
public static String setup(String sessionId) {
if (!com.retailapppartners.Utils.validSession(sessionId)) {
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
}
String currentUserId = com.retailapppartners.Utils.getUserFromSession(sessionId);
MDC.put("user-id", currentUserId);
return currentUserId;
}
public static void test3(String message) {
System.out.println(message);
String path = uriInfo.getPath();
// System.out.println("The client used this URI: " + uriInfo.getAbsolutePath().toASCIIString());
return;
}
This fails with null pointer exception. I want to see the path uri in the called method to confirm security for all methods in my utils called method. I have searched hi and low for called examples of this. Thanks

Use the #Context annotation to inject an instance of UriInfo into an field variable or method parameter of your resource class
e.g. #1
public String find(#Context UriInfo uri){}
e.g. #2
public class RESTResource{
#Context
private UriInfo uri;
}

Continuing with my comment.. into an answer
Like I said, you can't just decide to inject it anywhere you want. The class being injected into needs to be managed by the JAX-RS runtime, as it's the one that will be doing the injecting. A resource class is managed, a filter provider is managed, that's why you can inject into them. You're utility class is not. And in any case, I don't think it would even work for a [static] "utility" class (even if you were to somehow get it managed) because of the static nature.
Let me just first mention, that UriInfo is specific to each request. static, by nature is "global". You cannot inject it as a static field.
One solution I can see is to make the Utils class (and methods) non-static, and use the underlying injection framework to inject an instance of the Utils class, where ever you need it. This way, if the Utils class is managed, then it should be able to inject the managed UriInfo instance. How this (getting the Utils class managed) will be achieved depends on the implementation you are using, and it's underlying injection framework.
For instance, with Jersey (2), I could do this
public class Utils {
#Context UriInfo uriInfo;
public String test(String s) {
return s + "=" +uriInfo.getAbsolutePath().toString();
}
}
#Path("some")
public class SomeResource {
#Inject
Utils utils;
#GET
public Response getSomething() {
return Response.ok(utils.test("Hello")).build();
}
}
public class JerseyApplication extends ResourceConfig {
public JerseyApplication() {
packages("stackoverflow.jersey.test");
register(new AbstractBinder(){
#Override
protected void configure() {
bind(Utils.class).to(Utils.class);
}
});
}
}
And this works just fine
C:\>curl -v http://localhost:8080/some
Result: Hello=http://localhost:8080/some
Jersey uses HK2 for its injection, so I am able to leverage it to injection of my Utils class.
Now this is probably not the answer you're looking for (as it defeats the purpose of a static utility class), but what you are trying too just can't be done. Either way you go, whether refactoring to pass the UriInfo to your static methods, or the solution above, you will probably have a lot of refactoring to do. I'm surprised you've already created 200 methods using this functionality, before making sure one worked :/

Related

How to wrap an annotation and conditionally applies it to a method

Say I have an annotation (#RequiresAccount) introduced in another library and I'm using it in my project, is there a way to conditionally apply it to a method, e.g. apply it when the customer is from website A and not apply when customer is from website B?
I've taken a look and the only possibility I've found was, creating a wrapper-Annotation:
#Aspect
#Component
public class RequiresAccountWrapperAspect {
#Autowired
private HttpServletRequest request;
private RequiresAccountAspect requiresAccountAspect = new RequiresAccountAspect();
#Around("#annotation(com.example.demo.components.RequiresAccountWrapper)")
public Object checkIfRequiresAccount(ProceedingJoinPoint joinPoint) throws Throwable {
String requestURL = request.getRequestURL().toString();
if (requestURL.startsWith("http://localhost")) {
requiresAccountAspect.checkAccount(joinPoint);
}
return joinPoint.proceed();
}
}
So everywhere you've used your RequiresAccount annotation, you can use this wrapper instead. For example:
#GetMapping("/test")
#RequiresAccountWrapper
public String h() {
return "test";
}
As you can see I'm creating a new instance of the aspect. I don't know if you have access to the Aspect-class itself but if you have you can then call the method in it and pass the joinPoint. To find the URL from the request you can inject the HttpServletRequest.

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

Grizzly REST - POSTs are always 404

In my endpoint, I have some methods with #GET and some methods with #POST. #GETs are working fine, but #POSTs always return 404.
Here is some part from the endpoint's interface:
public interface TestEndpoint {
#GET
#Path("/ping")
Response ping();
#POST
#Path("/weather/{iata}/{pointType}")
Response updateWeather(#PathParam("iata") String iataCode,
#PathParam("pointType") String pointType,
String datapointJson);
#POST
#Path("/airport/{iata}/{lat}/{long}")
Response addAirport(#PathParam("iata") String iata,
#PathParam("lat") String latString,
#PathParam("long") String longString);
#GET
#Path("/exit")
Response exit();
}
Here is the server initialization part:
public class TestServer {
private static final String BASE_URL = "http://localhost:9090/";
public static void main(String[] args) {
try {
final ResourceConfig resourceConfig = new ResourceConfig();
resourceConfig.register(TestEndpointImpl.class);
HttpServer server = GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URL), resourceConfig, false);
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
server.shutdownNow();
}));
HttpServerProbe probe = new HttpServerProbe.Adapter() {
public void onRequestReceiveEvent(HttpServerFilter filter, Connection connection, Request request) {
System.out.println(request.getRequestURI());
}
};
server.getServerConfiguration().getMonitoringConfig().getWebServerConfig().addProbes(probe);
server.start();
Thread.currentThread().join();
server.shutdown();
} catch (IOException | InterruptedException ex) {
Logger.getLogger(TestServer.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
where, TestEndpointImpl is an implementation of TestEndpoint (as the name implies) with class-level annotation #Path("/collect").
When I perform GET requests, it works fine. But POSTs are problematic. Corresponding methods are not called.
As a side note, probe prints both GET and POST requests as expected, so I am sure that requests reach the server and paths are ok.
Is there any suggestion?
EDIT: Some snippet from the implementation:
#Path("/collect")
public class TestEndpointImpl implements TestEndpoint {
...
#Override
public Response updateWeather(#PathParam("iata") String iataCode, #PathParam("pointType") String pointType,
String datapointJson) {
System.out.println("TRACE: " + datapointJson);
// do something and return a Response
}
...
}
The registered probe prints /collect/weather/BOS/wind, but updateWeather is not called.
Short answer
Copy the #POST and the #Path annotations to the method implementation. It will do the trick.
Long answer
The section regarding annotation inheritance of the JAX-RS 2.0 specification (the specification which Jersey is the reference implementation) is pretty clear. See the quote below:
3.6 Annotation Inheritance
JAX-RS annotations may be used on the methods and method parameters of a super-class or an implemented interface. Such annotations are inherited by a corresponding sub-class or implementation class method provided that the method and its parameters do not have any JAX-RS annotations of their own. Annotations on a super-class take precedence over those on an implemented interface. The precedence over conflicting annotations defined in multiple implemented interfaces is implementation specific. Note that inheritance of class or interface annotations is not supported.
If a subclass or implementation method has any JAX-RS annotations then all of the annotations on the superclass or interface method are ignored. E.g.:
public interface ReadOnlyAtomFeed {
#GET
#Produces("application/atom+xml")
Feed getFeed();
}
#Path("feed")
public class ActivityLog implements ReadOnlyAtomFeed {
public Feed getFeed() {...}
}
In the above, ActivityLog.getFeed inherits the #GET and #Produces annotations from the interface. Conversely:
#Path("feed")
public class ActivityLog implements ReadOnlyAtomFeed {
#Produces("application/atom+xml")
public Feed getFeed() {...}
}
In the above, the #GET annotation on ReadOnlyAtomFeed.getFeed is not inherited by ActivityLog.getFeed and it would require its own request method designator since it redefines the #Produces annotation.
For consistency with other Java EE specifications, it is recommended to always repeat annotations instead of relying on annotation inheritance.
That can also happen if the url is not in the correct format; for example you could have sent a request without the correct path parameters.

how to mock methods of my superclass using Mockito

I am facing issues in mocking the data for the methods which are being called without the reference e.g getMethod(); don't know how will mocking framework know about it. Below is the code for which am facing issue am not able to set HttpRequest and URIInfo in my code.
Is it possible to bypass the method.
Class A {
private HttpServletRequest httpRequest;
private UriInfo uriInfo;
public HttpServletRequest getReq() {
return httpRequest;
}
public void setReq(HttpServletRequest req) {
this.httpRequest = req;
}
public UriInfo getUriInfo() {
return uriInfo;
}
public void setUriInfo(UriInfo uriInfo) {
this.uriInfo = uriInfo;
}}
class B extends A {
// some code
}
class C extends B {
protected Object executeCall(Object beIn) throws Exception{
prepareUpdateConfigurationRequest();
// some other methods.
return "";
}
private void prepareUpdateConfigurationRequest() {
implPutCustomerProductOrderIdProductConfigurationsImpl.setReq(getReq());
implPutCustomerProductOrderIdProductConfigurationsImpl.setUriInfo(getUriInfo());
}}
// Test class using Mockito Framework
#RunWith(MockitoJUnitRunner.class)
public class CTest {
#Mock
private A a = Mockito.mock(A.class);
#InjectMocks
private C c = new C();
private ImplBackEndInput implBackEndInput;
#Test
public void testExecuteCallObject() {
implBackEndInput = new ImplBackEndInput();
UriInfo uriInfo = Mockito.mock(UriInfo.class);
Mockito.when(a.getUriInfo()).thenReturn(uriInfo);
Mockito.when(a.getReq()).thenReturn(httpServletRequest);
try {
c.executeCall(implBackEndInput);
} catch (Exception e) {
}
}
}
Protected or privates methods cannot be mocked using Mockito, I would suggest if you are using spring to you create a DummyC class in your test package, reference that as a parent in the springConfig and make it just return the object when is called. In that way the class will use that method as a by-pass to the real class that you don't need to test.
I'm not really sure how your code is compiling, given some static references to non-static methods - A.getUriInfo() - and various other errors. It also doesn't make a whole lot of sense that you're calling setters using getters for the same object:
implPutCustomerProductOrderIdProductConfigurationsImpl.setReq(getReq());
implPutCustomerProductOrderIdProductConfigurationsImpl.setUriInfo(getUriInfo());
But to answer your question for anyone else who might wind up here, you don't need to be mocking type A in the test class at all (here):
Mockito.when(A.getUriInfo()).thenReturn(uriInfo);
Mockito.when(A.getReq()).thenReturn(httpServletRequest);
You really don't need those two lines at all. In fact, you can remove the mock of A entirely (this line): #Mock private A a = Mockito.mock(A.class);
Instead, just do:
c.setUriInfo(uriInfo);
c.setReq(httpServletRequest);
This works because C extends A, meaning all of A's methods, when not overridden, are inherited by C. So if you call the non-overridden setter methods on the instance of C, it will go straight to the methods of A. After calling the set methods shown above, when you call c.getUriInfo(), it will return the object you passed in as a parameter to the setUriInfo(uriInfo); method. No need to mock at all here.

is it possible to call one jax-rs method from another?

suppose i have some jax-rs resource class:
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public class ResourceA {
#GET
public Something get(#Context UriInfo uriInfo) {
if (...) {
//how to get to ResourceB ?
}
}
}
and i want to conditionally redirect the call to some other jax-rs resource:
public class ResourceB {
#GET
#Path("{identifier}")
public Other get(#PathParam("identifier")String someArg) {
}
}
how do i do this?
note that i dont want this to be visible to the client (so no http redirects) and generally the resource methods i want to redirect to dont share the same signature (they may have path params etc as in the example i gave).
im running jersey 2.6 under apache tomcat (its a spring app, if thats any help)
EDIT - im looking for a jax-rs equivalent of servlet forward. i dont want to do an extra http hop or worry abour instantiating resource classes myself
You can get it using ResourceContext as follows:
#Context
ResourceContext resourceContext;
This will inject the ResourceContext into your Resource. You then get the resource you want using:
ResourceB b = resourceContext.getResource(ResourceB.class);
The Javadoc for ResourceContext is here. You can find a similar question here
I'm not aware of any possibility to do this from a resource method, but if it fits your use case, what you could do is implement your redirect logic in a pre matching request filter, for example like so:
#Provider
#PreMatching
public class RedirectFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) {
UriInfo uriInfo = requestContext.getUriInfo();
String prefix = "/redirect";
String path = uriInfo.getRequestUri().getPath();
if (path.startsWith(prefix)) {
String newPath = path.substring(prefix.length());
URI newRequestURI = uriInfo.getBaseUriBuilder().path(newPath).build();
requestContext.setRequestUri(newRequestURI);
}
}
}
This will redirect every request to /redirect/some/resource to /some/resource (or whatever you pass to requestContext.setRequestUri()) internally, before the resource method has been matched to the request and is executed and without http redirects or an additional internal http request.

Categories