How to call servlet from java - java

I've a third-party servlet inside a JAR that I cannot change. I've extended that servlet and been using it normally as a servlet should be used, the client side makes an HTTP request that invokes my servlet.
But now the client wants an automatic service, that is, I will need to do some requests to that third party servlet from the same webapp where the servlet is.
I looked at the the third party servlet code but I didn't found a place to bypass the servlet because the HttpServletRequest and HttpServletResponse objects are passed from method to method... Basically it seems that I would need to re-implement all the third party code.
Solutions I found but do not satisfy me:
Call servlet from URL with HttpURLConnection: My common sense says that calling the third party servlet from a url is not the best way to
go, besides the overhead added, I don't want to expose the third party
servlet. Calling my servlet from a url also brings problems with
sessions and other things.
Call the doGet directly: This seems to be out of the question because there is no implementation for the HttpServletRequest and
HttpServletResponse.
Use jMock or something like that: Didn't explore this solution yet, but it seams wrong to use a test-driven library in the real
environment.
Anyone has an idea how to interact with that third party servlet?
EDIT:
Since my English is not very good and I'm finding difficult to explain myself here goes a schematic to try to explain better
EDIT2: After a meeting the third party maker they offer to isolate the methods I need to avoid calling the servlet. If you don't have the same luck I did check out both gigadot and BalusC answers.

If I understand your question correctly, you have implemented or have a third party servlet that generate the report for you.
Now what you want to do is to periodically generate the report and store in session so that when user want to get the report they can retrieve it using another servlet.
If this is the case then you want the task to be running periodically on your server. You will need some sort of task scheduler to run on your server and what the task does is just make a http request to your servlet (this can be http GET or POST).

Calling my servlet from a url also brings problems with sessions and other things.
If that's the sole problem, then just use the CookieManager to maintain the cookies (and thus also the session) in subsequent URLConnection calls.
// First set the default cookie manager.
CookieHandler.setDefault(new CookieManager(null, CookiePolicy.ACCEPT_ALL));
// All the following subsequent URLConnections will use the same cookie manager.
URLConnection connection = new URL(url).openConnection();
// ...
connection = new URL(url).openConnection();
// ...
connection = new URL(url).openConnection();
// ...
See also:
Using java.net.URLConnection to fire and handle HTTP requests

You could try to separate out your servlet logic into several phases. The entry point that takes the request/result, the action that processes parameters sent and generates the output.
public void doGet(HttpServletRequest req, HttpServletResponse rsp){
relay(rsp,act(req.getParameter("a"));
}
public static String act(String a){
return "You provided: " + a;
}
public static void relay(HttpServletResponse rsp, String content){
rsp.setResponseCode(200);
rsp.getOutputStream().write(content.getBytes());
}
This lets you call act(whatever) to do what you want, and then do what you want with the response. If returning a string is not enough, you could make any return type you want, probably something that could contain a list of headers, response code, and content template.

Related

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

Jersey web service proxy

I am trying to implement a web service that proxies another service that I want to hide from external users of the API. Basically I want to play the middle man to have ability to add functionality to the hidden api which is solr.
I have to following code:
#POST
#Path("/update/{collection}")
public Response update(#PathParam("collection") String collection,
#Context Request request) {
//extract URL params
//update URL to target internal web service
//put body from incoming request to outgoing request
//send request and relay response back to original requestor
}
I know that I need to rewrite the URL to point to the internally available service adding the parameters coming from either the URL or the body.
This is where I am confused how can I access the original request body and pass it to the internal web service without having to unmarshall the content? Request object does not seem to give me the methods to performs those actions.
I am looking for Objects I should be using with potential methods that would help me. I would also like to get some documentation if someone knows any I have not really found anything targeting similar or portable behaviour.
Per section 4.2.4 of the JSR-311 spec, all JAX-RS implementations must provide access to the request body as byte[], String, or InputStream.
You can use UriInfo to get information on the query parameters. It would look something like this:
#POST
#Path("/update/{collection}")
public Response update(#PathParam("collection") String collection, #Context UriInfo info, InputStream inputStream)
{
String fullPath = info.getAbsolutePath().toASCIIString();
System.out.println("full request path: " + fullPath);
// query params are also available from a map. query params can be repeated,
// so the Map values are actually Lists. getFirst is a convenience method
// to get the value of the first occurrence of a given query param
String foo = info.getQueryParameters().getFirst("bar");
// do the rewrite...
String newURL = SomeOtherClass.rewrite(fullPath);
// the InputStream will have the body of the request. use your favorite
// HTTP client to make the request to Solr.
String solrResponse = SomeHttpLibrary.post(newURL, inputStream);
// send the response back to the client
return Response.ok(solrResponse).build();
One other thought. It looks like you're simply rewriting the requests and passing through to Solr. There are a few others ways that you could do this.
If you happen to have a web server in front of your Java app server or Servlet container, you could potentially accomplish your task without writing any Java code. Unless the rewrite conditions were extremely complex, my personal preference would be to try doing this with Apache mod_proxy and mod_rewrite.
There are also libraries for Java available that will rewrite URLs after they hit the app server but before they reach your code. For instance, https://code.google.com/p/urlrewritefilter/. With something like that, you'd only need to write a very simple method that invoked Solr because the URL would be rewritten before it hits your REST resource. For the record, I haven't actually tried using that particular library with Jersey.
1/ for the question of the gateway taht will hide the database or index, I would rather use and endpoint that is configured with #Path({regex}) (instead of rebuilding a regexp analyser in your endpoint) .
Use this regex directly in the #path, this is a good practice.
Please take a look at another post that is close to this : #Path and regular expression (Jersey/REST)
for exemple you can have regexp like this one :
#Path("/user/{name : [a-zA-Z][a-zA-Z_0-9]}")
2/ Second point in order to process all the request from one endpoint, you will need to have a dynamic parameter. I would use a MultivaluedMap that gives you the possibility to add params to the request without modifying your endpoint :
#POST
#Path("/search")
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
#Produces({"application/json"})
public Response search( MultivaluedMap<String, String> params ) {
// perform search operations
return search( params);
}
3/ My 3rd advice is Reuse : make economy and economy make fewer bugs.
it's such a pitty to rewrite a rest api in order to perform solr search. You can hide the params and the endpoint, but could be great to keep the solr uri Rest formatting of the params in order to reuse all the search logic of solr directly in your api. This will make you perform a great economy in code even if you hide your solr instance behind you REST GATEWAY SERVER.
in this case you can imagine :
1. receive a query in search gateway endpoint
2. Transform the query to add your params, controls...
3. execute the REST query on solr (behind your gateway).

Alternative of URL parameter for deciding which method to call

Right now based on the site name in the URL parameter, we decide the appropriate actions to take(method calls etc) in the Java (Standard Jsp/Servlet web applications). For example, the request would be something like www.oursite.com?site=Ohio
Wondering what would be the alternative of doing this without having to provide URL parameter.
You could use POST instead of GET.
GET appends request parameters to the end of the URL.
POST sends encoded data using a form.
http://www.tutorialspoint.com/jsp/jsp_form_processing.htm
Why not just code it into the path?
www.oursite.com/Ohio
If you're just using straight servlet api, you can just do something of this nature:
String path = request.getPathInfo();
String site = path.split("/")[0];
That being said, most web frameworks have some support for helping with this.
For example, in spring mvc:
#RequestMapping(value="/{site}/blah/blah", method=RequestMethod.GET)
public ModelAndView blahBlah(HttpServletRequest req,
HttpServletResponse resp,
#PathVariable("site") String site) {
// do stuff here
}
Of course you could do this at the controller level too if all your methods need that sort of mapping:
#Controller
#RequestMapping(value="/{site}")
public class MyController {
#RequestMapping(value="/blah/blah", method=RequestMethod.GET)
public ModelAndView blahBlah(HttpServletRequest req,
HttpServletResponse resp,
#PathVariable("site") String site) {
// do stuff here
}
}
I believe this is cleaner than a query param, though it still shows up in your URL. There's other, more complex methods like using apache's reverse proxying and virtual host capabilities to switch based on site names. You could do something at login, and store the site in session. It all depends on your requirements.
You could use an alternate URL, like ohio.oursite.com. This process could be automated by having your server respond to *.oursite.com. I would probably set up a filter that looked at what the subdomain was and compared that with a predefined list of allowed sites. If it didn't exist, you could redirect back to the main (www) site. If it did, you could set a request attribute that you could use in a similar way that you currently use the request parameter now.

Servlet Testing

I am using the ServletTester class provided by Jetty to test one of my servlets.
The servlet reads the the body of the request using InputStream.read() to construct a byte[] which is the decoded and acted on by the servlet.
The ServletTest class provides a method getResponses(ByteArrayBuffer) but I'm unsure how to create one of these in the correct format since it would also need to contain things like headers (e.g. "Content-Type: application/octet-stream).
Can anyone show me an easy way to construct this, preferably using an existing library so that I can use it in a similar way to the HttpTester class.
If there is a "better" way to test servlets (ideally using a local connector rather than via the tcp stack) I'd like to hear that also.
Many thanks,
Why use a mock at all? Why not test the servlet by running it in jetty?
Servlet servlet = new MyServlet();
String mapping = "/foo";
Server server = new Server(0);
Context servletContext = new Context(server, contextPath, Context.SESSIONS);
servletContext.addServlet(new ServletHolder(servlet), mapping);
server.start();
URL url = new URL("http", "localhost", server.getConnectors()[0].getLocalPort(), "/foo?bar");
//get the url...assert what you want
//finally server.stop();
Edit: Just wanting to reassure people that this is very fast. Its also a very reliable indicator of what your code will actually do, because it is in fact doing it.
Spring MVC provides a small set of "mock" classes for the various javax.servlet interfaces, such as HttpServletRequest, HttpSession, and so on. This makes it easy to unit test the likes of a servlet, you just inject mocks into the e.g. doGet() method.
Even if you don't use Spring itself on the server, you can still use the mock from the library, just for your tests.
You can use HttpClient to simplify testing somewhat. Take a look at the following article:
http://roberthanson.blogspot.com/2007/12/testing-servlets-with-junit.html
That in combination with servlet tester should give you what you want unit test wise.

Categories