2

Does RequestDispatcher work over multiple webapps ?

I'm asking because I had a single webapp working fine that uses RequestDispatcher rather than redirects so state isnt lost when displaying error and feedback messages.

However I now need to split some functionality between two webapps, so initial call is made from a webpage hosted on webapp1, calls webapp2 which eventually returns user to a page hosted on webapp1.

Clearly if webapps and webapp2 were on different websites using RequestDispatcher would not be possible but is it if both webapps are deployed within the same instance of a servlet container (tomcat 7)

Update

Got the request dispatcher part to work as explained in answer but am unable to retrieve data put in my webapp2 which iss why Im using it

i.e

webapp2 called , does some processing and then dispatches to a jsp on webapp1

protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException
{

    HttpSession userSession = request.getSession(true);
    String emailAddress = ......
    String nextPage     = /finish.jsp
    userSession.setAttribute("DATA", emailAddress);
    ServletContext otherContext = getServletContext().getContext("/webapp1");
    otherContext.getRequestDispatcher(nextPage).forward(request, response); 
}

webapp2 jsp file contains

...
<p>Email(<%=((String)session.getAttribute("DATA"))%>)</p>
...

but always displays null

Update 2 **

Im wondering if Im misunderstanding what crossContext="true" actually does . Does it make the same HttpSession availble in different webapps, or does it just make the ServletContext from one webap availble to another and hence allow one webapp to see the HttpSessions of another webapp ?

Im starting to think what Im doing is a bad idea as Ive always been keen to use vanilla servlet setups and never want to tie myself to a particular implementation. I think it might help if I explain why I flet the need to split the webapps in the first place.

I had a one webapp (webapp1), that was a website about a product I develop and code for purchasing that product using Google Checkout (now Google Wallet).

I then added created a new webapp for a new product (webapp2).

I then tried to add Google Checkout for new product into webapp2, but realised I couldnt do this easily because Google Checkout requires me to provide it with a url which it can call by application once it has processed payment so that I can then send user a license. The url was already set to a servlet in webapp1, but it wouldn't make sense for webapp1 to process payment s for product 2.

One option was to merge webpp1 and webapp2 into one webapp, but this goes against my general view of keeping things modular, it would also mean evey time I would want to make chnages for one product Id have to redeploy everything. It also meant big modifications to webapp1 which I really didnt want to modify as it was working and stable.

The alternative was to create webapp3 and then google url can point to this, and use this for processing purchases of product 1 and product 2 which is what Ive done. But the problem is when purchasing product 1 the starting page is in webapp1 , and once purchase has taken place I want to go back to a page in webapp1, but only webapp3 has the details of the user who has just made the purchase which I wanted to display on on the page in webapp1.

Paul Taylor
  • 13,411
  • 42
  • 184
  • 351

3 Answers3

3

This is due to security reasons by default not possible. You first need to configure Tomcat to enable exposing the ServletContext of the current webapp to other webapps. This is to be done by setting the crossContext attribute of context.xml to true.

<Context ... crossContext="true">

Once done that, then you can use ServletContext#getContext() to obtain the other servlet context by its context path and finally use the RequestDispatcher as obtained by ServletContext#getRequestDispatcher() the usual way as you would do on the one obtained by HttpServletRequest#getRequestDispatcher().

E.g.

ServletContext otherContext = getServletContext().getContext("/otherContext");
otherContext.getRequestDispatcher("/WEB-INF/some.jsp").forward(request, response);
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Hi, thanks that does part does work, however the reason I want to do this is so that webapp2 but can put some data into session object using putAttribute and then get it out in webapp1 but webapp1 is't able to find the data, I'll update the question with example – Paul Taylor Nov 19 '12 at 12:19
  • That would of course only work if they share the same HTTP session. This needs also to be configured in the server side. If all webapps run in the same domain, just set the `sessionCookiePath` attribute of all webapp contexts to `/` to let all webapps share the same HTTP session. If that however forms design problems, then store it in a common datastore instead such as the application scope or a DB. – BalusC Nov 19 '12 at 12:22
  • I thought the session was stored with the request, no ? Just tried http://stackoverflow.com/questions/3980392/tomcat-7-session-cookie-path but has notr made any difference – Paul Taylor Nov 19 '12 at 12:38
  • The request of the current webapp is during dispatching replaced with the request specific to the other webapp. It'd otherwise have contained incorrect information resulting in possibly nasty bugs. Just store common data in application scope or DB instead. You can pass the unique identifier as a request parameter. – BalusC Nov 19 '12 at 12:45
  • Marked your answer right but please give me a clue, how do I store in application scope – Paul Taylor Nov 19 '12 at 12:47
  • The application scope is represented by attributes of `ServletContext` (like as session scope is represented by attributes of `HttpSession` and request scope by those of `HttpServletRequest` .. do you see it now? :) ) See also http://stackoverflow.com/questions/3106452/how-do-servlets-work-instantiation-session-variables-and-multithreading/3106909#3106909 – BalusC Nov 19 '12 at 12:50
  • Er sort, I understand ApplicationScope now and I could use that but I thought I was using Session scope rather than RequestScope and hence would still exist over webapps. – Paul Taylor Nov 19 '12 at 13:23
  • The session scope is represented by attributes of `HttpSession`. The HTTP session of the other webapp isn't the same as the HTTP session of the current webapp, unless they run at the same domain and the server is explicitly configured to set session cookie path to `/`. – BalusC Nov 19 '12 at 13:24
  • Right by same domain you mean the same tomcat instance, I did set the cookie path as described at http://stackoverflow.com/questions/3980392/tomcat-7-session-cookie-path – Paul Taylor Nov 19 '12 at 13:27
  • Ah, I cannot use ServletContext attributes as this variable is per user, so I just want it to exist for for that users interaction, I'll look futher into getting HttpSession working – Paul Taylor Nov 19 '12 at 13:33
1

Yes.

The first thing you need to do is get hold of a ServletContext for the other webapp. You do that with ServletContext::getContext on your own ServletContext, passing the context path of the other webapp.

Then, you simply do ServletContext::getRequestDispatcher as normal on the foreign context.

I haven't actually tried this, but i 100% guarantee that it will work.

Tom Anderson
  • 46,189
  • 17
  • 92
  • 133
1

I am not sure about different servlet container. But it works for same container by using getServletContext().getContext() method.

First you need to make changes in below file

(Windows) C:\Program Files\Apache Software Foundation\Tomcat 7.0\conf\context.xml Set value of crossContext to true.

context.xml

<Context crossContext="true">

    <!-- Default set of monitored resources -->
    <WatchedResource>WEB-INF/web.xml</WatchedResource>

    <!-- Uncomment this to disable session persistence across Tomcat restarts -->
    <!--
    <Manager pathname="" />
    -->

    <!-- Uncomment this to enable Comet connection tacking (provides events
         on session expiration as well as webapp lifecycle) -->
    <!--
    <Valve className="org.apache.catalina.valves.CometConnectionManagerValve" />
    -->

</Context>

Please note that crossContext="true".

Suppose you have two web applications with name InterServletComm1 and InterServletComm2 having servlets Servlet1 and Servlet1 in each web application respectively. Then the code in each servlets goes as follows:

Servlet1.java

package interServletComm1;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class Servlet1
 */
@WebServlet("/Servlet1")
public class Servlet1 extends HttpServlet {
    private static final long serialVersionUID = 1L;

    /**
     * @see HttpServlet#HttpServlet()
     */
    public Servlet1() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException 
    {
        response.setContentType("text/html");
        PrintWriter pw = response.getWriter();

        request.setAttribute("name", "WebApp1");
        ServletContext context = getServletContext().getContext("/InterServletComm2");
        RequestDispatcher rd = context.getRequestDispatcher("/Servlet2");
        rd.forward(request, response);
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
    }

}

Servlet2.java

package interServletComm2;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class Servlet2
 */
@WebServlet("/Servlet2")
public class Servlet2 extends HttpServlet {
    private static final long serialVersionUID = 1L;

    /**
     * @see HttpServlet#HttpServlet()
     */
    public Servlet2() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException 
    {
        response.setContentType("text/html");
        PrintWriter pw = response.getWriter();
        String name = (String) request.getAttribute("name");
        pw.println("This is web application 2.");
        pw.println("<br>The value received from web application one is: " + name);
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
    }

}

Above code sends attribute name from InterServletComm1 and it is received in InterServletComm2. Please let me know if this answer is not clear.

Abhijeet Ashok Muneshwar
  • 2,480
  • 2
  • 31
  • 32