bookmark_borderMigrating from Spring 3.2.x to Spring 4 and using ‘spring-mock 2.0.8’ gives “java.lang.NoSuchMethodError: org.springframework.core.CollectionFactory.createLinkedMapIfPossible”

So this is a very short post, with a ‘gotcha’. I wasn’t able to find anything about this, thats why I write it down here right now:

If you are migrating from Spring 3 to 4 and you have in your pom.xml the following dependency:

    <properties>
        <spring.version>3.2.4.RELEASE</spring.version>
        <junit.version>4.9</junit.version>
    </properties>
...
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-mock</artifactId>
            <version>2.0.8</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
            <scope>test</scope>
        </dependency>

Once you migrate to Spring 4 (lets say 4.0.3.RELEASE) and run your tests you might run into a following stacktrace:

java.lang.NoSuchMethodError: org.springframework.core.CollectionFactory.createLinkedMapIfPossible(I)Ljava/util/Map;
	at org.springframework.mock.web.MockHttpServletRequest.<init>(MockHttpServletRequest.java:107)
	at org.springframework.mock.web.MockHttpServletRequest.<init>(MockHttpServletRequest.java:210)
	at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:171)
	at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:100)
	at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:319)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:212)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:289)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:232)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:89)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
	at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
	at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:292)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:175)
	at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:252)
	at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:141)
	at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:112)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:483)
	at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189)
	at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165)
	at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85)
	at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:115)
	at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:75)

Then all you need to do is make sure that you *DO NOT* have ‘spring-mock’ still in your dependencies configured. As it seems that ‘spring-test’ has assimilated this in its own JAR in Spring 4.

Remove from POM.xml, re-run tests and be happy again. It took me a while to figure this out. I hope it saved you some time!

bookmark_borderGotcha’s showing error messages with Spring forms (form:errors)

(I have recently encountered this with Spring 3.1)

Want to validate your forms using Spring? Are you using the form tag and bind it with an object? Got the validator working? But still you just can’t seem to get these error messages showing up? Here is a gotcha that might help you out!

Consider this controller:

@Controller
public class FormController {

    @RequestMapping(&quot;/form&quot;)
    public ModelAndView handleGet(@Valid TellAFriendForm backingForm, BindingResult bindingResult) {
        ModelAndView modelAndView = new ModelAndView(&quot;backingForm&quot;);
        modelAndView.addObject(&quot;backingForm&quot;, backingForm);
        modelAndView.addObject(&quot;result&quot;, bindingResult);
        return modelAndView;
    }

}

With this jsp:

&lt;form:form method=&quot;POST&quot; commandName=&quot;myForm&quot; action=&quot;?&quot;&gt;
    &lt;div&gt;
        &lt;label for=&quot;name&quot;&gt;Name*&lt;/label&gt;
        &lt;form:input path=&quot;name&quot; id=&quot;name&quot;/&gt;
        &lt;form:errors path=&quot;name&quot;/&gt;
    &lt;/div&gt;
    &lt;div&gt;
        &lt;input type=&quot;submit&quot; value=&quot;Send&quot;/&gt;
    &lt;/div&gt;
&lt;/form:form&gt;

When the user submits this form, the form should be validated. Whenever a field does not validate, it should inform the user about this.

This is done by using:

        &lt;form:errors path=&quot;name&quot;/&gt;

Where ‘path’ refers to a field in the bound form object, in this case myForm. In this example, I have named my form “myForm”. The theory is that when the form is being validated, the BindingResult (put as “result” on the model), contains all fields that have validation errors. Using the form:errors tag you can make use of that and show error messages.

Although this works often, sometimes it does not and you spend some time figuring out why. A common gotcha is that you forget to put the bindingResult on your model (as “result”).

But there is another less intuitive gotcha.

When your given commandName in the form:form tag does not represent the camelcased, name of the type of the form, the form:errors tag will *not* work. So don’t make up your own name. Make sure it is the same as the type, and make sure it is camelcased correctly. I figured this out the hard way.

Solution: The form object is of type: BackingForm, the expected name would then be backingForm. Change the commandName into backingForm and you will see the form error messages showing up.

bookmark_borderPrevent cross-site scripting when using JSON objects using ESAPI and Jackson framework 1.7.x

Recently I have had the opportunity to fix a cross-site-scripting problem.

The problem: a lot of JSON objects are being sent over the wire and the data is not being html escaped. This means that anyone who would put html data IN would get it out and make any user vulnerable for XSS attacks.

In this case, JSON objects are being created by using the MappingJacksonHttpMessageConverter. This is deliverd by the Spring framework. Normally it is instantiated when you use spring-mvc (using the mvc-annotation tag). This allowed us to just return an object and the MappingJacksonHttpMessageConverter would take care of translating it into a JSON object.

In order to influence the creation of the JSON object and make it encode HTML for String values we need to do a few things:

  1. Create a Custom Object Mapper and wire into Spring
  2. Create a JsonSerializer that encodes HTML (using ESAPI)
  3. Register the JsonSerializer in the CustomObjectMapper

Create a Custom Object Mapper and wire into Spring

However, now we want to change the value serialized by the MappingJacksonHttpMessageConverter. So how do we do that? The MappingJacksonHttpMessageConverter uses an ObjectMapper. So in order to get the ObjectMapper map the values to an encoded one we need to create a custom Object mapper.

public class CustomObjectMapper extends org.codehaus.jackson.map.ObjectMapper {
}

At first we leave it empty. We want to make Spring make use of our CustomObjectMapper first. In order to do that you need to write out the mvc-annotation tag, because we need to inject our CustomObjectMapper in the the MappingJacksonHttpMessageConverter. This is a bit difficult to figure out at first, but for your convinience I have proveded the configuration you need.

<bean id="CustomObjectMapper" class="your custom object mapper"/>

<bean id="MappingJacksonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
	<property name="objectMapper" ref="CustomObjectMapper"/>
</bean>

<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
	<property name="order" value="1" />
	<property name="customArgumentResolver" ref="sessionParamResolver"/>
	<property name="webBindingInitializer">
	<bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
		<!-- <property name="conversionService" ref="conversionService" />  -->
		<property name="validator" ref="validator" />
	</bean>
	</property>
	<property name="messageConverters">
		<list>
			<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter" />
			<bean class="org.springframework.http.converter.StringHttpMessageConverter" />
			<bean class="org.springframework.http.converter.ResourceHttpMessageConverter" />
			<bean class="org.springframework.http.converter.FormHttpMessageConverter" />
			<ref bean="MappingJacksonHttpMessageConverter"/>
		</list>
	</property>
</bean>

As you can see, the AnnotationMethodHandlerAdapter is the bean where everything is getting wired into. The messageConverters property contains a list of converters. The last entry is where the MappingJacksonHttpMessageConverter is referenced. Normally it is defined like the beans above it. But since we want to inject our own CustomObjectMapper it is done this way.

Create a JsonSerializer that encodes HTML (using ESAPI)

Now we know our CustomObjectMapper is used, we can start the part where we want to influence the way the jackson framework serializes our objects to JSON. In order to do that we create our own JsonSerializer. Then, we override the serialize method and make sure the provided jsonGenerator is writing our encoded value.

Code:

public class JsonHtmlXssSerializer extends JsonSerializer {

   public void serialize(String value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
      if (value != null) {
         String encodedValue = encodeHtml(value);
         jsonGenerator.writeString(encodedValue);
      }
   }

}

So this class extends from org.codehaus.jackson.map.JsonSerializer, and implements its own serialize method. The method encodeHtml will use the ESAPI encoding functions to escape HTML.

 

Implementing this is very easy:

   protected String encodeHtml(String html) {
      DefaultEncoder encoder = new DefaultEncoder();
      return encoder.encodeForHTML(html);
   }

The defaultEncoder is from ESAPI allowing us to encode the HTML.

 

Register the JsonSerializer in the CustomObjectMapper

The final step is to register the JsonSerializer within the ObjectMapper used by Spring. Doing that using Jackson 1.7.0 is very easy. Using the provided SimpleModule class we can easily add our own Serializer to the ObjectMapper. We do that by creating our own default constructor, which will register our serializer. Like so:

@Component
public class CustomObjectMapper extends org.codehaus.jackson.map.ObjectMapper {

   public CustomObjectMapper() {
      SimpleModule module = new SimpleModule("HTML XSS Serializer", new Version(1, 0, 0, "FINAL"));
      module.addSerializer(new JsonHtmlXssSerializer());
      this.registerModule(module);
   }
}