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
- Adding Header Information to an existing HTTP Request
- How to modify request headers in a J2EE web application
- Modifying HTTP headers using Java
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>