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.
@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:
return
statements@Context
and ContainerRequest
propertiesThis 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.
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 ContainerRequestFilter
s. 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With