graphql-java-servlet's removal of SimpleGraphQLServlet - java

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();

Related

Use completableFuture to write doGET response from doPOST

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).

SlingServletFilter: Get status code before doFilter

I'm trying to make unexisting pages under my domain go to a 404 page. I need to distinguish 404 pages from the other pages. However, I do not know how to do this. And the thing below is not working.
#Component(service = Filter.class,
property = {
"service.ranking=" + Integer.MIN_VALUE})
#SlingServletFilter(scope = {SlingServletFilterScope.REQUEST},
pattern = "/content/foo/.*",
resourceTypes = "cq:Page",
extensions = {"html"},
methods = {"GET"})
public class NotFoundFilter implements Filter {
private static final String DEFAULT_METHOD = "GET";
#Reference
private UrlOperationsManager urlOperationsManager;
#Reference
private RequestResponseFactory requestResponseFactory;
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (!(request instanceof SlingHttpServletRequest) ||
!(response instanceof SlingHttpServletResponse)) {
chain.doFilter(request, response);
return;
}
SlingHttpServletResponse slingResponse = (SlingHttpServletResponse) response;
//this condition here is not working since slingResponse has no getStatusCode method.
if(slingResponse.getStatusCode() == 404) {
//do something
}
chain.doFilter(request, response);
}
#Override
public void destroy() {
}
}
You could work around this by implementing your own HttpServletResponseWrapper to save the value and access it later. The Sling implementation is marginally different (at least as far as this particular mechanic is concerned) from the generic Servlet API, which is covered in depth in How can I get the HTTP status code out of a ServletResponse in a ServletFilter?
However, if your intention is to serve a particular error document for a given status code, I'd approach it differently. Assuming you use a Dispatcher, you could have the web server take care of it.
The official AEM project archetype comes with a few simple examples that you could enable if you use Apache. The details will depend on your site structure but the gist is that it's possible to provide a similar configuration using the ErrorDocument directive to point to a cached error page relative to the document root, usually making it use content-editable error pages.
Some errors, especially HTTP 5** family could be a bit more tricky that way in that they usually happen when there's something wrong with AEM itself so it's prudent to make sure a fully static version is always available.

Access HttpServletRequest anywhere

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)

Differences between ServletResponse and HttpServletResponseWrapper?

I am new to servlet and reading some text about filters and wrappers. I can understand filters but got confused about wrappers. In the book, the author gives an example:
In case no wrapper:
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
String name = request.getParameter("name").trim();
try {
chain.doFilter(request, response);
PrintWriter out = response.getWriter();
if (name.length() == 0) {
out.println("Some message");
out.println("</body>");
out.println("</html>");
out.close();
}
} catch (Throwable t) {
}
}
In case of wrapper:
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
String name = request.getParameter("name").trim();
HttpServletResponse httpRes = (HttpServletResponse) response;
HttpServletResponseWrapper resWrapper = new HttpServletResponseWrapper(httpRes);
try {
chain.doFilter(request, response);
PrintWriter out = resWrapper.getWriter(); // why dont we just use response.getWriter();
if (name.length() == 0) {
out.println("<h3>Some message");
out.println("</body>");
out.println("</html>");
out.close();
}
} catch (Throwable t) {
}
}
Why we need HttpServletResponseWrapper while we can do the same thing with ServletResponse in case 1? Can anyone give me a clear example that we MUST use HttpServletResponseWrapper instead of ServletResponse? I have tried to google but found no luck.
BalusC's answer is good, but it might be a little overwhelming if you're just starting out.
Put simply: SerlvetResponse and its extension, HttpServletResponse, are interfaces telling you what methods are available to call to do the things you need. In the normal course of working with Filters, Servlets, et al., you'll use HttpServletResponse often to tell your app how to respond to requests.
HttpServletResponseWrapper is one particular implementation of HttpServletResponse which gives you a convenient way to wrap an existing response with some logic of your own without having to write a whole new implementation of the interface. It has a lot of methods, so this is really nice. As a trivial example, suppose you wanted to disallow calls to response.flushBuffer(). This code, using HttpServletResponseWrapper, will do that:
class DisallowFlushResponseWrapper extends HttpServletResponseWrapper {
public DisallowFlushResponseWrapper(HttpServletResponse response) {
super(response);
}
#Override
public void flushBuffer() {
throw new UnsupportedOperationException("Don't call this!");
}
}
The typical way to use such a wrapper would be to create a filter like this:
class DisallowFlushFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) {
if (response instanceof HttpServletResponse) {
HttpServletResponse newResponse =
new DisallowFlushResponseWrapper((HttpServletResponse) response);
chain.doFilter(request, newResponse);
}
...
}
...
}
Note that we wrap the response coming into the filter with an instance of our own wrapper. Then we hand the wrapper down to the next item in the filter chain. Thus anything that comes after this filter will get an exception if it calls flushBuffer() because it will be calling it on our wrapper. The wrapper, due to its default behavior, will delegate any other call to the wrapped response, which is the real one, so everything except calls to that one method will work normally.
That's really a stupid example which does not show the benefit of request/response wrapper. Actually, the whole filter example is poor. Emitting HTML should be done by a JSP or at highest a servlet (but also that is still poor). Go through our filters wiki page to get some ideas about what a filter can be used for.
A response wrapper is useful if you want to modify the response's behaviour or just want to collect information about the response while it is been used in the request-response chain. The modified behaviour takes then place whenever some servlet or JSP calls a certain method on the response. If you have overriden it in your wrapper class, then this one will be called instead. You could alter the behaviour or collect information there.
Here on Stackoverflow you can find some concrete examples of useful HttpServletResponseWrapper implementations.
How to insert response size and time into the page itself, at least partially?
MD5 Signing a HttpServletResponse
How to configure Tomcat to not encode the session id into the URL when HttpServletResponse.encodeURL() is invoked
How to add response headers based on Content-type; getting Content-type before the response is committed
How is annotations support in jsp implemented in sitebricks?
Capture and log the response body
Log only http servlet response headers
How to include a JSP page in a Facelets page?
Capture generated dynamic content at server side

Avoiding the endless loop in JSP servlet mapping

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).

Categories

Resources