REST service example using CXF 2.2, JAX-RS 1.0, JAXB and Spring
2009-06-17 14:23
826 查看
Filed under: java, spring — Tags: cxf, jaxb, jaxrs, maven, rest, roa, service, spring — Dhruba
From:http://dhruba.name/2008/12/08/rest-service-example-using-cxf-22-jax-rs-10-jaxb-and-spring/
Bandopadhyay @ 00:46
In the modern day transition from a service oriented architecture to a resource oriented architecture the tools are only just catching up. Here’s a very brief and simple example of a REST service using CXF 2.2-SNAPSHOT, JAX-RS 1.0 and Spring 2.5.6. A few key points to note are as below.
The maven pom file imports a cxf jax-rs bundle which in turn imports all prerequisites - this saves individual cxf imports.
The getUsers() call demonstrates a rather unorthodox use of a dto as a replacement for a standard collection due to the fact that jaxb cannot handle collections as first class citizens.
A snapshot version (2.2) is being used of CXF which supports the final version of JAX-RS (1.0) whereas CXF 2.1.x supports only JAX-RS 0.8.
Annotations have been applied to the implementation but can be applied to the interface.
Annotations are inherited from interface to implementation and from class level to method level and can be overridden at method level.
Users REST output
Single user REST output
The eclipse project is available for download. Build using
Update: Made correction - removed all annotations from interface as not necessary for the purposes of this example.
Update2: [15/12/2008] Made user collection rest xml a bit more semantic by fixing singular and plurals.
Has this been helpful to you? Please say so. What other topics would you like to see? Let me know and I’ll do my best.
An implementation MUST include pre-packaged MessageBodyReader and MessageBodyWriter implementations for the following Java and media type combinations:
byte[] All media types (*/*).
java.lang.String All media types (*/*).
java.io.InputStream All media types (*/*).
java.io.Reader All media types (*/*).
java.io.File All media types (*/*).
javax.activation.DataSource All media types (*/*).
javax.xml.transform.Source XML types (text/xml, application/xml and application/*+xml).
javax.xml.bind.JAXBElement and application-supplied JAXB classes XML media types (text/xml, application/xml and application/*+xml).
MultivaluedMap Form content (application/x-www-form-urlencoded).
StreamingOutput All media types (*/*), MessageBodyWriter only.
As the spec says above a jax-rs implementation must provide reader and writer implementations for the above types out of the box and ready to use which cxf does provide. It is only necessary to implement a reader or writer if you want to support a custom type or would like to provide a more efficient implementation of an existing type but luckily most of the work has already been done for you. If you look at the cxf source you’ll see provider implementations for the above minimum set of types already as below.
With particular reference to jaxb as a data binding technology the specification also mentions the following.
The implementation-supplied entity provider(s) for javax.xml.bind.JAXBElement and application supplied JAXB classes MUST use JAXBContextinstances provided by application-supplied context resolvers, see section 4.3. If an application does not supply a JAXBContext for a particular type, the implementation-supplied entity provider MUST use its own default context instead. [...]. An implementation MUST support application-provided entity providers and MUST use those in preference to its own pre-packaged providers when either could handle the same request.
The crux of it is that if you supply a JAXBContextResolver then CXF will use it otherwise it will create its own. An example JAXBContextResolver is provided below.
The specification mentions that an implementation must detect automatically any classes which are annotated as providers. However CXF does not yet support it and expresses a definite preference for explicit definition of providers as below.
However it seems that CXF does not in fact recognise the provider even with this configuration and I’m still trying to work out why. It would be nice to provide one’s own context and one’s own marshaller properties. This is left as a task for the reader for the time being and I’ll also continue to look into it.
From:http://dhruba.name/2008/12/08/rest-service-example-using-cxf-22-jax-rs-10-jaxb-and-spring/
Bandopadhyay @ 00:46
In the modern day transition from a service oriented architecture to a resource oriented architecture the tools are only just catching up. Here’s a very brief and simple example of a REST service using CXF 2.2-SNAPSHOT, JAX-RS 1.0 and Spring 2.5.6. A few key points to note are as below.
The maven pom file imports a cxf jax-rs bundle which in turn imports all prerequisites - this saves individual cxf imports.
The getUsers() call demonstrates a rather unorthodox use of a dto as a replacement for a standard collection due to the fact that jaxb cannot handle collections as first class citizens.
A snapshot version (2.2) is being used of CXF which supports the final version of JAX-RS (1.0) whereas CXF 2.1.x supports only JAX-RS 0.8.
Annotations have been applied to the implementation but can be applied to the interface.
Annotations are inherited from interface to implementation and from class level to method level and can be overridden at method level.
Maven dependencies (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>cxf-rest-example</groupId> <artifactId>cxf-rest-example</artifactId> <version>1.0</version> <packaging>war</packaging> <name>cxf-rest-example</name> <description>cxf-rest-example</description> <organization> <name>dhruba.name</name> <url>http://dhruba.name</url> </organization> <build> <finalName>cxf-rest-example</finalName> <plugins> <!-- enable java 6 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.6</source> <target>1.6</target> </configuration> </plugin> <!-- skip tests --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.4.2</version> <configuration> <skipTests>true</skipTests> </configuration> </plugin> <!-- automatically clean before build --> <plugin> <artifactId>maven-clean-plugin</artifactId> <executions> <execution> <id>auto-clean</id> <phase>validate</phase> <goals> <goal>clean</goal> </goals> </execution> </executions> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-bundle-jaxrs</artifactId> <version>2.2-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>2.5.6</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>2.5.6</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>2.5.6</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>2.5.6</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.5</version> <scope>test</scope> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.15</version> <exclusions> <exclusion> <groupId>com.sun.jmx</groupId> <artifactId>jmxri</artifactId> </exclusion> <exclusion> <groupId>javax.jms</groupId> <artifactId>jms</artifactId> </exclusion> <exclusion> <groupId>com.sun.jdmk</groupId> <artifactId>jmxtools</artifactId> </exclusion> </exclusions> </dependency> </dependencies> <repositories> <repository> <id>java.net.2</id> <name>Java Net 2 Repository</name> <url>http://download.java.net/maven/2</url> </repository> <repository> <id>apache.incubating</id> <name>Apache Incubating Repository</name> <url>http://people.apache.org/repo/m2-incubating-repository</url> </repository> <repository> <id>apache.snapshot</id> <name>Apache Snapshot Repository</name> <url>http://people.apache.org/repo/m2-snapshot-repository</url> </repository> </repositories> </project>
Container descriptor file (web.xml)
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <display-name>CXF REST Example</display-name> <description>CXF REST Example</description> <context-param> <param-name>webAppRootKey</param-name> <param-value>cxf.rest.example.root</param-value> </context-param> <context-param> <param-name>log4jConfigLocation</param-name> <param-value>/WEB-INF/classes/log4j.properties</param-value> </context-param> <listener> <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/deploy-context.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>CXFServlet</servlet-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>/*</url-pattern> </servlet-mapping> </web-app>
Spring context file (deploy-context.xml)
<?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" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd" default-lazy-init="false"> <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" /> <jaxrs:server id="myService" address="/"> <jaxrs:serviceBeans> <ref bean="serviceImpl" /> </jaxrs:serviceBeans> <jaxrs:extensionMappings> <entry key="xml" value="application/xml" /> </jaxrs:extensionMappings> </jaxrs:server> <bean id="serviceImpl" class="service.ServiceImpl" /> </beans>
User pojo
package pojo; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement(name = "user") public class User { private Integer id; private String name; public User() { } public User(Integer id, String name) { this.id = id; this.name = name; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public void setName(String name) { this.name = name; } public String getName() { return name; } @Override public String toString() { return String.format("{id=%s,name=%s}", id, name); } }
User DTO
package pojo; import java.util.Collection; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement public class UserCollection { private Collection users; public UserCollection() { } public UserCollection(Collection users) { this.users = users; } @XmlElement(name="user") @XmlElementWrapper(name="users") public Collection getUsers() { return users; } }
Service interface
package service; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.core.Response; import pojo.User; import pojo.UserCollection; public interface ServiceDefn { UserCollection getUsers(); User getCustomer(Integer id); Response getBadRequest(); }
Service implementation
package service; import java.util.HashMap; import java.util.Map; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import pojo.User; import pojo.UserCollection; @Path("/myservice/") @Produces("application/xml") public class ServiceImpl implements ServiceDefn { private static Map users = new HashMap(); static { users.put(1, new User(1, "foo")); users.put(2, new User(2, "bar")); users.put(3, new User(3, "baz")); } public ServiceImpl() { } @GET @Path("/users") @Override public UserCollection getUsers() { return new UserCollection(users.values()); } @GET @Path("/user/{id}") @Override public User getUser(@PathParam("id") Integer id) { return users.get(id); } @GET @Path("/users/bad") @Override public Response getBadRequest() { return Response.status(Status.BAD_REQUEST).build(); } }
REST Service
The following urls should now show the expected REST output.http://localhost:8080/cxf-rest-example/myservice/users/
http://localhost:8080/cxf-rest-example/myservice/user/1
Users REST output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <userCollection> <users> <user> <id>1</id> <name>foo</name> </user> <user> <id>2</id> <name>bar</name> </user> <user> <id>3</id> <name>baz</name> </user> </users> </userCollection>
Single user REST output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <user> <id>1</id> <name>foo</name> </user>
Discussion
It’s a little rough around the edges (the bad request call doesn’t respond in the expected fashion) but the REST service works using CXF 2.2-SNAPSHOT, JAXB 2.1.7, JAX-RS 1.0 and Spring 2.5.6. CXF can do a lot more but here I’ve created only a simple skeleton project. If you are using CXF 2.1 which supports JAX-RS 0.8 then this example will use@ProduceMimeand
@ConsumeMimeinstead of
@Producesand
@Consumes- a full migration list is provided by CXF. When I get a chance I’ll create an equivalent using Spring 3.0 - they’ve just released M1 which I’m hoping has REST.
The eclipse project is available for download. Build using
mvn package. The maven build file automatically cleans before build and skips tests. To compile on Mac OS X you must tell maven where Java lives.
Update: Made correction - removed all annotations from interface as not necessary for the purposes of this example.
Update2: [15/12/2008] Made user collection rest xml a bit more semantic by fixing singular and plurals.
Questions
If anyone knows how to enable xml formatting in the jaxb marshalling output when using cxf please let me know. I know how to do this in jaxb but because the use of jaxb and its marshaller is embedded within cxf it is not immediately clear how this would be done.Has this been helpful to you? Please say so. What other topics would you like to see? Let me know and I’ll do my best.
Standard Entity Providers
Update3 [25/12/2008]: In comment #1 to this post Victor asked why it is not necessary to implement MessageBodyReader or MessageBodyWriter and annotate with @Provider which are types from the api of the jax-rs 1.0 specification. In order to answer this, first, I quote the relevant section (4.2.4) of the jax-rs 1.0 specification. The CXF manual also sheds much light on what it supports and how.An implementation MUST include pre-packaged MessageBodyReader and MessageBodyWriter implementations for the following Java and media type combinations:
byte[] All media types (*/*).
java.lang.String All media types (*/*).
java.io.InputStream All media types (*/*).
java.io.Reader All media types (*/*).
java.io.File All media types (*/*).
javax.activation.DataSource All media types (*/*).
javax.xml.transform.Source XML types (text/xml, application/xml and application/*+xml).
javax.xml.bind.JAXBElement and application-supplied JAXB classes XML media types (text/xml, application/xml and application/*+xml).
MultivaluedMap Form content (application/x-www-form-urlencoded).
StreamingOutput All media types (*/*), MessageBodyWriter only.
As the spec says above a jax-rs implementation must provide reader and writer implementations for the above types out of the box and ready to use which cxf does provide. It is only necessary to implement a reader or writer if you want to support a custom type or would like to provide a more efficient implementation of an existing type but luckily most of the work has already been done for you. If you look at the cxf source you’ll see provider implementations for the above minimum set of types already as below.
$ ls -1 Downloads/apache-cxf-2.2-SNAPSHOT-src/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/ AbstractAegisProvider.java AbstractConfigurableProvider.java AbstractJAXBProvider.java AegisElementProvider.java AtomEntryProvider.java AtomFeedProvider.java BinaryDataProvider.java FormEncodingReaderProvider.java FormValidator.java JAXBElementProvider.java JSONProvider.java Messages.properties PrimitiveTextProvider.java ProviderFactory.java SourceProvider.java StringProvider.java XMLBeanStreamSerializer.java XMLBeansElementProvider.java XMLBeansJSONProvider.java
With particular reference to jaxb as a data binding technology the specification also mentions the following.
The implementation-supplied entity provider(s) for javax.xml.bind.JAXBElement and application supplied JAXB classes MUST use JAXBContextinstances provided by application-supplied context resolvers, see section 4.3. If an application does not supply a JAXBContext for a particular type, the implementation-supplied entity provider MUST use its own default context instead. [...]. An implementation MUST support application-provided entity providers and MUST use those in preference to its own pre-packaged providers when either could handle the same request.
The crux of it is that if you supply a JAXBContextResolver then CXF will use it otherwise it will create its own. An example JAXBContextResolver is provided below.
package context; import javax.ws.rs.ext.ContextResolver; import javax.ws.rs.ext.Provider; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; @Provider public class JAXBContextResolver implements ContextResolver { private final static String annotatedPackages = "pojo"; private final static JAXBContext context = initContext(); private static JAXBContext initContext() { JAXBContext context = null; try { context = JAXBContext.newInstance(annotatedPackages); } catch (JAXBException e) { throw new RuntimeException(e); } return context; } @Override public JAXBContext getContext(Class clazz) { return context; } }
The specification mentions that an implementation must detect automatically any classes which are annotated as providers. However CXF does not yet support it and expresses a definite preference for explicit definition of providers as below.
<?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" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd" default-lazy-init="false"> <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" /> <jaxrs:server id="myService" address="/"> <jaxrs:serviceBeans> <ref bean="serviceImpl" /> </jaxrs:serviceBeans> <jaxrs:providers> <ref bean="jaxbContextResolver" /> </jaxrs:providers> </jaxrs:server> <bean id="jaxbContextResolver" class="context.JAXBContextResolver" /> <bean id="serviceImpl" class="service.ServiceImpl" /> </beans>
However it seems that CXF does not in fact recognise the provider even with this configuration and I’m still trying to work out why. It would be nice to provide one’s own context and one’s own marshaller properties. This is left as a task for the reader for the time being and I’ll also continue to look into it.
相关文章推荐
- CXF系列之JAX-RS:CXF发布与调用REST服务
- Using JAX-RS with Protocol Buffers for high-performance REST APIs
- REST,JAX-RS与Jersey,CXF,RESTEasy之间的关系
- JWS,JAX-WS,JAX-RS,REST,Restlet,SOAP,JAXB,JAXP,JAXM,JAXR,SAAJ
- Failed while installing JAX-RS (REST Web Services) 1.1. org.osgi.service.prefs.BackingStoreException: Resource '/.settings' does not exist.
- CXF系列之JAX-RS:CXF与spring集成发布REST服务
- 使用 Spring + CXF 发布 REST 服务(jax-RS)
- REST with Java (JAX-RS) using Jersey - Tutorial(使用REST的Jersey与Java(JAX-RS)-教程)
- JAX-RS Rest Service官方文档
- JAX-RS 2.0 REST客户端编程实例
- JAX-RS REST客户端实现基本身份验证机制
- JAX-RS(基于Jersey) + Spring 4.x + MyBatis构建REST服务架构
- WebService(注解),CXF框架(jax-ws,Jax-rs,与spring整合)
- 使用 JAX-RS 简化 REST 应用开发
- jax rs restlet webservice搭建
- JAX-RS RESTful webservice 服务端及客户端实现(基于HTTPS双向认证)
- AbstractMethodError using UriBuilder on JAX-RS
- jaxws soap webservice 使用apache cxf tool ——wsdl2java
- 初识Rest、JSR、JCP、JAX-RS及Jersey
- CXF Webservice Using Embedded Jetty