Reading URL parameters when POST request with data arrives in Java Servlet - java

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.

Related

Wiremock capture response body for later reuse

I'm facing a particular use case while using Wiremock standalone API.
I would like to be able to reuse a response body generated by stubbing for a another request (stubbed as well) as a context model. The purpose is to store for a generated Id the entire response data, that would allow me to serve it again simply knowing the Id, in a get method particularly (where there is no request body).
Is there a way while defining a stub of response to capture the generated response, in order to store it?
Or if you have other better idea.
Finally I solved the problem by using an okhttp interceptor (which depends on your client solution).
In the interceptor, I store every response data (e.g.: a generated ID) and set them in every next request headers when it matches with part of the the response stored.
adding them to the request headers allows me to access them in a json template file for instance

Changing HTTP method in RequestDispatcher

How do I change HTTP method in javax,servlet.RequestDispatcher?
I have some old service APIs that support GET and POST, The new version supports DELETE method for removing a record which used to happen through POST earlier.
We are decommissioning old version APIs by setting RequestDispatcher.forward() for old end points (stop gap arrangement until clients change). everything was cool except this POST to DELETE mapping.
Any solution there for this problem without adding POST end point for delete operation in new API?>
Although I agree using the next layer after your servlets would be a better choice, this is interesting. It use to be common to wrap an incoming request to add request based functionality (IE: auth state, etc). The HttpServletRequestWrapper was used to accomplish this. You could do the following if you just need to change the method:
class PostDeleteAdapter extends HttpServletRequestWrapper {
public String getMethod(){ return "POST"; }
}
You may also change other aspects of the incoming request if you need to further adapt the request. This may play well with your servlet containers RequestDispatcher, however it's dependent upon the container entirely.
I think you can't do it using servlet API. You can do what you want creating a new request, processing it's response and sending it back through the original response (in the servlet).
Some http clientes might help you. See Apache HTTP client:
http://hc.apache.org/httpclient-3.x/methods/delete.html)

Set Request Attributes with ContentExchange

Is there a way to set request attributes in a ContentExchange object? What I have been doing so far is send information to the server in the Request body by using the setRequestContentSource(InputStream inputstream) method. But what should I do if I want to send information categorized by field names, say like, Content, Title, Author etc.?
The code that I have as of now is pasted below. Thanks for your help!
exchange.setRequestContentSource(new ByteArrayInputStream(
serialized.getBytes("UTF-8")));
exchange.setRequestContentType("text/html");
exchange.setMethod("POST");
exchange.setURL(("http://localhost:8089/"));
client.send(exchange);
To be more precise, by request attributes, I mean something equivalent to request.setAttribute("Name","ABC"). Only in this case, the request is sent is through the ContentExchange object.
Ah, so ServletRequest attributes are not part of the HTTP protocol.
There is no standard way to send those attributes via a HTTP protocol.
In fact, the servlet spec itself limits its use for application specific information (such as passing information from a filter to a servlet) and SSL certificate related information.
However, using standard POST and form data, via exchange.setRequestContentType("multipart/form-data"); and obtain those values using HttpServletRequest.getParameter("Content"), etc.. similar to how a FORM is submitted from a web browser. Benefit here, is that you can even provide a simple HTML FORM to test our your application.
Choice #2 is to send your data in a markup like JSON or XML and have the server parse it.
This has the benefit of allowing for hierarchical data.
If this interests you, I'd recommend you read about REST and maybe dig into a REST library like Jersey, Restlet, and standards like JAX-RS.

Sending a new request to another servlet in the same web container without using an extra thread

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.

Servlet include swallows HTTP headers in Tomcat

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?

Categories