Resource Redirection in Spring Pet Clinic Application for Tomcat 7 and Cloud Platforms

In previous months I’ve been playing with various cloud platforms, learning the basics, what’s different and what not, between them and comparing with more ‘traditional’ developments.

When I start to work in a new framework or tool, I tend to use the same set of reference applications to start. Simple stuff for a simple start. With that I pretend to concentrate in  the specifics of the f/t at hand, without dealing at the same time with whatever idea I had and was building.

The first app, as you can see in previous posts, is the simplest of Spring+Hibernate use cases, CRUD on a simple, two-field entity. This one is good to start but too simple to be really representative o an actual development.

For the second iteration I work with Spring Pet Clinic reference application: an exemplar use of Spring Framework created by Spring team a few years ago. To my surprise, Pet Clinic didn’t work out of the box with the latest Tomcat release, and while looking at what was happening I found out a few things worth sharing about the greatest and latest Spring and Tomcat.

In this post I will walkthrough my findings with Pet Clinic and what enhancements I did to make it ready for 2012 and beyond.

1. Downloading Pet Clinic

If you are following this post maybe you would like to work with the code as the post progresses. To get a copy of the latest Pet Clinic code just checkout this Subversion repository with your favorite tool:

https://src.springframework.org/svn/spring-samples/petclinic/trunk/

The latest revision at the time I’m writing this is 616, and includes metadata to import directly into Eclipse and Maven project model describing all dependencies needed to build the application, package and deploy it.

C:\petclinic>svn co https://src.springframework.org/svn/spring-samples/petclinic/trunk
A trunk\.classpath
A trunk\.project
A trunk\src
A trunk\src\test
A trunk\src\test\java
A trunk\src\test\java\org
A trunk\src\test\java\org\springframework
A trunk\src\test\java\org\springframework\samples
A trunk\src\test\java\org\springframework\samples\petclinic
A trunk\src\test\java\org\springframework\samples\petclinic\hibernate
A trunk\src\test\java\org\springframework\samples\petclinic\hibernate\HibernateClinicTests.java
...
A trunk\.settings\org.eclipse.wst.common.component
A trunk\.settings\org.springframework.ide.eclipse.core.prefs
 U trunk
Revisión obtenida: 616
C:\petclinic>cd trunk
C:\petclinic\trunk>mvn compile
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Building petclinic
[INFO] task-segment: [compile]
[INFO] ------------------------------------------------------------------------
[INFO] [resources:resources {execution: default-resources}]
[WARNING] Using platform encoding (ISO-8859-1 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 16 resources
[INFO] [compiler:compile {execution: default-compile}]
[INFO] Compiling 41 source files to C:\petclinic\trunk\target\classes
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3 seconds
[INFO] Finished at: Thu Sep 06 17:25:54 CEST 2012
[INFO] Final Memory: 21M/213M
[INFO] ------------------------------------------------------------------------

2. Where are my resources?

I did as usual: grabbed the latest Pet Clinic, built, packaged and deployed it into the servlet container of choice. The thing that was different this time is that it was the first time I was using Tomcat 7, while previously I used Tomcat 6.

This time, when I accessed the application, I found out that pages were not properly rendered: images, styles and scripts were missing as if they were not located by the container. Weird. I came back to my old Tomcat 6 installation, same War, and everything worked fine. Time for some forensics.

It turns out that Pet Clinic uses Tomcat’s default servlet to access static resources. The default servelt, as can be read in Tomcat’s documentation, “is the servlet which serves static resources as well as serves the directory listings”. By default it is mapped to “/” URL but Pet Clinic, in its web.xml file, redefines the mapping to “/static/*”. So, in short, when Pet Clinic asks for “/static/path/something.ext” the default servlet will return “path/something.ext”, relative to Pet Clinic’s web context root.

        <!--
	 - Map static resources to the default servlet
	 - examples:
	 -     http://localhost:8080/static/images/pets.png
	 -     http://localhost:8080/static/styles/petclinic.css
	-->
	<servlet-mapping>
		<servlet-name>default</servlet-name>
		<url-pattern>/static/*</url-pattern>
	</servlet-mapping>

However, this redirection for static resources is not working in Tomcat 7. Actually is not working with modern Tomcat 6 builds as well, so maybe it is a change in Catalina’s default servlet implementation to reduce the security risk that is to have such a powerful redirection mechanism active by default. This mechanism is so powerful that is also capable of exposing the web.xml file through an HTTP GET request as can be seen in the screenshots below.

Fig 1. Pet Clinic with static resource redirection working on Tomcat 6.0.20.

Fig 2. Pet Clinic exposing internals due to the default servlet resource redirection mechanism in older Catalina container versions.

Fig 3. Default servlet redirection not working in Apache 6.0.35.

By the time of finished writing this, I have verified this behavior with the following containers:

  • Tomcat 6.0.20: working – security hole is present
  • Tomcat 6.0.35: not working
  • Tomcat 7.0.30: not working
  • Jetty 7.2.2: not working
  • Jetty 7.5.4: not working
  • Jetty 8.1.3: not working
  • JBoss 6.1.0: working – security hole is present (*)
  • JBoss 7.1.1: working – security hole is present (*)
  • Glassfish 3.1.1: working – security hole is present (**)

(*) Requires Pet Clinic to update Spring dependencies to 3.0.3 or above first, due to this bug: https://jira.springsource.org/browse/SPR-7197

(**) Default servlet is not active by default in Glassfish. Requires explicit declaration of default servlet in web.xml (see comments inside Pet Clinic’s web.xml for more information).

Whatever the root cause is, a bug, a change of implementation to reduce a security risk or any other, the important fact is that we need a new resource redirection mechanism, ideally one that is not dependent on a container implementation, that is secure and easy to configure, and fortunately for us Spring MVC Framework, starting with 3.0, provides with one.

3. Spring MVC resource redirection

Starting with Spring 3.0, the MVC framework comes with a resource redirection mechanism. Later starting with version 3.0.4, the mechanism was improved to support multiple locations.

To activate the mechanism, these are the changes that were needed in Pet Clinic application.

a) Update dependencies to Spring in Maven’s POM

This is a simple change as Spring’s version is configured as a build property, relieving us from the task of finding and changing the version in any single dependency with a Spring artifact. Open trunk/pom.xml file, find <properties> section and change spring.version to 3.0.6.RELEASE:

...
    <properties>
        <spring.version>3.0.6.RELEASE</spring.version>
...

b) Deactivate default servlet resource redirection in web.xml

Next step is to deactivate the default servlet resource redirection. Open trunk/src/main/webapp/WEB-INF/web.xml, find the servlet mapping section and remove or comment it:

...
    <!--
    <servlet-mapping>
         <servlet-name>default</servlet-name>
         <url-pattern>/static/*</url-pattern>
    </servlet-mapping>
    -->
...

c) Activate MVC resource redirection

Next step is to activate MVC resource redirection. Open trunk/src/main/webapp/WEB-INF/petclinic-servlet.xml and make the following changes:

c.I) Add MVC schema definition to beans root XML element:

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:oxm="http://www.springframework.org/schema/oxm"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation=
"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm-3.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
...

c.II) Activate MVC annotation driven configuration

Next, we need to activate MVC annotation driven configuration:

<mvc:annotation-driven/>

Now that MVC is configured to work with annotations, we should deactivate the web binding bean that now has no effect:

<!-- <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
    <property name="webBindingInitializer">
        <bean class="org.springframework.samples.petclinic.web.ClinicBindingInitializer"/>
    </property>
</bean> -->

c.III) Activate static resource mapping

Next, activate static resource mapping adding the following definition:

<mvc:resources mapping="/static/**" location="/"/>

c.IV) Activate URL handler mapping

At this point, we may think that we are done. If we run the application at this point we will find out that MVC is not mapping some URLs to the corresponding controller actions or views. To activate this with MVC running in annotation mode, we should explicitly declare this URL handler mapping:

<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />

This way we can mix annotation driven URL mappings with bean name based mappings (the ‘old’ style) within the same Spring context. Pet Clinic code actually mixes both styles and that’s why this is needed.

d) Configuring web bindings to work again

As a side effect of activating MVC annotation driven configuration we’ve lost the web bindings bean definition. Web bindings are used to map web form fields to fields in classes, adding validations, special formats and type matches in a very convenient way. Pet Clinic registers web bindings within the class org.springframework.samples.petclinic.web.ClinicBindingInitializer:

public class ClinicBindingInitializer implements WebBindingInitializer {
    @Autowired
    private Clinic clinic;
    public void initBinder(WebDataBinder binder, WebRequest request) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        dateFormat.setLenient(false);
        binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
        binder.registerCustomEditor(String.class, new StringTrimmerEditor(false));
        binder.registerCustomEditor(PetType.class, new PetTypeEditor(this.clinic));
    }
}

To bring this functionality back, the best way I’ve found out (and please, correct me if you know of anything better compatible with MVC annotation drive configuration) is to add the binding initialization to each one of the form classes (all in folder trunk/src/main/java/org/springframework/samples/petclinic/web):

  • AddOwnerForm
  • AddPetForm
  • AddVisitForm
  • EditOwnerForm
  • EditPetForm

In all of them, we should add a @InitBinder public void initBinder(WebDataBinder binder, WebRequest request) method, which will include the bindings relevant to each form.

Let’s see the addition class by class:

d.I) AddOwnerForm

@InitBinder
public void initBinder(WebDataBinder binder, WebRequest request) {
    binder.registerCustomEditor(String.class, new StringTrimmerEditor(false));
}

d.II) AddPetForm

@InitBinder
public void initBinder(WebDataBinder binder, WebRequest request) {
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
    dateFormat.setLenient(false);
    binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
    binder.registerCustomEditor(String.class, new StringTrimmerEditor(false));
    binder.registerCustomEditor(PetType.class, new PetTypeEditor(this.clinic));
}

d.III) AddVisitForm

@InitBinder
public void initBinder(WebDataBinder binder, WebRequest request) {
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
    dateFormat.setLenient(false);
    binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
    binder.registerCustomEditor(String.class, new StringTrimmerEditor(false));
}

d.IV) EditOwnerForm

@InitBinder
public void initBinder(WebDataBinder binder, WebRequest request) {
    binder.registerCustomEditor(String.class, new StringTrimmerEditor(false));
}

d.V) EditPetForm

@InitBinder
public void initBinder(WebDataBinder binder, WebRequest request) {
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
    dateFormat.setLenient(false);
    binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
    binder.registerCustomEditor(String.class, new StringTrimmerEditor(false));
    binder.registerCustomEditor(PetType.class, new PetTypeEditor(this.clinic));
}

4. Summary of changes

With these changes done, Pet Clinic is ready to be rebuilt and redeployed, with resource redirection working in Tomcat 6 (old and modern), Tomcat 7 and also in Cloud Foundry. Moreover, this alternative way to enable resource redirection in Java web applications reduces security risks and it is applicable to other containers as JBoss 6 and JBoss 7.

What we’ve done is, in highlights:

  • Update Spring dependency to 3.0.4 or later version.
  • Deactivate the old resource redirection mechanism based on Catalina’s default servlet.
  • Activate Spring MVC annotation driven configuration and MVC resource redirection.
  • Fix web bindings, broken after introducing annotation driven configuration.

These changes should be, in my opinion, a best practice for web application design. Resource redirection based on the default servlet mechanism exposes an important security vulnerability and should not be allowed in real, client-facing applications, even if the container of choice still allows for it.

Advertisement

8 thoughts on “Resource Redirection in Spring Pet Clinic Application for Tomcat 7 and Cloud Platforms

  1. In Tomcat 7 you can make petclinit app with less steps. In step “c.II) Activate MVC annotation driven configuration”, do not comment:

    <bean class=”org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter”>
    <property name=”webBindingInitializer”>
    <bean class=”org.springframework.samples.petclinic.web.ClinicBindingInitializer”/>
    </property>
    </bean>

    You can also avoid step “c.IV) Activate URL handler mapping”

    And finally, with previos step changes you do not need to modify any form (controller) class. So you can avoid step “d) Configuring web bindings to work again” completly.

    1. Hi, how do you make web bindings to work? In my case, as soon as I activated MVC annotation driven configuration, all MVC related items in the XML were, simply, ignored.

      1. I did nothing. I only inserted <mvc:annotation-driven/&gy; and <mvc:resources location=”/” mapping=”/static/**”/> at begining of petclinic-servlet.xml, commented servlet-mapping for “/static/*” url-pattern in web.xml and changed spring.version to 3.0.6.RELEASE in pom.xml. And now all the thing is working in Tomcat 7.0.30.

      2. I will check again with Tomcat 7.0.30. In my case enabling MVC annotation driven caused all other regular MVC config in XML to stop working. It should not be anything related with the container but who knows

      3. I second that. works with 6.0.35 too with minimum changes listed.

      4. Hi again. I’ve double checked the post. I’ve also started from scratch using Pet Clinic revision 627, Apache Tomcat 6.0.36 and 7.0.37 (the latest as of Today). I can confirm that *all* steps described in the post are required for Pet Clinic to work with resource redirection in these containers.

        Let me be a bit more precise about that. If you skip steps c.IV) and d), resource redirection will work but web bindings will *not* work. This means that you get the right forms but not the right data formats (date format, string lengths, etc.). If you add a visit, for example, the date format is not valid.

        I’ve added a few screenshots to show that:

        1) What happens in Add Visit Form when web bindings are not working.

        2) Error if you try to add the visit.

        3) Add Visit Form loads correctly once web bindings are added again.

        4) The visit can be added as expected.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s