Search This Blog

Thursday 24 December 2015

Servlets 3.x - pluggin servlets at runtime

In the previous post we looked at Servlet 3.0 spec's ability to allow Servlets and filters to be defined in jars outside the deployment descriptor. In this post I am going to look at a different feature - declaring Servlets and Filters at runtime.
The first step was to create a servlet class:
public class TestServlet extends HttpServlet {

  @Override
  protected void doGet(HttpServletRequest req, 
      HttpServletResponse resp) throws ServletException, IOException {
    resp.getOutputStream()
      .write("<html><body>Request received</body></html>".getBytes());
  }
}
I also defined a filter class:
public class TestFilter implements Filter {

  @Override
  public void init(FilterConfig arg0) throws ServletException {
    System.out.println("TestFilter initialized");
  }

  @Override
  public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, 
      FilterChain filterChain) throws IOException, ServletException {
    System.out.println("request received in TestFilter");
    filterChain.doFilter(servletRequest, servletResponse);
    return;

  }

  @Override
  public void destroy() {
    System.out.println("LocalFilter destroyed");
  }
}
These two components haven't been defined in the web.xml. If we run the servlet app now, then neither of the above classes will work. With Servlet 3.0 we have a new feature - Pluggability.
Servlet specs provide us with the ability to add definitions dynamically.
The first step is to add a ServletContext Listener.
<listener>
   <listener-class>
      com.listener.PluggabilityListener
   </listener-class>
</listener>
Now to look into my Listener class:
public class PluggabilityListener implements ServletContextListener {

  @Override
  public void contextDestroyed(ServletContextEvent arg0) {

  }

  @Override
  public void contextInitialized(ServletContextEvent sce) {
    ServletContext sc = sce.getServletContext();
   javax.servlet.ServletRegistration.Dynamic dynamicServlet 
             = sc.addServlet("myServlet", TestServlet.class);
   dynamicServlet.addMapping("*.do","/pages/*");

   EnumSet<DispatcherType> enumset = EnumSet.of(DispatcherType.REQUEST);
   javax.servlet.FilterRegistration.Dynamic dynamicFilter = 
       sc.addFilter("myServlet", TestFilter.class);
   dynamicFilter.addMappingForServletNames(enumset,true,"myServlet");

   dynamicFilter.addMappingForUrlPatterns(null, true, "*.jsp");
   
  }

}
On initialization of the Servlet context, the contextInitialized method is called. Here we addServlet method of the ServletContext class. The parameters are the servlet name (one we use in web.xml) and the servlet class. The method returns an instance of Dynamic class. This instance can be used to initialize init parameters, set loadOnStartUp value among other features. Here we have used it to set the mappings for the servlet. In this case we have added mappings for "*.do" and "/pages/*" Similar is the code for Filter. With Filter we can add mapping identical to the Servlet. This is done with the addMappingForServletNames method. The first parameter is the Dispatcher type which has been set to Request. On starting the server:
TestFilter initialized
INFO: Server startup in 654 ms
I decided to test the code. On hitting Url "http://localhost:8080/WebFragments/pages/hy" both the filter and the servlet were invoked. Same is the case for "http://localhost:8080/WebFragments/p.do" If I test for "http://localhost:8080/WebFragments/index.jsp", the request was received only by the filter.
Is this feature useful ?
To be honest I cant think of a general use case in application development where I would like to configure a servlet or a filter in this manner. Maybe for some frameworks where depending on some configuration parameters we set up the servlets  and filters.

No comments:

Post a Comment