I have a servlet that does a request dispatcher include of another servlet.
The included servlet sets headers that I would like to read in the including servlet. So I pass in a custom HTTPResponse object in the include() method which captures all feedback activity from the servlet.
The problem is that the headers are not being set in my custom response. I've run in debug and examined what looks like Tomcat wrapping my custom response object with its own response object. The setHeader calls go to this wrapping class and never propagate to my custom response object.
I imagine Tomcat does this to protect the client from headers being set in the wrong place. The funny thing is that the same approach works the way I'd expect in Jetty.
It's been a while since I've done Servlets seriously so I'm struggling a bit here. I'm trying to figure out how to read the response headers from a servlet that's invoked via dispatcher.include().
From the Servlet specifications section SRV.8.3:
The include method of the RequestDispatcher interface may be called at any time.
The target servlet of the include method has access to all aspects of the request
object, but its use of the response object is more limited.
It can only write information to the ServletOutputStream or Writer of the
response object and commit a response by writing content past the end of the
response buffer, or by explicitly calling the flushBuffer method of the
ServletResponse interface.
It cannot set headers or call any method that affects
the headers of the response. Any attempt to do so must be ignored.
How about setting your values for the calling servlet in request scope, with request.setAttribute(...) and then reading it from there once you return? Could that work?
Related
In Java servlets you read a JSON from a POST request e.g. via
new JSONObject(toString(httpRequest.getInputStream()))
Now additionally to the JSON I would like to specify parameters in the URL, they can be read via:
httpRequest.getParameterMap().get("someURLParam")
All is working (I'm using AJAX post requests and jetty for server side)
BUT
I'm concerned and confused if and when these two methods influence each other as the javadocs from javax.​servlet.​ServletRequest.getParamter(String) says:
If the parameter data was sent in the request body, such as occurs
with an HTTP POST request, then reading the body directly via
ServletRequest.getInputStream or ServletRequest.getReader can
interfere with the execution of this method.
What does it mean in my case? Or do they only interfere if content type is x-www-form-urlencoded? Or only if using getParameter and the method getParameterMap is fine?
If you are only using getParameter/getParameterMap, you will be fine. This is because, behind the scenes, those methods may call getInputStream. The spec says MAY because it's up to the implementation, so the behavior may vary from one container to another.
If your content isn't form encoded, or you are processing a GET request, etc., getParameter/getParameterMap only needs to get the parameters from the query string, so it makes sense that Jetty wouldn't read the body in those cases.
I am studying for the Spring Core certification and I have some doubts about how Spring handles REST requests.
I know that with REST the resources are exposed as name and that the actions on these resources are the HTTP methods, such as GET, PUT, POST, and DELETE.
And I know that requests are handled by the use of #RequestMapping annotations over the method that handles the operation on the resource.
From what I have understood, both standard web applications and RESTful applications use some codes to communicate with their clients (the RESTful application have an expanded set of codes) that I think represent the status of the request (for example 200 is the request is a successful GET returning content, etc.).
Now the documentation shows the use of the #ResponseStatus annotation as in this example:
#RequestMapping(value="/orders", method=RequestMethod.POST)
#ResponseStatus(HttpStatus.CREATED) // 201
public void createOrder(HttpServletRequest request, HttpServletResponse response) {
Order order = createOrder(request);
// determine full URI for newly created Order based on request
response.addHeader("Location",
getLocationForChildResource(request, order.getId()));
}
So looking at the previous method I know that it handles HttpRequest POST requests towards the resource named /orders (using REST the resource is seen as an URL, is that correct?).
But what exactly does the annotation below do:
#ResponseStatus(HttpStatus.CREATED) // 201
I know that the 201 status code means that a new resource was created on POST.
And looking at the official documentation I can read:
Marks a method or exception class with the status code and reason that
should be returned. The status code is applied to the HTTP response
when the handler method is invoked, or whenever said exception is
thrown.
So what exactly it means? I think that as is done in the previous example it sets the 201 status that says that the resource is correctly created by the POST request. If this is correct I have 2 questions:
The resource is the /orders URI. So what is created? a file named orders (I think that this assertion is false) or what?
Where the 201 status is put?
201 is an HTTP status code. It indicates that the
request has been fulfilled and resulted in a new resource being created.
So if your server is returning such a status code, then the client understands that some (conceptual) resource was created. What that resource is is your responsibility, you're the server.
A status code is part of the HTTP response status line.
Actually, the JavaDocs for the ResponseStatus annotation warn us not to use this annotation for REST APIs. This annotation will cause Spring to call the HttpServletResponse.sendError() method, which will result in an HTML Error page. You don't want that in a RESTful service. Here's what it says:
Warning: when using this annotation on an exception class, or when setting the reason attribute of this annotation, the HttpServletResponse.sendError method will be used.
With HttpServletResponse.sendError, the response is considered complete and should not be written to any further. Furthermore, the Servlet container will typically write an HTML error page therefore making the use of a reason unsuitable for REST APIs. For such cases it is preferable to use a ResponseEntity as a return type and avoid the use of #ResponseStatus altogether.
So I have a org.springframework.stereotype.Controller and what I want to do is process the request normally if HTTP headers HA and HB are specified. But if the request has query parameters QA and QB specified, I want to redirect the request WITHOUT QA or QB, but WITH HTTP headers HA and HB such that HA has the value of QA and HB has the value of QB.
For example, if the incoming request is HTTP GET ~/rest/mortgage with HTTP headers x-name=foo and x-date=bar, I would process that request as is. But if the incoming request is HTTP GET ~/rest/mortgage?x-name=foo&x-date=bar, I want to redirect to myself with the request HTTP GET ~/rest/mortgage with HTTP headers x-name=foo and x-date=bar.
By doing this, I can have a single code path dealing with both styles of making the HTTP request just by adding a simple check at the controller level. This is because I pass the HttpServletRequest around to various parts of the code, so if I have the consistency of always having HA and HB instead of QA and QB, it reduces the cyclomatic complexity of the code.
Is this possible? And if so, how?
If this is not possible, I can achieve my desired result by defining my own wrapper for HttpServletRequest that does the transformation for me, but that's not as elegant of a solution.
If you want to intercept the HTTP request you need to use a Servlet Filter.
In order to replace the URL you have to get the Dispatcher for the new URL, and then dispatch the request again with the forward() method.
Something like this:
servletRequest.getRequestDispatcher(modifiedURL)
.forward(servletRequest, servletResponse);
However, you are not explicitly allowed to modify the HTTP headers. The way around this is to wrap the request and have a proxy object that handles this, by extending HttpServletRequestWrapper. It already implements the HttpServletRequest so you shouldn't have any issues using it wherever you are interacting with the original servlet request.
You can simply extend it and add your own method like addHeader() and keep an internal Map or something of the extra artificial headers you are adding. Then you have to just override getHeaderNames() and getHeader() to also combine the original headers with your own extra ones.
The last thing you will need to do is wrap your original servletRequest in your new wrappedServletRequest in your Filter. So something like this (where WrappedServletRequest is your new implementation that extends HttpServletRequestWrapper):
WrappedServletRequest wrappedServletRequest =
new WrappedServletRequest(servletRequest);
wrappedServletRequest.addHeader("yourHeader", "yourValue");
wrappedServletRequest.getRequestDispatcher(modifiedURL)
.forward(wrappedServletRequest, servletResponse);
Remember to add your <filter> and <filter-mapping> of your Filter in your web.xml for it to work.
You might want to have a look at this article for more details. It has some more information as to why you have to do it this way.
I didn't try it myself, so comment below and confirm if you manage to get it to work.
Is there any known way to send a new SOAP request from one servlet to another within a single WebContainer without consuming an additional web container thread?
So far I have tried using RequestDispatcher.include(request, response) with a customised request and response so I can provide my own input and intercept the callee's output.
With this, I am able to intercept the output without issue (using a custom HttpServletResponse class that writes to a buffer), but I have been unable to send customised input with this method. I am using an extension of HttpServletRequestWrapper to provide my own input to the third party application (instead of the original request to my application), however it seems like either WebSphere or Axis are discarding my wrapper and I therefore get a SOAP fault instead of a valid response.
For clarity, I don't need to forward the original request to the callee (which is a JSONP GET request), I need to fabricate a new SOAP request within my application and send that to the callee instead.
Is there a variation of this method I should try? Is there a completely different way to send a request within a single web container?
Many thanks to those who respond.
For context, I am writing a JSON/REST web service to be run on WebSphere Application Server, which in turn calls a third party product via SOAP on Axis 2. Unfortunately this third party product is only available via a SOAP HTTP interface, despite itself being a Java servlet running inside the same WebSphere web container.
Previously I have been calling this application using an HTTP proxy generated with the SOAP proxy generator based on third party product's WSDL. This works fine however it means that one call to my service in turn consumes two web container threads which is a severe vulnerability. Once the web container thread pool is full, it stays full since the requests to my servlet are holding threads until the third party application responds, which it is not able to do because no threads are available to process the HTTP request my servlet made.
Update:
I have done some further testing and been able to do this type of forwarding to my REST service successfully. I am able to query my REST/JSON service with a synthetic ServletRequest and ServletResponse, therefore allowing me to achieve my original purpose if the product I was calling did not use an Axis SOAP interface.
It appears that Axis is looking in a different place for the SOAPAction header than I expected, as I always get a "no SOAPAction header found!" fault message back despite me adding a SOAPACtion header to my synthetic request (I have verified that the SOAPAction header is in fact added).
It turns out that the reason I could not get the service to work with Axis due a "missing" SOAPAction header has nothing to do with WebSphere or Axis at all. It was a ConcurrentHasMap that somehow was comparing two equivalent strings and saying they were different, so the SOAPAction header was never returned when Axis looked for it. To work around this, I simply tested for queries on 'SOAPAction' and hardcoded the response.
So, for future reference here is the general setup I used.
Create a class implementing HttpServletRequest that wraps another HttpServletRequest to be provided in the constructor. In this class the getHeader method was overridden to catch requests for the SOAPAction header, other header requests may be forwarded to the original request (Axis doesn't seem to look for anything other than the SOAPAction header). I also over-rode the getInputStream method to return my own ServletInputStream implementation that simply reads from a byte buffer using a fixed text encoding, and the getContentLength method to return a length consistent with my data.
Create a class implementing HttpServletResponse, which only correctly implements the getWriter and getOutputStream methods. The getOutputStream method return a custom ServletOutputStream implementation that records its output to a byte buffer. The getWriter method returned a special PrintWriter that wrote to the same ServletOutputStream returned by getOutputStream, except this writer always needed to flush after writing - I am not sure why this had to be case.
Before dispatching the request using RequestDispatcher.include(request, response), I wrapped my synthetic HttpServletRequest in a HttpServletRequestWrapper, which oddly seemed to help. I then used RequestDispatcher.include(request, response) in the usual fashion, and read the SOAP service's output from my custom ServletOutputStream's byte buffer to process as it in the same as if I had issued an HTTP request.
I've used a Request Dispatcher in a Filter to change the request URI, but the problem with this Request Dispatcher is that it breaks the Filter chain. So, the Filter which is going to be called after this Filter doesn't get called.
If I use send redirect then the Filter will be called but the URL is reflected in the browser.
I then tried the HttpRequestWrapper class to change the URI by overriding the getRequestURI() method and I've written the whole logic to change the URI and return it. This method gets called and the URL is indeed changed.
However, Tomcat always throws an HTTP 404 Error. I've also tried with GlassFish.
I don't know what the problem is. Is overriding the getRequestURI() the right thing to do?