I have 2 APIs (doGET and doPOST) and I am trying to use an async mechanism to make doPOST write httpServletResponse for doGET request.
My control flow -
Client make a requestA (getData) call
Java server does some processing and makes a call to out of environment 3rd party API
3rd party API does not return response but calls my another endpoint doPOST
doPOST now need to write an object of httpServletResponse into doGET
doGET returns this object as soon as doPOST is done.
To solve this problem, I figured out I can use some asynchronous programming mechanism like CompletableFuture in java. But I am confused about how to exactly set this mechanism in my code. Here is what I have done so far -
doGET
public void doGET(HttpServletRequest request, HttpServletResponse response) {
// some processing
// Call 3rd Party API
CompletableFuture<HttpServletRequest> completableFuture = CompletableFuture.supplyAsync(() -> doPOST());
while (!completableFuture.isDone()) {
System.out.println("CompletableFuture is not finished yet...");
}
HttpServletRequest result = completableFuture.get();
response = result;
}
I have not been able to figure out how can I set up completableFuture for this. Need help here.
doPOST
public HttpServletResponse doPOST(HttpServletRequest request, HttpServletResponse response) {
// receive 3rd party request
// add data from 3rd party request into a new response object
// add response object into hashmap
}
How can I properly make this work?
You may have a map of request ids vs. an object as property of your class exposing the two methods:
private final Map<String, HttpRequestResponse> requests = new HashMap<>();
... where the class HttpRequestResponse is a simple wrapper for a request (that you receive upon doGET) and a response (that will be supplied by doPOST):
class HttpRequestResponse {
private final HttpServletRequest request;
private final CompletableFuture<HttpServletResponse> responseSupplier;
public HttpRequestResponse(HttpServletRequest request, CompletableFuture<HttpServletResponse> responseSupplier) {
this.request = request;
this.responseSupplier = responseSupplier;
}
public void supplyResponse(HttpServletResponse response) {
this.responseSupplier.complete(response); //<-- this will release the .get()
}
//getters
public CompletableFuture<HttpServletResponse> getSupplier() {
return responseSupplier;
}
}
Upon receiving the request on doGET, you will create the instance and put it into the map, then wait for the result:
public void doGET(HttpServletRequest request, HttpServletResponse response) {
HttpRequestResponse responseSupplier = new HttpRequestResponse(request, new CompletableFuture<>());
requests.put(yourId, responseSupplier); //add supplier to the map (so that doPOST can retrieve it later)
//perform request to your 3rd party API
response = responseSupplier.getSupplier().get(); //<- wait until someone completes the future
}
On the other hand, upon receiving the response from the 3rd party API on doPOST, you will need to get the future by its id, remove it from the map and complete it:
public void doPOST(HttpServletRequest request, HttpServletResponse response) {
HttpRequestResponse responseSupplier = requests.remove(yourId); //<-- removes the supplier from the map and returns it to you
responseSupplier.getSupplier().complete(<your response>); //<-- once you complete the future with a result, the .get() which is hanging on doGET will return
}
Question: why do you want to use asynchronous pattern if the doGET waits anyway for the response to be ready before continuing?
I guess once you put this in place, the doGET can become asynchronous as well and return the execution id (which the client can then listen for a result).
Related
A client request is handled by doGET which calls a third-party API. This third-party API then does not send a response back to doGET but calls my other endpoint to be handled by doPOST. I now need to write a response from doPOST into doGET.
Required workflow is as follows -
The third-party API will callback doPOST
doPOST should be using Callable Task that executor service uses
Then I will have Future returned from executor service submit call as shown above
The doGET API will do a blocking .get() operation on this future
Once the reply from doPOST is received, doGET will return the same response back to the client
To make this happen, I am trying to setup executor and future mechanism as follows -
public HttpServletResponse doGET(Request jettyReq, HttpServletRequest request, HttpServletResponse response) throws ExecutionException {
ExecutorService executor = Executors.newFixedThreadPool(10);
// Placeholder for the value which will arrive in the future
Future<WriteDoGetResponse> future = executor.submit(new WriteDoGetResponse());
// perform some unrelated actions here
// check future status that gives me an actual response object
HttpServletResponse modifiedResponse = future.get();
// if hashmap has future for request-id
executor.shutdown();
return modifiedResponse;
}
public WriteDoGetResponse doPOST(Request jettyReq, HttpServletRequest request, HttpServletResponse response) {
}
I have the callable task in the other class for now but this is not something I am looking to solve.
static class WritedoGetResponse implements Callable<HttpServletResponse> {
#Override
public HttpServletResponse call() throws Exception {
return null;
}
But I need help with how do I make my doPOST api callable? I need a mechanism to solve this. Because executor.submit() takes an instance and I can not make doPOST implement callable. Any idea?
In an old version of graphql-java-servlet I used to extend SimpleGraphQLServlet and then override GraphQLContext createContext( Optional request, Optional response ) to add a cookie to the response. I would also override GraphQLErrorHandler getGraphQLErrorHandler() to do some custom error handling.
I'm now trying to do a large jump in version to graphql-java-servlet 6.x.
As of graphql-java-servlet 6.x, SimpleGraphQLServlet is gone. There now is a SimpleGraphQLHttpServlet, which I can't use directly.
Unfortunately, though, the github documentation is way out of date and still suggests using SimpleGraphQLServlet even though it is long gone. There are some builders and I can find some very simplistic references outside of the github documentation, but none of them cover my use cases.
I don't want to do anything fancy, but I need to be able to add a cookie to the response and do some custom error handling.
How can I do this in graphql-java-servlet 6.x? I can't seem to find any clarity on this.
There is GraphQLServletListener. It is also described in project docs, although it is a bit wrong (no OperationCallback in code).
Anyway, here is a working piece of code (used com.graphql-java-kickstart:graphql-java-servlet:6.1.4):
GraphQLSchema schema = getSchema();
List<GraphQLServletListener> listeners = new ArrayList<GraphQLServletListener>();
GraphQLServletListener lstnr = new GraphQLServletListener(){
public RequestCallback onRequest(HttpServletRequest request, HttpServletResponse response) {
System.out.println("onRequest:" + request.getRequestURI());
//TODO cookies here
response.addCookie(new Cookie("sample","test"));
return new RequestCallback() {
#Override
public void onSuccess(HttpServletRequest request, HttpServletResponse response) {
System.out.println("onSuccess:" + request.getRequestURI());
}
#Override
public void onError(HttpServletRequest request, HttpServletResponse response, Throwable throwable) {
//TODO add some error handling here
System.out.println("onError:" + request.getRequestURI());
}
#Override
public void onFinally(HttpServletRequest request, HttpServletResponse response) {
System.out.println("onFinally:" + request.getRequestURI());
}
};
}
};
listeners.add(lstnr);
SimpleGraphQLHttpServlet servlet = SimpleGraphQLHttpServlet.newBuilder(schema)
.withListeners(listeners)
.build();
Is it possible to get HttpServletRequest from the ServletContext?
Is it possible to get HttpServletRequest from the ServletContext?
No.
The ServletContext represents the application. The application can cover many sessions and requests. But you can't get the "currently running" request or session via the ServletContext. Detail on how servlets and scopes work can be found in this related answer: How do servlets work? Instantiation, sessions, shared variables and multithreading.
You're unfortunately not clear on the concrete functional requirement where you need this solution. You apparently have a ServletContext at hands somehow in an instance of the class of interest, but not a HttpServletRequest. It's hard to propose an answer showing the right way how to grab the HttpServletRequest in an instance of such class anyway. Decent MVC frameworks like JSF and Spring MVC have ways to grab the HttpServletRequest associated with the current thread in any class you want.
In case you're not using a MVC framework and thus can't use its facilities, then you can achieve this manually by storing the request (and response) as a ThreadLocal<T> in the current thread via a servlet filter.
Here's a kickoff example how such a thread local context class can look like:
public final class YourContext implements AutoCloseable {
private static ThreadLocal<YourContext> instance = new ThreadLocal<>();
private HttpServletRequest request;
private HttpServletResponse response;
private YourContext(HttpServletRequest request, HttpServletResponse response) {
this.request = request;
this.response = response;
}
public static YourContext create(HttpServletRequest request, HttpServletResponse response) {
YourContext context = new YourContext(request, response);
instance.set(context);
return context;
}
public static YourContext getCurrentInstance() {
return instance.get();
}
#Override
public void close() {
instance.remove();
}
// ... (add methods here which return/delegate the request/response).
}
You can create (and close!!) it in a servlet filter as below.
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
try (YourContext context = YourContext.create(request, response)) {
chain.doFilter(request, response);
}
}
Do note that closing is very important. Otherwise the thread will get polluted after it has done its job and will be recycled for a different request or even a completely different purpose. In case you aren't on Java 7 yet and thus can't use try-with-resources statement as above, then use a try-finally block.
Then, in any artifact which is invoked by the same thread/request (i.e. other filters, any servlets, any beans/classes (in)directly invoked by those artifacts, etc), you can obtain the HttpServletRequest associated with the current thread as below:
YourContext context = YourContext.getCurrentInstance();
HttpServletRequest request = context.getRequest();
// ...
Or, better create a delegate method, depending on whatever you'd like to do with the current request, such as obtaining the request locale:
YourContext context = YourContext.getCurrentInstance();
Locale requestLocale = context.getRequestLocale();
// ...
As a real world example, Java EE's MVC framework JSF offers exactly this possibility via FacesContext.
FacesContext context = FacesContext.getCurrentInstance();
Locale requestLocale = context.getExternalContext().getRequestLocale();
// ...
I used to have an Open Session In Conversation Filter based on cookies for a JSF 2 app. Now I want to build the same mechanism but technology-agnostic. Reusing some code, I have written this in a class that extends OncePerRequestFilter:
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
UUID conversationId = lookupConversationOrCreateIfNecessary(request,
response);
log.debug("Binding conversation '{}' to request '{}'", conversationId,
request);
bindConversation(conversationId, request);
try {
filterChain.doFilter(request, response);
} finally {
log.debug("Unbinding conversation '{}' from request '{}'",
conversationId, request);
unbindConversation(conversationId, request);
}
}
Now, when I reach bindConversation(conversationId, request) I just add a request attribute which points to the conversationId which is mapped to a Hibernate Session.
Anyways, in JSF I can access the current request by using FacesContext.getCurrentInstance().getExternalContext().getRequest() and implemented a CurrentSessionContext using this. But in plain servlets how can I access the current request programmatically?
Note: I have been reading the OncePerRequestFilter javadocs and I found this:
As of Servlet 3.0, a filter may be invoked as part of a REQUEST or
ASYNC dispatches that occur in separate threads. A filter can be
configured in web.xml whether it should be involved in async
dispatches. However, in some cases servlet containers assume different
default configuration. Therefore sub-classes can override the method
shouldNotFilterAsyncDispatch() to declare statically if they [sic] shouuld
indeed be invoked, once, during both types of dispatches in order to
provide thread initialization, logging, security, and so on. This
mechanism complements and does not replace the need to configure a
filter in web.xml with dispatcher types.
So, would it be dangerous to use a ThreadLocal to achieve what I want?
As you mention in your question: using a ThreadLocal seems a good option. I don't see why it would be unsafe as soon as you use your filter for both REQUEST and ASYNC.
EDIT
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
UUID conversationId = lookupConversationOrCreateIfNecessary(request,
response);
log.debug("Binding conversation '{}' to request '{}'", conversationId,
request);
ConversationHolder.setId(conversationId);
bindConversation(conversationId, request);
try {
filterChain.doFilter(request, response);
} finally {
log.debug("Unbinding conversation '{}' from request '{}'",
conversationId, request);
ConversationHolder.clear();
unbindConversation(conversationId, request);
}
}
#Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
return false; //to be sure both REQUEST and ASYNC are filtered
}
And the ConversationHolder
public class ConversationHolder extends ThreadLocal<UUID>{
private static ConversationHolder INSTANCE = new ConversationHolder();
public static void setId(UUID conversationId){
INSTANCE.set(conversationId);
}
public static UUID getId(){
return INSTANCE.get();
}
public static void clear(){
INSTANCE.remove();
}
}
Since conversationId is a local variable it won't be shared between request.
Since ConversationHolder is a ThreadLocal, the value you get from it during doFilter(...) will be correct. (except if you create new Thread by hand during your request processing, but it is not a recommended design)
I've got this issue, recently I read about the REST arquitecture and it makes a perfect sense, so I'd like to achieve a RESTful web application.
Now, I'm following the Front Controller pattern that means that all of the URL mappings go to the controller.java servlet, I map the by specific URLs, not by using the /* wildcard,
the controller implements the four HTTP methods POST,GET,PUT,DELETE, each method calls the controllers service method and there I determine based on the HttpServletRequest and pathInfo the action to execute.
Controller.java
#Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
IAction action;
View view;
try {
action = ActionFactory.produceAction(req);
view = action.execute(req, resp);
switch (view.getDispatchMethod()) {
case REDIRECT:
resp.sendRedirect(resp.encodeURL(view.getResource()));
break;
case FORWARD:
req.getRequestDispatcher(view.getResource()).forward(req, resp);
break;
case INCLUDE:
req.getRequestDispatcher(view.getResource()).include(req,resp);
break;
default:
}
} catch (ActionFailedException uae) {
req.setAttribute("ActionName", "Action");
req.setAttribute("FailCause", uae.getMessage());
req.getRequestDispatcher(VIEW_FAIL.getResource()).forward(req, resp);
}
}
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
this.service(req, resp);
}
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.service(req, resp);
}
#Override
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.service(req, resp);
}
#Override
protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.service(req, resp);
}
I've run into a particular issue when loading a specific order by the URI /orders/*, it is mapped to the controller servlet, the the action is executed and I load the appropriate order the action returns a View.java class
//ommited accessors and mutators for brevety.
public class View {
public enum DispatchMethod {
INCLUDE, FORWARD, REDIRECT
}
private DispatchMethod dispatchMethod;
private String resource;
public View(DispatchMethod dispatchMethod, String resource) {
this.dispatchMethod = dispatchMethod;
this.resource = resource;
}
}
Then the request is dispatched according to the getDispatchMethod() of the returned view.
Now, here is where the loop gets triggered, I use the following URL, myapp/orders/78965 /orders/* gets mapped to controller.java the appropriate action is executed and the correct order is found by the pathInfo() the returned view is new View(View.DispatchMethod.FORWARD,"order_details.jsp") the problem is that with the three available dispatch methods REDIRECT,FORWARD and INCLUDE a request is re-triggered on the URL and so on and on and on I never reach the order_details.jsp that renders the data.
So, how would you avoid the looping, as I'd like to preserve the URI displaying the order number I use the forward method, also, I'd like to do it using servlets, I've heard of the UrlRewriteFilter maybe in the future, but right now, how would it be done using "Plain Vanilla" since I'm using the Front Controller pattern, will it be necessary to add an additional servlet in the /orders/ URI ?
Any help or insights is truly appreciated.
EDIT 1:
Pasted the source code of the controller, a very basic one, I have my suspicions that the way the service method calls all of the overriden do[Method] of the servlet is triggering the loop and that it may be solved by splittig them.
Implementing a RESTful HTTP interface in Java is a lot easier using a JAX-RS implementation like RESTEasy or Jersey.
Using a Front Controller to dispatch requests to the right resource is a good approach, it's exactly the approach taken by these JAX-RS frameworks. I fear you may be re-inventing the wheel here by writing a bespoke URL parsing and dispatching mechanism when this can be taken off-the-shelf.
JAX-RS is a lightweight way to expose resources. By using a couple of simple annotations you can expose a REST interface without any plumbing required. For example:
public class Order {
#GET
#Path("/orders/{orderId}")
#Produces("text/html")
public void getOrder(#Context HttpServletResponse response,
#Context HttpServletRequest request,
#PathParam("orderId") String orderId) throws ServletException, IOException {
// ... create view and add to request here
request.getRequestDispatcher("orders.jsp").forward(request, response);
}
}
You can see how simple it is to attach this class to a URL path (using the #Path annotation), and how easily you can parse values from the URL using #PathParam. Since you get all the plumbing/dispatching/parsing off-the-shelf, you can concentrate on the bits of your app that are specific to your domain (such as what an order contains).