Category: Spring
For my current client I am involved in the process of creating a list with allowed, deprecated and blacklisted frameworks and dependencies. I`ve noticed that there are a lot of internal projects that copy dependencies and framework choices from previous projects and are therefore using old and deprecated libraries. We are trying very hard to get projects to use standard APIs and get rid of the old stuff.
Frameworks like Spring, Google Guice, Seam, etc, are also moving toward standard APIs. Spring 3.0 embraces a lot of standards APIs like JSR303, JSR330, JSR317, which in my opinion is a good thing!
Why move to these new APIs?
It allows developers to focus on one API instead of learning a specific API for each framework. Switch or to other/competing frameworks or upgrading becomes easier. For example, the gap between Spring 3 and Java EE 6 (CDI + EJB3) has become much smaller due to these new standards. A good example were standardisation helps is the JSR 303 Bean Validation API which is supported by both Spring, Hibernate/JPA, JSF, etc.
Springmodules validation
In Spring 3 the JSR 303 Beans Validation API support was introduced which effectively replaces the springmodules validation project. Users of Spring 3 are encouraged to use this new JSR 303 Beans Validation API.
More information
When you want to leverage the standard API in your project, please have a look at the following articles:
http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/validation.html#validation-beanvalidation
http://www.openscope.net/2010/02/08/spring-mvc-3-0-and-jsr-303-aka-javax-validation/
Spring Dependency Injection annotations
Spring and Google have been working on a standardisation for Dependency Injection annotations, result: the JSR 330 specification. This is a standard API for doing annotation based dependency injection. Spring supports this API since Spring 3.0 (http://blog.springsource.com/2009/09/29/spring-framework-3-0-rc1-released/).
More information
Have a look at the following articles:
http://code.google.com/p/atinject/
http://ralf.schaeftlein.de/2009/11/19/spring-3-0-rc-2-and-jsr-330-dependency-injection-for-java/
Hibernate vs Java Persistence API
The Java Persistence API (JPA) was introduced as part of the Java EE 5 and EJB 3 specification. JPA is in a way the formalisation of Hibernate in a Java Specification (JSR). Since the makers of Hibernate were actively involved in this specification there are numberous of simularities between Hibernate and JPA.
Differences between Hibernate and JPA
JPA defines the standard API for using persistence in Java. You can use Hibernate as a JPA implementation. When using JPA developers can leverage the power of a standardised persistence API and still being able to use Hibernate specific functionality if required. But it opens up possibilities to move to EclipseLink or some other JPA implementation.
Hibernate 3.5 – JPA 2.0
In Hibernate 3.5 a lot of Hibernate subprojects have been merged back into the Hibernate core. This makes it easier to configure and use JPA in conjunction with Hibernate. The JSR 303 API was not officially included as a requirement for JPA 2.0 but Hibernate integrates nicely with the validation specification.
Conclusion
There are a lot of examples that demonstrate the benefits of using standard JSR APIs whenever possible. I would advice to use standard APIs whenever possible and only use specific framework functionality when the standards do not provide enough functionality.
Last month I blogged about the lacking gzip support in the Spring Android RestTemplate. Some collegues at my current client were struggeling invoking gzip encoded RESTful services using Spring 3.0 Web MVCs RestTemplate. While searching the web they found my blog post about Spring RestTemplate and the gzip troubles and decided to ask me for some help
In this article I will explain how to unmarshall a gzip encoded response from RESTful service that uses XML messages.
gzip encoded content
When invoking RESTful services from a client you typically specify in the client request, using HTTP request headers, what the Accept type is (JSON or XML) and content encoding you support as a client. The RESTful service inspects the HTTP request headers and returns the requested content (if supported). This proces is called content negotiation. Have a look at a sample request below:

Not all services support different types of content encoding and/or return types. Sometimes you find yourself in a situation that you have to invoke services that return specific content types or encoding formats, for example compressed content using gzip compression.
In the HTTP Accept-Encoding header you specify the content encoding you are supporting. When invoking services that use gzip encoding you need to specify the Accept-Encoding: gzip,deflate in the request headers. Spring RestTemplate using commons-httpclient does not add this by default and disallows you to invoke services that requires a client supporting gzip encoding, this typically results in a HTTP 400 Bad Request. In this blog I described an option to solve this problem in the RestTemplate, but thats only part of the solution.
Guess what happens when you invoke a RESTful service using Spring Web MVCs RestTemplate and the content body contains gzip encoded content? You are out of luck… or not? Read on…
Gzip Enabled MarshallingHttpMessageConverter
The RESTful service my collegues were invoking returns XML data in the response body. So the return value of the RESTful service is an XML message, this message will be unmarshalled using an XML unmarshaller like JAXB. The way to do this using Spring Web MVCs RestTemplate is by adding a MessageConvertor called MarshallingHttpMessageConverter to your RestTemplate and you are good to go.
The MarshallingHttpMessageConverter takes care of marshalling and unmarshalling messages passing between the client and RESTful service. But if your service returns gzip encoded content this is not enough. Because the text/xml content of the response body is gzip encoded we first need to deflate the content before JAXB2 is able to unmarshall the content. The response below contains response content that is gzip encoded. The tool I used, SoapUI, already deflated the content for me, but when invoking with the RestTemplate you have to do this yourself:

I created a GzipEnabledMarshallingHttpMessageConverter that supports both gzip and non-gzip encoded content. This message convert only works for XML messages that need to be (un)marshalled. For JSON services you need to implement a different solution. In the MessageConverter I perform a check to see if the Content-Encoding actually is gzip encoded, if so, I use a GZipStreamSource to read the Gzip compressed content from the InputStream of the response body.
The implementation below is a combination of the Spring MarshallingHttpMessageConverter and the AbstractXmlMessageConverter. I had to extend the AbstractHttpMessageConverter because the StreamSource is created within this class.
package com.oudmaijer.rest.spring.xml.message.converter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.oxm.Marshaller;
import org.springframework.oxm.MarshallingFailureException;
import org.springframework.oxm.Unmarshaller;
import org.springframework.oxm.UnmarshallingFailureException;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import org.springframework.util.Assert;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.IOException;
import java.util.List;
import java.util.zip.GZIPInputStream;
/**
* Add gzip decompression support for HttpResponse body's that are encoded with gzip.
* <p/>
* Implements the AbstractHttpMessageConverter in the same way as
* the Spring AbstractXmlMessageConverter and adds the MarshallingHttpMessageConverter
* functionality to it.
*
* @param <T>
*/
public class GzipEnabledMarshallingHttpMessageConverter<T> extends AbstractHttpMessageConverter<T> {
private final TransformerFactory transformerFactory = TransformerFactory.newInstance();
private Marshaller marshaller;
private Unmarshaller unmarshaller;
/**
* Protected constructor that sets the {@link #setSupportedMediaTypes(java.util.List) supportedMediaTypes}
* to {@code text/xml} and {@code application/xml}, and {@code application/*-xml}.
*/
public GzipEnabledMarshallingHttpMessageConverter(Jaxb2Marshaller marshaller, Jaxb2Marshaller unmarshaller) {
super(new MediaType("application", "xml"), new MediaType("text", "xml"), new MediaType("application", "*+xml"));
Assert.notNull(marshaller, "marshaller must not be null");
Assert.notNull(unmarshaller, "unmarshaller must not be null");
this.marshaller = marshaller;
this.unmarshaller = unmarshaller;
}
@Override
public final T readInternal(Class<? extends T> clazz, HttpInputMessage inputMessage) throws IOException {
StreamSource streamSource = null;
boolean gzip = false;
HttpHeaders headers = inputMessage.getHeaders();
List<String> contentEncoding = headers.get("Content-Encoding");
if( headers != null && contentEncoding != null ) {
for(String contentEncodingString : contentEncoding) {
if( contentEncodingString != null && "gzip".equalsIgnoreCase(contentEncodingString.trim()) ) {
gzip = true;
}
}
}
if( gzip ) {
streamSource = new GZipStreamSource(inputMessage.getBody());
} else {
streamSource = new StreamSource(inputMessage.getBody());
}
return (T) readFromSource(clazz, inputMessage.getHeaders(), streamSource);
}
@Override
protected final void writeInternal(T t, HttpOutputMessage outputMessage) throws IOException {
writeToResult(t, outputMessage.getHeaders(), new StreamResult(outputMessage.getBody()));
}
/**
* Set the {@link Marshaller} to be used by this message converter.
*/
public void setMarshaller(Marshaller marshaller) {
this.marshaller = marshaller;
}
/**
* Set the {@link Unmarshaller} to be used by this message converter.
*/
public void setUnmarshaller(Unmarshaller unmarshaller) {
this.unmarshaller = unmarshaller;
}
@Override
public boolean supports(Class<?> clazz) {
return this.unmarshaller.supports(clazz);
}
/**
*
* @param clazz
* @param headers
* @param source
* @return
* @throws IOException
*/
protected Object readFromSource(Class<?> clazz, HttpHeaders headers, Source source) throws IOException {
Assert.notNull(this.unmarshaller, "Property 'unmarshaller' is required");
try {
return this.unmarshaller.unmarshal(source);
} catch (UnmarshallingFailureException ex) {
throw new HttpMessageNotReadableException("Could not read [" + clazz + "]", ex);
}
}
/**
*
* @param o
* @param headers
* @param result
* @throws IOException
*/
protected void writeToResult(Object o, HttpHeaders headers, Result result) throws IOException {
Assert.notNull(this.marshaller, "Property 'marshaller' is required");
try {
this.marshaller.marshal(o, result);
} catch (MarshallingFailureException ex) {
throw new HttpMessageNotWritableException("Could not write [" + o + "]", ex);
}
}
}
The GZipStreamSource comes from an XML library created by Kohsuke Kawaguchi. I made some minor modifications to the code but the credits for this code goes to Kohsuke.
package com.oudmaijer.rest.spring.xml.message.converter;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.zip.GZIPInputStream;
import javax.xml.transform.stream.StreamSource;
/**
* {@link StreamSource} that reads from gzip-compressed XML stream.
* <p/>
* THIS SOFTWARE IS IN PUBLIC DOMAIN. NO WARRANTY.
*
* @author Kohsuke Kawaguchi (kk@kohsuke.org)
*/
public class GZipStreamSource extends StreamSource {
/**
* Creates a {@link StreamSource} from a gzip-compressed XML file.
*/
public GZipStreamSource(File file) throws IOException {
this(new FileInputStream(file), file.toURI().toURL().toExternalForm());
}
/**
* Creates a {@link StreamSource} from a gzip-compressed stream.
*/
public GZipStreamSource(InputStream stream) throws IOException {
super(new GZIPInputStream(stream));
}
/**
* Creates a {@link StreamSource} from a gzip-compressed stream.
* <p/>
* <p>This constructor allows the systemID to be set in addition
* to the input stream, which allows relative URIs
* to be processed.</p>
*/
public GZipStreamSource(InputStream stream, String systemId) throws IOException {
super(new GZIPInputStream(stream), systemId);
}
/**
* Creates a {@link StreamSource} from a gzip-compressed stream.
*
* @param url
* @throws IOException
*/
public GZipStreamSource(String url) throws IOException {
this(new URL(url).openStream(), url);
}
}
Spring configuration
And finally you have to configure your RestTemplate to use the new MessageConverter.
<bean id="httpClientFactory" class="org.springframework.http.client.CommonsClientHttpRequestFactory">
<constructor-arg ref="httpClient"/>
</bean>
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
<constructor-arg ref="httpClientFactory"/>
<property name="messageConverters">
<list>
<bean class="com.oudmaijer...GzipEnabledMarshallingHttpMessageConverter">
<constructor-arg ref="jaxbMarshaller"/>
<constructor-arg ref="jaxbMarshaller"/>
</bean>
...
</list>
</property>
</bean>
Conclusion
Using this MessageConverter you can invoke RESTful services that return gzip encoded content. Lets hope Spring adds it to the default message converters in the future because gzip compressed content is an elegant solution when you need to return large messages from a RESTful service.
The org.springframework.web.client.RestTemplate lacks support for gzip encoding. The RestTemplate is a class that is provided by the spring-android module for calling RESTfull services.
The Accept-Encoding header for gzip is not being added to the request by spring-android. I have added support for gzip to the RestTemplate and I created a patch for it. See the extended RestTemplate below:
package com.oudmaijer.android.rest;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.Assert;
import org.springframework.web.client.*;
import java.io.IOException;
import java.net.URI;
/**
* Adds support for gzip encoding to the Spring RestTemplate
*/
public class GzipRestTemplate extends RestTemplate {
/**
* {@inheritDoc}
*/
@Override
protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback, ResponseExtractor<T> responseExtractor) throws RestClientException {
Assert.notNull(url, "'url' must not be null");
Assert.notNull(method, "'method' must not be null");
ClientHttpResponse response = null;
try {
ClientHttpRequest request = createRequest(url, method);
if( request.getHeaders() != null ) {
request.getHeaders().add("Accept-Encoding", "gzip,deflate");
}
if (requestCallback != null) {
requestCallback.doWithRequest(request);
}
response = request.execute();
if (!getErrorHandler().hasError(response)) {
logResponseStatus(method, url, response);
} else {
handleResponseError(method, url, response);
}
if (responseExtractor != null) {
return responseExtractor.extractData(response);
} else {
return null;
}
} catch (IOException ex) {
throw new ResourceAccessException("I/O error: " + ex.getMessage(), ex);
} finally {
if (response != null) {
response.close();
}
}
}
private void handleResponseError(HttpMethod method, URI url, ClientHttpResponse response) throws IOException {
if (logger.isWarnEnabled()) {
try {
logger.warn(
method.name() + " request for \"" + url + "\" resulted in " + response.getStatusCode() + " (" +
response.getStatusText() + "); invoking error handler");
} catch (IOException e) {
// ignore
}
}
getErrorHandler().handleError(response);
}
private void logResponseStatus(HttpMethod method, URI url, ClientHttpResponse response) {
if (logger.isDebugEnabled()) {
try {
logger.debug(
method.name() + " request for \"" + url + "\" resulted in " + response.getStatusCode() + " (" +
response.getStatusText() + ")");
} catch (IOException e) {
// ignore
}
}
}
}
The least unobtrusive way to change the RestTemplate is not to change it. I decided to override the doExecute method because I want the RestTemplate functionality as it is right now. I also had to included the private functions being called by the RestTemplate. I could have used the exchange() function in the RestTemplate but this is to abstract IMO.
How to use
Instead of instantiating the Spring RestTemplate you can now use the GzipRestTemplate. And example of using the RestTemplate for reading JSON responses:
...
RestTemplate restTemplate = new GzipRestTemplate();
restTemplate.setRequestFactory(new SimpleClientHttpRequestFactory());
List<HttpMessageConverter<?>> mc = restTemplate.getMessageConverters();
MappingJacksonHttpMessageConverter json = new MappingJacksonHttpMessageConverter();
List<MediaType> supportedMediaTypes = new ArrayList<MediaType>();
supportedMediaTypes.add(new MediaType("text", "html"));
supportedMediaTypes.add(new MediaType("application", "json"));
json.setSupportedMediaTypes(supportedMediaTypes);
mc.add(json);
restTemplate.setMessageConverters(mc);
JsonResult jsonResult = restTemplate.getForObject(url, JsonResult.class);
...
And a pom.xml of an application that uses the RestTemplate:
...
<build>
<sourceDirectory>src</sourceDirectory>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>com.jayway.maven.plugins.android.generation2</groupId>
<artifactId>maven-android-plugin</artifactId>
<version>2.8.3</version>
<configuration>
<sdk>
<path>/path/to/android-sdk-windows</path>
<platform>8</platform>
</sdk>
<emulator>
<avd>my_avd</avd>
</emulator>
<deleteConflictingFiles>true</deleteConflictingFiles>
<undeployBeforeDeploy>true</undeployBeforeDeploy>
</configuration>
<extensions>true</extensions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
</plugin>
</plugins>
</build>
<dependencies>
...
<dependency>
<groupId>com.google.android</groupId>
<artifactId>android</artifactId>
<version>2.2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.android</groupId>
<artifactId>spring-android-rest-template</artifactId>
<version>1.0.0.BUILD-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>commons-httpclient</groupId>
<artifactId>commons-httpclient</artifactId>
<version>3.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.6.2</version>
<scope>compile</scope>
</dependency>
</dependencies>
<repositories>
<!-- For testing against latest Spring snapshots -->
<repository>
<id>org.springframework.maven.snapshot</id>
<name>Spring Maven Snapshot Repository</name>
<url>http://maven.springframework.org/snapshot</url>
<releases><enabled>false</enabled></releases>
<snapshots><enabled>true</enabled></snapshots>
</repository>
<!-- For developing against latest Spring milestones -->
<repository>
<id>org.springframework.maven.milestone</id>
<name>Spring Maven Milestone Repository</name>
<url>http://maven.springframework.org/milestone</url>
<snapshots><enabled>false</enabled></snapshots>
</repository>
</repositories>
...
In this article I’ll describe how to get started with Spring 3.0.5 and the latest apache-wicket version with Maven. Easiest way is to generate your project skeleton using Maven using with the Wicket archetype. See this page how to generate the project using a maven archetype: http://wicket.apache.org/start/quickstart.html. Or just run the command below:
mvn archetype:create -DarchetypeGroupId=org.apache.wicket -DarchetypeArtifactId=wicket-archetype-quickstart -DarchetypeVersion=1.4.15 -DgroupId=my.group -DartifactId=my-artifact -DinteractiveMode=false
Add wicket-spring integration dependencies to the Maven pom, see this guide (is a bit outdated): https://cwiki.apache.org/WICKET/spring.html. When using the latest version make sure that you do not include the wicket-spring-anot dependency because it has been merged into the wicket-spring jar. You do need to add the wicket-ioc jar if you are using annotation-based dependency injection in Wicket.
Below is my Maven pom.xml. It includes the Wicket and Spring dependencies but also Jetty dependencies to run the Wicket application.
...
<properties>
<wicket.version>1.4.15</wicket.version>
<jetty.version>6.1.25</jetty.version>
<slf4j.version>1.5.8</slf4j.version>
<log4j.version>1.2.14</log4j.version>
<wicket.spring.version>1.2.6</wicket.spring.version>
<spring.version>3.0.5.RELEASE</spring.version>
</properties>
<dependencies>
<!-- WICKET DEPENDENCIES -->
<dependency>
<groupId>org.apache.wicket</groupId>
<artifactId>wicket</artifactId>
<version>${wicket.version}</version>
</dependency>
<!-- SPRING INTEGRATION -->
<dependency>
<groupId>org.apache.wicket</groupId>
<artifactId>wicket-spring</artifactId>
<version>${wicket.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.wicket</groupId>
<artifactId>wicket-ioc</artifactId>
<version>${wicket.version}</version>
</dependency>
<!-- SPRING -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- LOGGING DEPENDENCIES - LOG4J -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
</dependency>
<!-- JUNIT DEPENDENCY FOR TESTING -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.1</version>
<scope>test</scope>
</dependency>
<!-- JETTY DEPENDENCIES FOR TESTING -->
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty</artifactId>
<version>${jetty.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-util</artifactId>
<version>${jetty.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-management</artifactId>
<version>${jetty.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<filtering>false</filtering>
<directory>src/main/resources</directory>
</resource>
<resource>
<filtering>false</filtering>
<directory>src/main/java</directory>
<includes>
<include>**</include>
</includes>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</resource>
</resources>
<testResources>
<testResource>
<filtering>false</filtering>
<directory>src/test/java</directory>
<includes>
<include>**</include>
</includes>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</testResource>
</testResources>
<plugins>
<plugin>
<inherited>true</inherited>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
<optimize>true</optimize>
<debug>true</debug>
</configuration>
</plugin>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<version>${jetty.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.8</version>
<configuration>
<downloadSources>true</downloadSources>
</configuration>
</plugin>
</plugins>
</build>
...
Modify your web.xml to add the Spring and Wicket integration configuration. Previously you had to configure this using the WicketServlet. The prefered way is to use the WicketFilter as shown below:
... <context-param> <param-name>contextConfigLocation</param-name> <param-value>WEB-INF/applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <filter> <filter-name>WicketFilter</filter-name> <filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class> <init-param> <param-name>applicationFactoryClassName</param-name> <param-value>org.apache.wicket.spring.SpringWebApplicationFactory</param-value> </init-param> </filter> <filter-mapping> <filter-name>WicketFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> ...
That is all, no need to defined the applicationClassName because it will be configured in Spring. Now edit your Spring applicationContext.xml, mine is in the WEB-INF. I prefer loading it from the WEB-INF and not from the classpath to avoid name collisions with other Spring xml files on the classpath.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
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">
<!-- Enable annotation scanning. -->
<context:component-scan base-package="my.package" />
<!-- Wicket WebApplication setup -->
<bean id="wicketApplication" class="my.package.WicketApplication"/>
</beans>
You still need to define that you want to use annotation-based injection of your beans in Wicket specific classes. This can be done by adding the SpringComponentInjector to Wicket in the init method of your WicketApplication.
public class WicketApplication extends WebApplication {
/**
* Constructor
*/
public WicketApplication() {
}
@Override
public void init() {
super.init();
addComponentInstantiationListener(new SpringComponentInjector(this));
}
/**
* @see org.apache.wicket.Application#getHomePage()
*/
public Class<HomePage> getHomePage() {
return HomePage.class;
}
}
Example
An example of leveraging the dependency capabilities of Wicket.
import javax.inject.Named;
@Named("mySpringBean")
public class MySpringBean {
public void foo() {
System.err.println("foo");
}
}
Using the bean in your Wicket WebPage:
public class HomePage extends WebPage {
@SpringBean
private MySpringBean mySpringBean;
public HomePage(final PageParameters parameters) {
mySpringBean.foo();
}
...
}
Launching the application
You can launch the application from the commandline using Maven and Jetty. The pom.xml contains all the required Jetty dependencies. Run the following command:
mvn clean jetty:run
This will compile your application, startup a Jetty instance and deploy the application.
Conclusion
IOC integration in Wicket is getting better and easier to configure, but there is room for improvement though. I would prefer to inject my (Spring) managed beans using @Inject. This allowes for a smooth transition to Java EE6 and standardized annotation usage.
Also the init() section can be improved. Why can the ComponentInstantiationListener not be configured in the Spring configuration? This removes the hard dependency on the SpringComponentInjector in the code.
I created a small utility class which allowes me to configure my Wicket apps using Spring like this:
<!-- Wicket WebApplication setup -->
<bean id="wicketApplication" class="com.oudmaijer.wicket.application.ConfigurableWicketApplication">
<property name="homePage" value="my.HomePage"/>
<property name="componentInstantiationListenerType" value="spring"/>
</bean>
This removes the need for a WicketApplication class for each new application. Here is the code:
public class ConfigurableWicketApplication extends WebApplication {
private Class<WebPage> homePage;
private String type;
/**
* Constructor
*/
public ConfigurableWicketApplication() {
}
@Override
public void init() {
super.init();
if( "spring".equalsIgnoreCase(type) ) {
addComponentInstantiationListener(new SpringComponentInjector(this));
}
}
/**
* @see org.apache.wicket.Application#getHomePage()
*/
public Class<WebPage> getHomePage() {
return homePage;
}
public void setHomePage(Class<WebPage> homePage) {
this.homePage = homePage;
}
public void setComponentInstantiationListenerType(String type) {
this.type = type;
}
}
Spring 3.0 has support for REST style WebServices, the Spring MVC controllers facilitate the functionality. In this example I will show an example of how to implement a basic REST service that uses XML marshalling to sent information over HTTP. Disclaimer: this is not an in depth tutorial for building REST style WebServices.
The Spring MVC controller
The Spring 3.0 REST support relies havily on Spring MVC. You need to use the Spring MVC Controller for implementing REST style WebServices with Spring. To declare a Controller you can use the Spring annotation based configuration. In this example I have annotated the ProductRestService class with the @Controller annotation. In order for Spring to pick-up the annotation Spring needs to be configured to scan for annotation (see the Spring configuration section).
REST uses templates that describe the URI to be used to invoke a WebService method. These URI templates can contain variable placeholders which allow for passing information to the WebService. The URI template contains all the information required for invoking a RESTfull GET method.
The @RequestMapping annotation allowes you to define the URI and HTTP method that are mapped to a method. In this example I have annotated the ProductRestService.getProductById(Long productId) with the @RequestMapping.
The value of the @RequestMapping, in this case: /products/{productId} , defines the URI that is mapped to this method. The productId variable needs to be defined when invoking the method and will be resolved automatically by Spring MVC with the value from the request URI. You can use the @PathVariable to inject the value of the productId variable directly into a method parameter.
The @ResponseBody annotation tells Spring to marshall the return value of the method to the HTTP response body. Spring allowes you to configure HTTP message converters that take care of conversion of the return value to a format which is accepted by the client. In this example the return value will be marshalled to XML using XStream.
package com.oudmaijer.spring.rest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* This is an example REST style MVC controller. It serves as an
* endpoint for retrieving Product Objects.
*/
@Controller
public class ProductRestService {
/**
* This method returns a specific Product. The URI to request a Product is
* specified in the @RequestMapping.
*
* @param productId the identifier of the requested product
* @return a Product
*/
@RequestMapping(value="/products/{productId}", method = RequestMethod.GET)
@ResponseBody
public Product getProductById(@PathVariable Long productId) {
Product p = new Product();
p.setId(productId);
return p;
}
}
Spring configuration
The configuration is where all the magic happens. It is important to define the <mvc:annotation-driven /> element at the end of the configuration file or else Spring will not register the marshallingHttpMessageConverter. It took me some time to figure this out ;(
You need to add the MessageConverters to the configuration in order to get the OXM marshalling to work. Spring uses the requests Accept header to determine which converter to use.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:util="http://www.springframework.org/schema/util" 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/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> <!-- Enable annotation scanning. --> <context:component-scan base-package="com.oudmaijer.spring.rest" /> <!-- Define the OXM marshaller which is used to convert the Objects <-> XML. --> <bean id="oxmMarshaller" class="org.springframework.oxm.xstream.XStreamMarshaller" /> <bean id="marshallingHttpMessageConverter" class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter"> <property name="marshaller" ref="oxmMarshaller" /> <property name="unmarshaller" ref="oxmMarshaller" /> </bean> <!-- Required for REST services in order to bind the return value to the ResponseBody. --> <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> <property name="messageConverters"> <util:list id="beanList"> <ref bean="marshallingHttpMessageConverter" /> </util:list> </property> </bean> <!-- Should be defined last! --> <mvc:annotation-driven /> </beans>
Maven2 dependencies
You need to add a couple of Maven2 dependencies to get the project up and running. Below is the entire pom.xml.
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.oudmaijer.spring.rest</groupId>
<artifactId>spring-3.0-rest</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<!-- http://maven.apache.org/plugins/maven-compiler-plugin/ -->
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
<optional>false</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>3.0.0.RELEASE</version>
<optional>false</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>3.0.0.RELEASE</version>
<optional>false</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>3.0.0.RELEASE</version>
<optional>false</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
<version>3.0.0.RELEASE</version>
<optional>false</optional>
</dependency>
<dependency>
<groupId>xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.2.2</version>
<optional>false</optional>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Web deployment descriptor: web.xml
Last but not least the web.xml. Since the REST support in Spring is based on Spring MVC you need to define the DispatcherServlet. Make sure to map the correct URL pattern to the DispatcherServlet.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>spring-3.0-rest</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/META-INF/spring/*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
Invoking the service
This example only supports the HTTP GET method. If you want to test or build a client that uses REST WebServices you should use the RestTemplate in Spring. We can easily validate if the example WebService is running by accessing the service through Firefox. This will result in the following response.
For more information on REST support in Spring 3.0 please refer to the Spring reference documentation.
