Monday, April 27, 2015

Setting up Undertow with Spring MVC

In case you still don't know about it, Undertow is web server developed under JBoss community, and is praised a lot for its impressive performance. I usually deployed my web application in .war form under some servlet container (mostly Tomcat), but now I wanted to try out embedded servlet container, and Undertow had seemed especially good for that considering its small footprint.

I am also heavy Spring user, so I wanted to integrate this web server with Spring MVC, but only examples so far where I could find Spring-Undertow integration is within Spring Boot project, and for some reason I still use plain Spring Framework, so I set out to do it my own.

Of course, as everything in Spring is a bean, I developed my own UndertowServer bean which was pretty straightforward, and bigger question was how to define my web application deployment in it because there are couple of ways to do it. I decided to use Servlet 3.0+ ServletContainertInitializer (one other option would be to straight use Undertow's API to specify web application deployment).

Here is piece of code from our implementation of this interface:
 public class WebAppServletContainerInitializer implements ServletContainerInitializer, ApplicationContextAware {  
   private ApplicationContext applicationContext;  

   @Override  
   public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {  
     XmlWebApplicationContext rootWebAppContext = new XmlWebApplicationContext();  
     rootWebAppContext.setConfigLocation("/WEB-INF/applicationContext.xml");  
     rootWebAppContext.setParent(applicationContext);  
     ctx.addListener(new ContextLoaderListener(rootWebAppContext));
  
     FilterRegistration.Dynamic encodingFilter = ctx.addFilter("encoding-filter", CharacterEncodingFilter.class);  
     encodingFilter.setInitParameter("encoding", "UTF-8");  
     encodingFilter.setInitParameter("forceEncoding", "true");  
     encodingFilter.addMappingForServletNames(EnumSet.allOf(DispatcherType.class), false, "admin");  

     FilterRegistration.Dynamic springSecurityFilterChain = ctx.addFilter("springSecurityFilterChain", DelegatingFilterProxy.class);  
     springSecurityFilterChain.addMappingForServletNames(EnumSet.allOf(DispatcherType.class), false, "admin");  
     ServletRegistration.Dynamic dispatcher = ctx.addServlet("admin", DispatcherServlet.class);  
     dispatcher.setLoadOnStartup(1);  
     dispatcher.addMapping("/admin/*");  
 ...  
 ...  

We had to inject our main ApplicationContext (that contains UndertowServer bean) via ApplicationContextAware marker, to be able to use it as parent context for root WebApplicationContext that we put under /WEB-INF/ together with other DispatcherServlet contexts.

Later on, we use this ServletContainertInitializer implementation during startup phase of Undertow server to construct DeploymentInfo object:
 InstanceFactory<? extends ServletContainerInitializer> instanceFactory = new ImmediateInstanceFactory<>(servletContainerInitializer);  
 ServletContainerInitializerInfo sciInfo = new ServletContainerInitializerInfo(WebAppServletContainerInitializer.class, instanceFactory, new HashSet<>());  

 DeploymentInfo deploymentInfo = constructDeploymentInfo(sciInfo);  

FInal result is that we can now readily use this web server bean in our Spring XML deployment descriptors, such as:
 <bean class="vmarcinko.undertow.UndertowServer">  
   <property name="port" value="8080"/>  
   <property name="webAppName" value="myapp"/>  
   <property name="webAppRoot" value="${distribution.dir}/web-app-root"/>  
   <property name="servletContainerInitializer">  
     <bean class="vmarcinko.web.admin.WebAppServletContainerInitializer"/>  
   </property>  
 </bean>  

If you ask me why I still use Spring XML in 2015 instead of Java config - well, I think XML is still nicer DSL for describing deployment than Java, but that's just me :)

Anyway, when you boot the system, this little web server will be up and running under specified port, serving this single web application under given root directory. In the example above, my administration web console (specific DispatcherServlet serving that under '/admin/*') would be available under:

http://localhost:8080/myapp/admin

Whole code for both classes is available at this Gist.



17 comments:

  1. Hello Vjeran Marcinko,

    I'm trying your code downloaded from Gist. I created a main class to start the Spring application context with the following line:

    ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("file:" + appPath + "/config/rootContext.xml");

    After creating web-app-root in the project dir and passing -Ddistribution.dir=/full/path/to/my/project and creating WEB-INF inside web-app-root and there the requested (by the application) xml files (applicationContext.xml, admin-servlet.xml and customer-servlet.xml), I'm getting the following error:

    DEBUG org.springframework.web.filter.DelegatingFilterProxy - Initializing filter 'springSecurityFilterChain'
    ERROR io.undertow.request - UT005023: Exception handling request to /myapp/admin
    org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'springSecurityFilterChain' is defined

    I'm not pretty sure what to do now. Could you give me a hint? Could you put a full project in Gist, instead of just the two classes? That way it will be easer to understand your example which, by the way, it's the most complete I've found on the net until now.

    Regards.

    ReplyDelete
    Replies
    1. I have bunch of my custom-related stuff in it, so I don't have time right now to pull it out of the project, but you just go on setting Spring MVC and Security the usual way...
      In you case, it seems the problem is that you haven't set up Spring Security properly, meaning, you should have something like special securityContext.xml referenced from some of your higher-level cotexts (something like applicationContext.xml) and use special Spring Security namepsace support to set up required beans.

      Delete

  2. Nice to see your blog post.. I really enjoyed by reading your blog post. Thanks a lot for sharing this with us.. Keep on sharing like this informative post.

    Salesforce Training in Chennai

    ReplyDelete
  3. This is a great article, I have been always to read something with specific tips! I will have to work on the time for scheduling my learning.
    Jobs in Kolkata
    Jobs in Mumbai

    ReplyDelete
  4. Thank you for sharing such a nice and interesting blog with us. I have seen that all will say the same thing repeatedly. But in your blog, I had a chance to get some useful and unique information. I would like to suggest your blog in my dude circle.
    Jobs in Chennai
    Jobs in Bangalore
    Jobs in Delhi
    Jobs in Hyderabad
    Jobs in Kolkata
    Jobs in Mumbai
    Jobs in Noida
    Jobs in Pune

    ReplyDelete
  5. very nice blog this course very useful to us this gives the big opportunity to study course details us i like this information very much.

    Informatica Training in Chennai

    ReplyDelete
  6. Thank you for sharing such a nice and interesting blog with us. But in your blog, I had a chance to get some useful and unique information. I would like to suggest your blog.
    Hyperpigmentation cream
    Viral Fever Medicines

    ReplyDelete
  7. It is really very excellent,I find all articles was amazing.Awesome way to get exert tips from everyone,not only i like that post all peoples like that post.Because of all given information was wonderful and it's very helpful for me.
    SAP Training in Chennai
    SAP ABAP Training in Chennai
    SAP FICO Training in Chennai
    SAP MM Training in Chennai

    ReplyDelete
  8. Its really an Excellent post. I just stumbled upon your blog and wanted to say that I have really enjoyed reading your blog. Thanks for sharing....

    Carwash in omr
    usedcars in omr
    automotors in omr
    car accessories in omr
    secondhand car in omr

    ReplyDelete
  9. Thanks for the informative article.This is one of the best tips in my life. I have in quite some time.Nicely written and great info.I really cannot thank you enough for sharing.

    Apartments in Chennai

    ReplyDelete

  10. As you have now understood the usage of ‘Record and Playback’ tool, the following are the different posts using which you can explore the functioning of ‘Selenium IDE’
    selenium Training in chennai

    ReplyDelete
  11. Wonderful blog.. Thanks for sharing informative Post. Its very useful to me.

    dailyconsumerlife
    Guest posting sites

    ReplyDelete