How to support batch web api request processing using Spring/Servlets - java

We have our Web API written in using RESTEasy. We would like to provide support for Batch requests processing the way Google Batch request processing works.
Following is the approach which are using currently,
We have a filter which accepts incoming multipart request. This filter then creates multiple mock requests and response objects and then calls chain.doFilter using these mock requests.
public class BatchRequestProcessingFilter extends GenericFilterBean {
#Override
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)req;
MockHttpServletRequest[] mockRequests = BatchRequestProcessorUtils.parseRequest(request);
MockHttpServletResponse[] mockResponses = new MockHttpServletResponse[mockRequests.length];
for(int i=0 ; i <= mockRequests.length ; i++ ) {
chain.doFilter(mockRequests[i], mockResponses[i], chain);
}
BatchRequestProcessingUtils.populateResponseFromMockResponses(res, mockResponses);
}
}
MockHttpServletResponse class returns a dummy OutputStream which wraps ByteArrayOutputStream.
BatchRequestProcessorUtils parses the multipart request and returns the mock request which wraps actual request but returns the header specified in split body of the actual request body.
I could not find any existing library which supports batch request processing. So my question is that, is this the correct approach to support batch request or is there any standard way which should be used?
Note that we are using Tomcat 8.

Sachin Gorade. I have not heard about such libraries, but I think your approach is reasonable. If I had to solve that problem, I would think like this:
In our HTTP servlets we can process requests only separately, and it is the reason why we should wrap all requests, that we want to send, into another single request at client side.
As on our server side we have only one request, then we should unwrap all requests we have put into it. And, because we dont know how to process each request in our batch mechanizm - we shold send it through all filters/servlets. Also it is a reason to put our batch filter at the first position in the order.
Eventually, when all requests has been processed, we should send a response back to the client. And again, to do that we should wrap all responses into a single one.
At the client side we should unwrap responses and send each of that to some objects, that can process it.
In my oponion there should be two mechanizms:
Batch sender for client side, that is responsible for collecting and wrapping requests, unwrapping responses and sending them to theirs processors(methods that process regular responses).
Batch processor for server side, that is responsible for unwrapping requests, and collecting and wrapping responses.
Of course, that two parts may be coupled (i.g. to have shared "Wrapper" module), because objects we must be wrapped and unwrapped in the same way.
Also, if I worked on it, I would try to develop the client side mechanizm like a decorator upon a class that I use to send regular requests. In that case, I would be able to substitute regular/batch mode anytime I need to do it.
Hope my opinion is helpful for you.

Related

Server side custom headers with embedded jetty and jersey

We have a legacy application that uses embedded Jetty and provides functionality through clients making HTTP calls. Most of the information/parameters needed by the server is sent by the client through HTTP headers. We are now prototyping the use of REST API calls using Jersey where the same parameters are provided as JSON inputs. One of the requirements is to maintain backward compatibility and not disturb the existing functionality.
While we were able to use Jersey and pass in parameters, we are looking for help with the following:
We want to avoid changing the current jetty handlers in the application, so we would like to convert the json input from the REST API input into headers and pass them on to the handlers, so that the current functionality can kick in from that point.
I have tried the other (very helpful) posts on using the wrapper/filter mechanisms to add custom headers, even one using the ContainterRequestFilter. Following are my references:
Adding an HTTP header to the request in a servlet filter
How to add servlet filter with embedded jetty
How to add a header to http request using a filter in jax-rs
However for security reasons, the legacy application has this line of code (recommended in Jetty docs) which uses the base request instead of the wrapped request:
Request base_request = request instanceof Request ? (Request)request : HttpConnection.getCurrentConnection().getHttpChannel().getRequest();
Response base_response = response instanceof Response ? (Response)response : HttpConnection.getCurrentConnection().getHttpChannel().getResponse();
This effectively does not use the HttpServletRequestWrapper object that I send in. Since this line of code looks for the org.eclipse.jetty.server.Request object, I tried creating a wrapper around this object, like so, but this did not work because this instance seems to have most of the content as null, plus it would not provide the rest of the methods that the Request object would provide.
class MyRequestWrapper extends Request
{
public MyRequestWrapper(HttpServletRequest request)
{
super( ((Request)request).getHttpChannel(), ((Request)request).getHttpInput());
}
#Override
public String getHeader(String name)
{
if(name.equalsIgnoreCase("X-My-Test"))
{
return "MyName";
}
return super.getHeader(name);
}
}
What is the best way to send the JSON inputs in as headers from the REST handling method to the existing Jetty handlers, without creating security concerns? I guess I could tweak that check for the base request a bit, but I am not sure of the best way to do it.
Wrapped requests are only valid for the same ServletContext and Filter chain that the wrapped request was created in and only applies to the rest of the executing Filter chain from the point in time it was created.
A wrapped request will never apply to a standard Jetty Handler, as that's not participating in a ServletContext or Filter chain.
It is also impossible to wrap the core Jetty Request object due to the needs of the context-less environment it executes within. You cannot change this behavior.
If you are wrapping requests and whatnot just to provide a custom request header, then stop doing ALL of the wrapping and nonsense you are dealing with right now.
Note: The minute you stop wrapping HttpServletRequest, HttpServletResponse, or the Servlet streams is the minute you will have the ability to use features introduced for Servlet 3.0 and newer, such as AsyncContext and Async I/O. The technique of wrapping these components is discouraged in modern usage because it limits your options for better performing webapps.
You have 2 choices, both modify the Request headers in-place.
Modify the Request headers before dispatch.
Modify the Request headers during dispatch via a low level Jetty Handler.
If you choose to modify the headers before dispatch, there are 2 places you can do this.
As a HttpConfiguration.Customizer
During one of the pre-dispatch HttpChannel.Listener events
If you choose to modify the headers during dispatch, then create a Jetty Handler that modifies the Request headers, and put it somewhere early in your server handler hierarchy.
The code that modifies the Request headers will all do the same thing, here's the Handler version as an example.
package jetty.demo;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.handler.HandlerWrapper;
public class ModifyRequestHeadersHandler extends AbstractHandler
{
#Override
public void handle(String target, Request baseRequest,
HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
// As fully fleshed out field
final HttpField X_MY_TEST = new HttpField("X-My-Test", "MyName");
baseRequest.getHttpFields().put(X_MY_TEST);
// As predefined header and custom value
baseRequest.getHttpFields().put(HttpHeader.X_POWERED_BY,
"ModifiedRequestHeadersHandler");
// As string header and string value
baseRequest.getHttpFields().put("X-Foo-Test", "MyFooName");
}
}

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)

Send data from one Java application to another

I have to send an XML-file from one java Application to another.
Currently, it works like this:
- Export to local XML-file from application 1
- Import local XML-file in application 2
Now I have to do this via web service(s). Is it possible to create a JAX-WS web service in application 1 that redirects to application 2 with the data needed?
I can send the data (object) as a serialized object, instead of a XML-file. But is this possible? And if so, how?
Both applications are written in Eclipse-Scout.
Thanks in advance.
webservice is simple and usefull if your two apps run on different machines.
Sending server:
use a library for http (post or get)
1 only keep your file. just use an HTTP / POST. works for text an binary
2 more simple: if your datas are little text, you can use HTTP / GET (beware of special characters: you can encode them).
3 if you can put all your datas in one structure (object), just serialize it, put the result in a String, and send it.
Receiving server:
if you use tomcat, extend HttpServlet, and get by doPost or doGet
Or you can use another light http server
Or soap library (no really need).
DOPOST/DOGET
Sending server:
HttpURLConnection conn= (HttpURLConnection) url.openConnection(); // etc.
Receiving server:
public class MyServlet extends HttpServlet {
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String one_parameter = request.getParameter("name_of_parameter");
See these links for more explanation:
Java - sending HTTP parameters via POST method easily
doGet and doPost in Servlets
(A bit long for a comment)
You may want to stay away from bloated things like JAX-WS and just use a standard Servlet and JAXB or XStream for de-/serialisation.
Also, do not ever use Java (binary) deserialisation or default XStream on unauthenticated inputs/transport (e.g. HTTP or untrusted clients even with HTTPS). It always leads to remote code execution exploits that cannot be migitated without redeveloping your webservice interface. Most recent instance...

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.

Intercept HTTP requests in server java

I need to implemented something like a filter or listener, that intercepts HTTP requests and retrieves the HTTP headers for various purposes.
I use Java, Jboss application server and web services. I want this filtering system to be performed prior to the Web Services call - was thinking about aspects but they do not hold the HTTP related stuff. After the filter, the service call should be carried out.
Jax-WS handlers don't work for me either as they only hold the SOAP payload.
Any ideas?
Thanks in advance.
can you not create a servlet filter which intercepts all the requests coming to your webservice engine? If you are using Axis or anyother SOAP engine, I hope you should be able to create a filter that intercepts all the requests coming to the main servlet that the SOAP engine provides.
public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain) throws IOException,ServletException
{
HttpServletRequest httpRequest=(HttpServletRequest)request;
HttpServletResponse httpResponse=(HttpServletResponse)response;
Enumeration headerNames = httpRequest.getHeaderNames();
while(headerNames.hasMoreElements()) {
String headerName = (String)headerNames.nextElement();
out.println(headerName);
out.println(request.getHeader(headerName));
}
chain.doFilter(request,response);
}
Use libpcap and the Java interface jNetPcap.

Categories