legacy servlet chaining using servlet aliasing

2019-08-19 21:24发布

问题:

I'm trying to migrate an existing application from IIS with ServletExec to tomcat. I got everything working except for the case where there is a servlet chain with two servlets.

The first servlet does some processing and the second servlet does some translation.

request-->ProcessServlet--> (untranslated html) -->TranslateServlet --> response

In my case, I can call the ProcessServlet and it produces html with untranslated tags. The second servlet looks like it takes the html, locates well known translation tags and translates them and produces readable outupt to the browser.

From googling, looks like servlet chaining using servlet aliasing is no longer supported/recommended( from this page) and the same functionality can be achieved with filters but the classes have to implement an interface but unfortunately in my case I have to work with the existing class files.

This page specifies the concept but has no examples.

Also from this page

How to Configure a Servlet Chain Using Servlet Aliasing

Using the Servlet Aliasing subsection of the setup section in the Administration Tool, a list of servlets can be named for a particular URL request. In the servlets table, when adding a new mapping, you are allowed to enter a comma-separated list of servlets in the order in which they should be invoked when a request arrives for that particular URL. This configures a servlet chain to be invoked every time a request that matches the URL arrives.

Can anyone specify an example or point me in the right direction as to how to get this chain to work from web.xml?

UPDATE:

Per kschneid's outline in his answer, here's the full implementation that worked for me

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class TranslateFilter implements Filter {

  private FilterConfig config = null;

  public void init(FilterConfig config) throws ServletException {
    this.config = config;
  }

  public void destroy() {
    config = null;
  }

  public void doFilter(ServletRequest request, ServletResponse response,
                     FilterChain chain) throws IOException, ServletException {

        HttpServletResponse httpResponse = (HttpServletResponse) response;
        MyHttpServletResponseWrapper processResponse  =   new MyHttpServletResponseWrapper(httpResponse);
        chain.doFilter(request, processResponse );
        String content = processResponse.toString();
        config.getServletContext().log("CONTENT: " + content);
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        MyHttpServletRequestWrapper processResponseAsRequest = new MyHttpServletRequestWrapper(httpRequest, content);

        RequestDispatcher dispatch = request.getRequestDispatcher("/Translate");
        response.setContentType("text/html");
        dispatch.forward(processResponseAsRequest, response); // forward to translate servlet with response from process servlet as the request and the original response
  }

}

class MyHttpServletResponseWrapper 
  extends HttpServletResponseWrapper {

  private StringWriter sw = new StringWriter();

  public MyHttpServletResponseWrapper(HttpServletResponse response) {
    super(response);
  }

  public PrintWriter getWriter() throws IOException {
    return new PrintWriter(sw);
  }

  public ServletOutputStream getOutputStream() throws IOException {
    throw new UnsupportedOperationException();
  }

  public String toString() {
    return sw.toString();
  }
}

class MyHttpServletRequestWrapper 
  extends HttpServletRequestWrapper {
    private String content;
    public MyHttpServletRequestWrapper(HttpServletRequest request) {
        super(request);
    }

    public MyHttpServletRequestWrapper(HttpServletRequest request, String content) {
        super(request);
        this.content = content;
    }

    public ServletInputStream getInputStream()
    {
        return new MyServletInputStream(content);
    }

    public BufferedReader getReader()
    {
        InputStreamReader in= new InputStreamReader(getInputStream());
        return new BufferedReader(in);
    }
  }

class MyServletInputStream extends ServletInputStream
{
    private InputStream is;

    public MyServletInputStream(String content)
    {
        is = new ByteArrayInputStream(content.getBytes());
    }

    public int read() throws IOException
    {
        return is.read();
    }
}

回答1:

I'm not really familiar with the implementation details of servlet chaining, but here's a general approach that might work. Map the two servlets to different URLs:

<servlet-mapping>
    <servlet-name>process</servlet-name>
    <url-pattern>/process</url-pattern>
</servlet-mapping>

<servlet-mapping>
    <servlet-name>translate</servlet-name>
    <url-pattern>/translate</url-pattern>
</servlet-mapping>

Then map a filter to the process servlet:

<filter-mapping>
    <filter-name>processChain</filter-name>
    <servlet-name>process</servlet-name>
</filter-mapping>

The processChain filter would do something like:

public void doFilter(ServletRequest request,
                     ServletResponse response,
                     FilterChain chain) throws IOException, ServletException {
    ServletResponseWrapper processResponse = ...; // response buffer
    chain.doFilter(request, processResponse); // let process servlet populate response buffer
    ServletRequestWrapper processResponseAsRequest = ...; // use processResponse to create request for translate servlet
    RequestDispatcher dispatch = request.getRequestDispatcher("/translate");
    dispatch.forward(processResponseAsRequest, response); // forward to translate servlet with response from process servlet as the request and the original response
}

...or something like that ;)