Tagged: servlet

launching 3rd party servlets using jetty for unit testing


References

Apache Maven

<dependency>
  <!-- I'm not sure this is the minimum dependency -->
  <groupId>org.eclipse.jetty</groupId>
  <artifactId>jetty-servlet-tester</artifactId>
  <scope>test</scope>
</dependency>

TestNG


    private static final PORT;

    static {
        PORT = ThreadLocalRandom.current().nextInt(1024, 65536);
    }

    private static Server SERVER;

    @BeforeClass
    public static void startServer() throws Exception {
        SERVER = new Server(PORT);
        final ServletHandler handler = new ServletHandler();
        SERVER.addServeltHandler(handler);
        handler.addServletWithMapping(SomeServlet.class, "/some");
        SERVER.start();
    }

    @AfterClass
    public static void stopServer() throws Exception {
        SERVER.stop();
        SERVER = null;
    }

    @Test
    public void test() throws MalformedURLException {
        final URL url = new URL("http://localhost:" + PORT + "/some");
    }

the JDBC Driver has been forcibly unregistered


My Tomcat instance keeps complaining about de-registration failures of JDBC drivers whenever my web app re-deployed.

The web application [some-fine-webapp] registered the JDBC driver [some.nasty.JdbcDriver] but failed to unregister it when the web application was stopped. To prevent a memory leak, the JDBC Driver has been forcibly unregistered.

This is actually not a problem. That message means that the Tomcat took care of something by himself.

I pay or I owe.

@WebListener
public class MyServletContextListener implements ServletContextListener {

    @Override
    public void contextInitialized(final ServletContextEvent sce) {
    }

    @Override
    public void contextDestroyed(final ServletContextEvent sce) {
        // Is this safe? De-registering while enumerating?
        for (final Enumeration<Driver> drivers = DriverManager.getDrivers();
             drivers.hasMoreElements();) {
            try {
                DriverManager.deregisterDriver(drivers.nextElement());
            } catch (final SQLException sqle) {
            }
        }
    }
}

Well, well, well.

Collections.list(DriverManager.getDrivers()).forEach(d -> {
    try {
        DriverManager.deregisterDriver(d);
    } catch (final SQLException sqle) {
    }
});

Extending Filter Implemtations


Let’s say you are going to extend a class implementing Filter interface.

public class MyFilter implements Filter {


    @Override
    public void init(final FilterConfig config) throws ServletException {

        logger = newLogger();
    }


    protected Logger getLogger() {

        if (logger == null) {
            // before init() or after destroy();
            throw new IllegalStateException("no logger");
        }

        return logger;
    }


    @Override
    public void destroy() {

        logger = null;
    }


    private Logger logger;
}

This is how we should override those two important life-cycle methods

public class YourFilter extends MyFilter {


    @Override
    public void init(final FilterConfig config) throws ServletException {

        super.init(config);

        // do your jobs, after.
        getLogger().log("init()");
    }


    @Override
    public void destroy() {

        // do your jobs, before.
        getLogger().log("destroy()");

        super.destroy();
    }
}

Adding additional headers on a ServletRequest


References

Problem

Simply, we can’t add or modify request headers on given HttpServletRequest instances.

Solution

The only solution, at least mentioned in above links, is using HttpServletRequestWrapper class. I wrote, for my own, a class which wraps additional headers around the actual request instance.

public class RequestHeaderWrapper extends HttpServletRequestWrapper {

    public RequestHeaderWrapper(
        final HttpServletRequest request,
        final Map<String, List<String>> precedingHeaders,
        final Map<String, List<String>> succeedingHeaders) {

        super(request);

        headers = new HashMap<>();

        if (precedingHeaders != null) {
            for (final String name : precedingHeaders.keySet()) {
                List<String> values = headers.get(name);
                if (values == null) {
                    values = new ArrayList<>();
                    headers.put(name, values);
                }
                values.addAll(precedingHeaders.get(name));
            }
        }

        for (final Enumeration<String> names = request.getHeaderNames();
             names.hasMoreElements();) {
            final String name = names.nextElement();
            List<String> value = headers.get(name);
            if (value == null) {
                value = new ArrayList<>();
                headers.put(name, value);
            }
            value.addAll(Collections.list(request.getHeaders(name)));
        }

        if (succeedingHeaders != null) {
            for (final String name : succeedingHeaders.keySet()) {
                List<String> values = headers.get(name);
                if (values == null) {
                    values = new ArrayList<>();
                    headers.put(name, values);
                }
                values.addAll(succeedingHeaders.get(name));
            }
        }
    }

    @Override
    public String getHeader(final String name) {
        final List<String> values = headers.get(name);
        if (values != null && !values.isEmpty()) {
            return values.get(0);
        }
        return null;
    }

    @Override
    public Enumeration<String> getHeaders(final String name) {
        List<String> values = headers.get(name);
        if (values == null) {
            return Collections.emptyEnumeration();
        }
        return Collections.enumeration(values);
    }

    @Override
    public Enumeration<String> getHeaderNames() {
        return Collections.enumeration(headers.keySet());
    }

    private final Map<String, List<String>> headers;
}

Full and latest source code is here and the apidocs is here.

Apache Maven

Please check the central if you’re using Apache Maven.

<dependency>
  <groupId>com.googlecode.jinahya</groupId>
  <artifactId>jinahya-ee</artifactId>
  <version>@@?</version>
</dependency>

Usage

There are some factory methods for simple use.

public class MyFilter implements Filter {

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

        final ServletRequest wrapper =
            RequestHeaderWrapper.newPrecedingInstance(
                requerst, "Accept", "application/xml");
        chain.doFilter(wrapper, response);
}

Servlet Filter for HTTP


A problem of Filter is that the doChain method is for ServletRequest and ServletResponse.

public class MyFilter implements Filter {

    @Override
    public void init(final FilterConfig filterConfig)
        throws ServletException {
    }

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

    @Override
    public void destroy() {
    }
}

It’s not a big deal if you presume that those are actually instances of HTTP version.

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

    final HttpServletRequest hequest = (HttpServletRequest) request;
    final HttpServletResponse hesponse = (HttpServletResponse) response;

    // happy, now?
}

I quickly tired of doing this.

public abstract class HttpFilter implements Filter {

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

        if (request instanceof HttpServletRequest
            && response instanceof HttpServletResponse) {

            doFilter((HttpServletRequest) request,
                     (HttpServletResponse) resposne, chain);

           return;
        }

        chain.doFilter(request, response, chain);
    }

    protected abstract void doFilter(HttpServletRequest request,
                                     HttpServletResponse response,
                                     FilterChain chain)
        throws IOException, ServletException;
}

Now I can do this.

public class MyFilter extends HttpFilter {

    @Override
    protected void doFilter(final HttpServletRequest request,
                            final HttpServletResponse response,
                            final FilterChain chain)
        throws IOException, ServletException {

        // never bean happier. :)
    }
}

Full source is here.

passing the ContextPath to XSL


in your ServletFilter

transformer.setParameter(
    "contextPath", filterConfig.getServletContext().getContextPath());

in your XSL

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
  <xsl:output method="xml"
              doctype-system="http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"
              doctype-public="-//W3C//DTD XHTML 1.1//EN" indent="yes"/>
  <xsl:param name="contextPath"/>
  <xsl:template match="/">
    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" >
      <head>
        <link rel="stylesheet"
              href="{$contextPath}/css/mycss.css"
              type="text/css"/>
      </head>
      <body/>
    </html>
  </xsl:template>
</xsl>