I recently learned that with JAX-RS 2.0 long running service endpoints can make use of the #Suspended annotation and AsyncResponse to free resources for incoming requests while the actual work is done in the background. All client examples - at least the ones I found so far - are either calling such endpoints directly (plain http-call) or make use of the JAX-RS client API. However I was not able to figure out how to use this with the proxy-based API.
Given a REST endpoint that uses #Suspended:
public interface HeavyLiftingService {
#GET
#Path("/heavylifting")
public void heavyLifting(#Suspended final AsyncResponse aResponse);
}
its implementation using Spring:
#Component
public class HeavyLiftingServiceImpl implements HeavyLiftingService {
#Override
#Async
public void heavyLifting(#Suspended final AsyncResponse aResponse) {
final Result result = doHeavyLifting();
aResponse.resume(result);
}
}
And a proxy-based client, that wants to obtain the result:
HeavyLiftingService proxy = JAXRSClientFactory.create("https://some-server.xyz", HeavyLiftingService.class);
proxy.heavyLifting(null); // what to put in here?
Result result = null; // how can I get the result?
Obviously there are two problems:
What do I need to provide to the heavyLifting method as value for the AsyncResponse parameter?
How can I get the result as the return type of methods using #Suspended has to be void?
And another question is how exceptions in the service method are handled. Will an exception automatically resume the response and return a corresponding error status?
Related
I have recently started looking into Spring Integration as a result of this question and have a question about object-type based routing.
The application I am maintaining needs to process requests from an incoming ActiveMQ queue (request.queue) and send a response back to the caller on a topic (response.topic). At present the requests are structured as follows:
public abstract class Request {
// base class
}
public abstract class CustomerRequest extends Request {
// base class for customer-specific requests
}
public class FindCustomerByIdRequest extends CustomerRequest {
private int id;
}
public class FindAllCustomersRequest extends CustomerRequest {
private boolean includeArchivedCustomers;
}
public class AddCustomerRequest extends CustomerRequest {
private String name;
private Date signupDate;
private Address address;
}
I have a service for each high level domain object which provides the functionality to service these incoming requests:
#Service
public class CustomerService {
public CustomerResponse findCustomerById(FindCustomerByIdRequest request) {
// code snipped
return customerResponse;
}
public AddCustomerResponse addCustomer(AddCustomerRequest request) {
// code snipped
return addCustomerResponse;
}
}
I need to route each specific request to the approriate method in CustomerService via #ServiceActivator which I understand can be done by creating a separate channel for each request and implementing a PayloadTypeRouter to place requests on the correct channel based on type.
Over time the list of request types is going to grow, and I am questioning whether a one-channel-per-request setup is efficient/practical/scalable. For example, if there are 100 different request types in the future there are going to be 100 different channels.
What would be great is if I could route the high-level requests of superclass CustomerRequest to CustomerService and have Spring work out the approriate method to call via an annotation or some other mechanism. Does anyone know if this is possible, or have any comments regarding the many-channels approach?
If there is no ambiguity, use <service-activator ... reg="fooBean" /> (no method) and the framework will chose the target method based on the payload.
If there is ambiguity (more than one method for the same type), it will fail.
However, a single class with 100+ methods is probably not a good design.
It seems to be that your request types are app feature specific. This suggest that you have one queue for all the possible application features. That is horrible idea. You should have at least separate queue per feature.
I suggest to rethink the design of your app.
I am working on a project that works in two flavors with and without multi tenancy.
The project exposes a REST service which I would like to be asynchronous.
So my basic service looks like
#Component
#Path("/resouce")
#Consumes(MediaType.APPLICATION_JSON)
public class ResouceEndpoint {
#POST
#ManagedAsync
public void add(final Event event, #Suspended final AsyncResponse asyncResponse) {
resouce.insert (event);
asyncResponse.resume( Response.status(Response.Status.NO_CONTENT).build());
}
}
That works fine without multi tenancy and I get the benefits of the internal Jersey executor service for free. See #ManagedAsync
When I switch to multi tenancy I add a filter on the request that resolve the tenant id and place it on the thread local (in our case the HTTP thread).
When the processing chain hits the "add()" method above the current thread is the one provided by the Jersey executor service, so it does not include my tenant id.
I could think only on the following options to work around this issue.
Extend the ResouceEndpoint to MutliTenantResouceEndpoint and drop the #ManagedAsync
Using my own thread executor
public class MutliTenantResouceEndpoint extends ResouceEndpoint {
#POST
public void add(final Event event, #Suspended final AsyncResponse asyncResponse) {
final String tenantId = getTeantIdFromThreadLocal();
taskExecutor.submit(new Callable<Void>() {
#Override
public Void call() throws Exception {
setTeantIdToThreadLocal(tenantId);
browserEventsAnalyzer.insertEvent(event);
Response response = Response.status(Response.Status.NO_CONTENT).build();
asyncResponse.resume(response);
return null;
}
});
}
}
But this way I need to manage my own thread executor and it feel's like I am missing something here.
Any suggestion on a different approach?
Here are a handful of recommendations, in order.
For context, I've been using Jersey for 2 years, and faced this exact problem 18 months ago.
1. Stop using #ManagedAsync
If you have control over the http server that Jersey is running on, I would recommend you stop using #ManagedAsync.
Instead of setting up Jersey to return it's http handling thread immediately and offload real request work to a managed executor service thread, use something like Grizzly for your http server, and configure it to have a larger worker thread pool. This accomplishes the same thing, but pushes the async responsibility down a layer, below Jersey.
You'll run into many pain points over the course of a year if you use #ManagedAsync for any medium-to-large project. Here are some of them off the top of my head:
If any ContainerRequestFilter's hits an external service (e.g. an auth filter hits your security module, which hits the database) you will lose the benefits you thought you were gaining
If your DB chokes and that auth filter call takes 5 seconds, Jersey hasn't offloaded work to the async thread yet, so your main thread needed to receive a new conn is blocked
If you set up logback's MDC in a filter, and you want that context throughout your request, you'll need to set up the MDC again on the managed async thread
Resource methods are cryptic to new comers and ugly to read because:
they need an extra parameter
they return void, hiding their real response type
they can "return" anywhere, without any actual return statements
Swagger or other API doc tools cannot automatically document async resource endpoints
Guice or other DI frameworks may have trouble dealing with certain scope bindings and/or providers in async resource endpoints
2. Use #Context and ContainerRequest properties
This would involve involved calling requestContext.setProperty("tenant_id", tenantId) in your filter, then calling calling requestContext.getProperty("tenant_id") in your resource with a #Context injected request.
3. Use HK2 AOP instead of Jersey filters
This would involve setting up an HK2 binding of InterceptionService which has a MethodInterceptor that checks for managed async resource methods and manually executes all RequestScoped bound ContainerRequestFilters. Instead of your filters being registered with Jersey, you'd register them with HK2, to be run by the method interceptor.
I can add more detail and code samples to options 2/3 if you'd like, or give additional suggestions, but it would first be helpful to see more of your filter code, and I again suggest option 1 if possible.
I have a RESTful server implementation as well as a library for clients to make the calls, all using JAX-RS. The server components are divided up into interface FooResource and implementation FooResourceService.
In order for the client and server libraries to share RESTful path and other definitions, I wanted to split out the FooResource interface into its own project:
#Path(value = "foo")
public interface FooResource {
#GET
public Bar getBar(#PathParam(value = "{id}") int id) {
I want to set some headers in the response. One easy way to do this is to use #Context HttpServletResponse in the method signature:
public Bar getBar(#PathParam(value = "{id}") int id, #Context HttpServletResponse servletResponse) {
But the problem is that this exposes implementation details in the interface. More specifically, it suddenly requires my REST definition project (which is shared between the client and server library) to pull in the javax.servlet-api dependency---something the client has no need up (or desire for).
How can my RESTful resource service implementation set HTTP response headers without pulling in that dependency in the resource interface?
I saw one post recommending I inject the HttpServletResponse as a class member. But how would this work if my resource service implementation is a singleton? Does it use some sort of proxy with thread locals or something that figures out the correct servlet response even though the singleton class is used simultaneously by multiple threads? Are there any other solutions?
The correct answer seems to be to inject an HttpServletResponse in the member variable of the implementation, as I noted that another post had indicated.
#Context //injected response proxy supporting multiple threads
private HttpServletResponse servletResponse;
Even though peeskillet indicated that the semi-official list for Jersey doesn't list HttpServletResponse as one of the proxy-able types, when I traced through the code at least RESTEasy seems to be creating a proxy (org.jboss.resteasy.core.ContextParameterInjector$GenericDelegatingProxy#xxxxxxxx). So as far as I can tell, thread-safe injection of a singleton member variable seems to be occurring.
See also https://stackoverflow.com/a/10076327/421049 .
So injecting HttpServletResponse seems like a no go. Only certain proxy-able types are inject-able into singletons. I believe the complete list is as follows:
HttpHeaders, Request, UriInfo, SecurityContext
This is somewhat pointed out in the JAX-RS spec, but is explained more clearly in the Jersey reference guide
The exception exists for specific request objects which can injected even into constructor or class fields. For these objects the runtime will inject proxies which are able to simultaneously server more request. These request objects are HttpHeaders, Request, UriInfo, SecurityContext. These proxies can be injected using the #Context annotation.
SecurityContext may be Jersey specific, as it's not stated in the spec, but I'm not sure.
Now those types mentioned above don't really do much for you because they are all request contexts and nothing to set the response.
One Idea though is to use a javax.ws.rs.container.ContainerResponseFilter, along with the HttpHeaders to set a temporary request header. You can access that header through the ContainerRequestContext passed to the filter method. Then just set the response header through the ContainerResponseContext, also passed to the filter method. If the the header is not specific to the context of that resource method, then it's even easier. Just set the header in the filter.
But let's say the header is dependent on the execution of the resource method. Then you could do something like
#Singleton
#Path("/singleton")
public class SingletonResource {
#Context
javax.ws.rs.core.HttpHeaders headers;
#GET
public String getHello() {
String result = resultFromSomeCondition(new Object());
headers.getRequestHeaders().putSingle("X-HELLO", result);
return "Hello World";
}
private String resultFromSomeCondition(Object condition) {
return "World";
}
}
Then the ContainerResponseFilter might look something like this
#Provider
public class SingletonContainerResponseFilter
implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext crc,
ContainerResponseContext crc1) throws IOException {
String header = crc.getHeaderString("X-HELLO");
crc1.getHeaders().putSingle("X-HELLO", "World");
}
}
And just so only the singleton classes run through this filter, we can simply use a #NameBinding annotation
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.ws.rs.NameBinding;
#NameBinding
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
public #interface SingletonHeader {}
...
#SingletonHeader
public class SingletonResource {
...
#SingletonHeader
public class SingletonContainerResponseFilter
implements ContainerResponseFilter {
This is the only way I can think to handle this situation.
Resources:
Filters and Interceptors
Name Binding
#Path("/foo")
public interface FooResource {
#GET
#Path("{id}")
public Response getBar(#PathParam("id") int id) {
Bar bar = new Bar();
//Do some logic on bar
return Response.ok().entity(bar).header("header-name", "header-value").build()
}
}
Returns a JSON representation of the instance of bar with a status code 200 and header header-name with value header-value. It should look something along the lines of:
{
"bar-field": "bar-field-value",
"bar-field-2": "bar-field-2"
}
I am using Jersey Restful webservices. I have below web method to get the results.
#Path("/persons")
public class PersonWS {
private final static Logger logger = LoggerFactory.getLogger(PersonWS.class);
#Autowired
private PersonService personService;
#GET
#Path("/{id}")
#Produces({MediaType.APPLICATION_XML})
public Person fetchPerson(#PathParam("id") Integer id) {
return personService.fetchPerson(id);
}
#DELETE
#Path("/{id}")
public void deletePerson(#PathParam("id") Integer id) {
return personService.deletePerson(id);
}
}
In above Jersey RESTful webservice, i have two web methods one for get and one more for delete with same number of parameters. In above case will there be any ambiguity? If not what should be the URIs for both of the methods? Thanks!
Thanks!
Jersey decides which method to call based on the HTTP method specified in the request. If you use multiple methods with the same HTTP method like GET, then the choice is made by more Annotations like Consumes or Produces etc.
BTW: If you use the URI /persons/{id} for all endpoints, then you can annotate your class with #Path("/persons/{id}") instead of annotating every method with this sub-URI.
There is no ambiguity as the HTTP Method is different (GET vs DELETE).
The same url would also be used to update the object, using the HTTP method PUT
No ambiguity since the HTTP methods used are different i.e GET and DELETE
And the urls will be same as the param required is "id" for both
IN Jersey client program use GET http method for fetching person info, Use DELETE http method for deleting the person.
I have an http endpoint redirecting to a REST Java web service.
I am receiving a application/x-www-form-urlencoded request with some attributes embedded within the body of the request.
Inside the web service I would like to update the mule message status with those attributes.
Since RequestContext.getEventContext() is now deprecated and Doc says to implement Callable instead, however seems not working to me.The onCall method is never invoked.
Any idea ?
Below my code:
enter code here
#Path("/restClass")
public class HelloREST implements Callable{
private String industry;
private String lob;
private String nuixlegalentity;
private org.apache.log4j.Logger log = Logger.getLogger(LoadClass.class);
#POST
#Path("/setPayload")
#Consumes("application/x-www-form-urlencoded")
public void setMessage(#FormParam("industry") String industryParam, #FormParam("lob") String lobParam,#FormParam("nuixlegalentity") String nuixlegalentityParam){
log.debug("**************INSIDE SETMESSAGE******************");
industry=industryParam;
lob=lobParam;
nuixlegalentity=nuixlegalentityParam;
}
#Override
public Object onCall(MuleEventContext eventContext) throws Exception{
log.debug("**************INSIDE ONCALL******************");
eventContext.getSession().setProperty("industry","industry");
eventContext.getSession().setProperty("lob",lob);
eventContext.getSession().setProperty("nuixlegalentity",nuixlegalentity);
return eventContext.getMessage();
}
}
}
I assume you're using this class as a resource with the Jersey transport. In that case, Mule will call the JAX-RS annotated methods, based on the incoming request, and thus will not call onCall. Therefore implementing Callable is of no use.
Using RequestContext.getEventContext() is the only way possible to get the EventContext in a Jersey-handled resource.
To this date, MuleSoft hasn't provided a workable replacement for cases like this one so, even if RequestContext.getEventContext() is deprecated, you unfortunately have no other choice than using it.