Search code examples
spring-mvcapache-tilesweb-fragment

Apache Tiles + Spring MVC serves jsps inside jar files


Purpose
Design an "add-on" to a web project.

Web Project
I have a project packaged as WAR which uses Spring MVC 4.1.6 and Apache Tiles 3.0.5 as the UI framework. This a sample of the application-context:

<bean class="org.springframework.web.servlet.view.tiles3.TilesConfigurer" id="tilesConfigurer">
    <property name="definitions">
        <list>
            <value>/WEB-INF/foo/bar/layouts.xml</value>
            <value>classpath:/META-INF/ext/**/views.xml</value><!-- For add-ons -->
        </list>
    </property>
</bean>

JAR
I have another JAR, which is the "add-on". This jar will need to contain some jsp files. The general idea is, if I remove this jar from /WEB-INF/lib directory, all related features will be removed when I restart the web server. Likewise, all related features will be available when I put the jar into the lib directory. Sample tiles definition (views.xml):

<!DOCTYPE tiles-definitions PUBLIC "-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN" "http://tiles.apache.org/dtds/tiles-config_3_0.dtd">
<tiles-definitions>
    <definition extends="main" name="space">
        <!-- This does not work -->
        <put-attribute name="body" value="classpath:/META-INF/resources/index.jspx" />
    </definition>
</tiles-definitions>


1. Can the jsp files that resides in the JAR file be defined in the tiles definition?
2. Can this be achieved with/without using web-fragment?


Solution

  • Somehow, I managed to find the solution.

    1. Create a class that implements ServletContainerInitializer in your jar. Override method and add servlet mapping, something like this:

      @Override
      public void onStartup(final Set<Class<?>> clazzes, final ServletContext servletContext) throws ServletException {
          final ServletRegistration servletRegistration = servletContext.addServlet("name-of-servlet-declared-in-web.xml", DispatcherServlet.class);
          servletRegistration.addMapping("/name-of-tiles-definition");
      }
      
    2. In your tiles definition which resides in your jar, map the definition name added in #1.

      <tiles-definitions>
          <definition extends="main" name="name-of-tiles-definition">
              <put-attribute name="body" value="/path-to-resource/jsp-name.jspx" />
          </definition>
      </tiles-definitions>
      
    3. Create a file with name "javax.servlet.ServletContainerInitializer" in "/META-INF/services" directory. Inside this file should contain the FQDN of ServletContainerInitializer implementation.

    4. As for your JSPs, put them under the "/META-INF/resources/" directory. If for instance your JSPs are placed under "/META-INF/resources/example", in #2, the value for the "put-attribute" element would be "/example/jsp-name.jspx".

    5. Lastly, create your web-fragment.xml file in "/META-INF" directory. My web-fragment.xml contains nothing but only the "display.name" element. I am not sure if this file is necessary but since it worked for me I figured I'll just let it live in my hard drive.

    In my jar, I have Controllers and i18n properties files as well, which is defined in the servlet-context, loaded and initialized in the main web.xml.

    Let me know if this works for you. HTH.