I love REST. It’s easy, straightforward and above all: easily testable. When you’re developing with REST, you have a lot of options to choose from. There’s Jersey, the reference Sun implementation, then you have RestEasy, the JBoss choice, and there is CXF, the Apache choice. I chose CXF for this example simply because it’s so easy to create REST services with it, without a lot of configuration.
We’re going to start with a basic web application, which in my case means firing up Maven’s mvn archetype:generate and choosing the simple webapp archetype. So now we have this:
.
|-- pom.xml
`-- src
`-- main
|-- resources
`-- webapp
|-- WEB-INF
| `-- web.xml
`-- index.jsp
Now, since the article states I’ll be using Spring and CXF, let’s start and add the dependencies.
...
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.0.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>3.0.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxrs</artifactId>
<version>2.2.3</version>
</dependency>
...
Don’t forget to alter the compiler plugin, as we’ll be using Java 5 features.
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.1</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
...
Now that we have the jars we need, we now need a service to expose. In this example no Hello World, but something slightly more practical: a time service. The 10 minute version of a watch, as you will.
import org.springframework.format.datetime.DateFormatter;
import org.springframework.stereotype.Service;
import java.util.Calendar;
import java.util.Locale;
@Service("timeService")
public class TimeService {
public String getDateTime()
{
DateFormatter formatter = new DateFormatter("dd/MM/yyyy hh:mm:ss");
return formatter.print(Calendar.getInstance().getTime(), Locale.getDefault());
}
}
CXF uses the JAX-RS spec to expose REST services, so we’ll need to add some annotations to the service.
@Service("timeService")
@Path("/time")
public class TimeService {
@GET
@Produces("text/plain")
public String getDateTime()
{
...
}
}
And that’s it for coding, people. Now we only need to do some Spring configuration magic. First, we’ll Spring-enable the web context and add the CXF servlet for handling the REST calls.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/beans.xml</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<servlet>
<servlet-name>CXFServlet</servlet-name>
<display-name>CXF Servlet</display-name>
<servlet-class>
org.apache.cxf.transport.servlet.CXFServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
</web-app>
The only thing left to do is the Spring configuration. CXF needs 3 of its own Spring configuration files, so we’ll need to import those (this is nicely illustrated in the CSF manual). I’m using component scanning here, if you don’t use it, don’t forget to include the service bean.
<?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:jaxrs="http://cxf.apache.org/jaxrs"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<import resource="classpath:META-INF/cxf/cxf.xml"/>
<import resource="classpath:META-INF/cxf/cxf-extension-jaxrs-binding.xml"/>
<import resource="classpath:META-INF/cxf/cxf-servlet.xml"/>
<context:component-scan base-package="be.insaneprogramming.cxf"/>
<jaxrs:server id="restContainer" address="/">
<jaxrs:serviceBeans>
<ref bean="timeService"/>
</jaxrs:serviceBeans>
</jaxrs:server>
</beans>
And that’s it!
To test the service, I used the tomcat plugin to quickly startup a Tomcat container
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>tomcat-maven-plugin</artifactId>
<version>1.0-beta-1</version>
<configuration>
<port>9999</port>
<path>/cxf</path>
<warFile>${project.basedir}/target/${project.build.finalName}.war</warFile>
</configuration>
</plugin>
Now browse to http://localhost:9999/cxf/rest/time and have a look at your new expensive 10-minute watch.
Tomorrow, I’ll post a follow-up on this article explaining how easy it is to enable security on this service.
#1 by jcllings on 2012/07/15 - 07:51
Could use some tips on how to convert this to an XML base. Tried just changing the @Produces to “application/xml” but then in my browser I get a “not well formed” error. This tells me that the mime type changed but the output was not actually XML.
What I want is to add enough info the the code example so that the user can do this easily.