Category: English
Hightlights of Spring 3.1
- Conversation management
- Environment based profiles (ala ruby/grails)
- Configuration enhancements
See the presentation by Juergen Hoeller
http://www.infoq.com/presentations/Spring-3.0-3.1-3.2
More on configuration enhancements in Spring 3.1
http://www.infoq.com/presentations/Configuration-in-Spring-3-1
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>
...
Here are a couple of screenshots of the Android 3.0 SDK preview made on my laptop running Windows 7 and the Android SDK. Android 3.0 is the long awaited Android version targetted at Tablets. Can’t wait to see the first Tablets coming out with Android 3.0 like the Motorola Xoom or the Toshiba Tablet.
Booting the AVD
Startup error
Initially I started the device with a screen resolution of 800 x 600 but this gave an error during startup. Later on I changed the screen resolution to 600×800 and the error was gone.
600×800
I changed the resolution to 600×800 to have a landscape view. You can rotate the screen in the emulator by pressing Ctrl + F11. This is what the Android 3.0 homescreen looks like:
The homescreen clock:
Search widget:
Browser:
Add widget/application menu:
Application shortcuts:
Apps menu:
Settings menu:
Try it yourself
You can download the Android SDK, create an Android Virtual Device and try starting the Android emulator yourself. This is how I configured my Android Virtual Device.
The Android Virtual Device
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;
}
}
Finally a Hudson version that supports Maven 3! Olivier Lamy has put out a version on his site as a Christmas present:
Version 1.389 was release on 24th of december and is still a snapshot version. I tried version 1.389 with Maven 3.0.1 and seems to be working fine. Now I can safely abandon Maven 2
Also make sure to check out the Maven3 support for Hudson @ hudson-ci.org.












