JAX-RS : Services Configuration
Configuring JAX-RS services programmatically
import org.apache.cxf.jaxrs.JAXRSServerFactoryBean; ... JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean(); sf.setResourceClasses(CustomerService.class); sf.setAddress("http://localhost:9000/"); sf.create();
Some things to note:
- The JAXRSServerFactoryBean creates a Server inside CXF which starts listening for requests on the URL specified.
- Check the JAXRSServerFactoryBean API for methods for adding multiple root resources
- setResourceClasses() is for root resources only, use setProvider() or setProviders() for @Provider-annotated classes.
By default, the JAX-RS runtime is responsible for the lifecycle of resource classes, default lifecycle is per-request. You can set the lifecycle to singleton by using following line:
sf.setResourceProvider(BookStore.class, new SingletonResourceProvider(new BookStore()));
If you prefer not to let the JAX-RS runtime handle the resource class lifecycle for you (for example, it might be the case that your resource class is created by other containers such as Spring), you can do the following:
JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean(); CustomerService cs = new CustomerService(); sf.setServiceBeans(cs); sf.setAddress("http://localhost:9080/"); sf.create();
OSGI
Blueprint
The following example shows how to configure a JAX-RS endpoint in OSGI containers supporting Blueprint:
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxrs="http://cxf.apache.org/blueprint/jaxrs" xmlns:cxf="http://cxf.apache.org/blueprint/core" xsi:schemaLocation=" http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd http://cxf.apache.org/blueprint/jaxrs http://cxf.apache.org/schemas/blueprint/jaxrs.xsd http://cxf.apache.org/blueprint/core http://cxf.apache.org/schemas/blueprint/core.xsd "> <cxf:bus> <cxf:features> <cxf:logging/> </cxf:features> </cxf:bus> <jaxrs:server id="customerService" address="/customers"> <jaxrs:serviceBeans> <ref component-id="serviceBean" /> </jaxrs:serviceBeans> </jaxrs:server> <bean id="serviceBean" class="service.CustomerService"/> </blueprint>
Spring
<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-2.0.xsd http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd"> <jaxrs:server id="customerService" address="/customers"> <jaxrs:serviceBeans> <ref bean="serviceBean"/> </jaxrs:serviceBeans> </jaxrs:server> <bean id="serviceBean" class="service.CustomerService"/> </beans>
Spring Boot
Please see CXF SpringBoot documentation.
Please see JAX-RS Spring Boot and JAX-RS Spring Boot Scan demos.
Please see JAXRSClientSpringBoot documentation on how CXF JAX-RS Clients can be used in a SpringBoot Application.
Configuring JAX-RS endpoints programmatically without Spring
Note that even though no Spring is explicitly used in the previous section, it is still used by default to have various CXF components registered with the bus such as transport factories. If no Spring libraries are available on the classpath then please follow the following example :
JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean(); sf.setResourceClasses(CustomerService.class); sf.setResourceProvider(CustomerService.class, new SingletonResourceProvider(new CustomerService())); sf.setAddress("http://localhost:9000/"); BindingFactoryManager manager = sf.getBus().getExtension(BindingFactoryManager.class); JAXRSBindingFactory factory = new JAXRSBindingFactory(); factory.setBus(sf.getBus()); manager.registerBindingFactory(JAXRSBindingFactory.JAXRS_BINDING_ID, factory); sf.create();
Blueprint Web
This section describes how CXF JAX-RS endpoints can be bootstrapped with CXFBlueprintServlet and Blueprint contexts.
This approach is recommended for developers building CXF JAX-RS endpoints to be deployed in OSGI and which will do RequestDispatcher-based forwards.
Additionally it allows to reuse the same Blueprint contexts between OSGI and non-OSGI deployments.
Both options below work with CXF 3.1.3:
<dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-frontend-jaxrs</artifactId> <version>3.1.3</version> </dependency>
Maven dependencies
OSGI
<dependency> <groupId>org.apache.aries.blueprint</groupId> <artifactId>org.apache.aries.blueprint.webosgi</artifactId> <version>1.0.1</version> </dependency>
In OSGI (Karaf) one should also install a 'war' feature.
Servlet Container
<dependency> <groupId>org.apache.aries.blueprint</groupId> <artifactId>org.apache.aries.blueprint.web</artifactId> <version>1.1.1</version> </dependency>
Common example
This web.xml shows how to setup CXFBlueprintServlet which processes this Blueprint context. It works exactly the same way in OSGI and non-OSGI environments.
Configuring JAX-RS services in container with Spring configuration file.
web.xml
In web.xml one needs to register one or more CXFServlet(s) and link to an application context configuration.
Using Spring ContextLoaderListener
<?xml version="1.0" encoding="ISO-8859-1"?> <!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>/*</url-pattern> </servlet-mapping> </web-app>
The application context configuration is shared between all the CXFServlets
Using CXFServlet init parameters
<?xml version="1.0" encoding="ISO-8859-1"?> <!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> <servlet> <servlet-name>CXFServlet1</servlet-name> <display-name>CXF Servlet1</display-name> <servlet-class> org.apache.cxf.transport.servlet.CXFServlet </servlet-class> <init-param> <param-name>config-location</param-name> <param-value>/WEB-INF/beans1.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet> <servlet-name>CXFServlet2</servlet-name> <display-name>CXF Servlet2</display-name> <servlet-class> org.apache.cxf.transport.servlet.CXFServlet </servlet-class> <init-param> <param-name>config-location</param-name> <param-value>/WEB-INF/beans2.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>CXFServlet1</servlet-name> <url-pattern>/1/*</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>CXFServlet2</servlet-name> <url-pattern>/2/*</url-pattern> </servlet-mapping> </web-app>
Each CXFServlet can get a unique application context configuration. Note, no Spring ContextLoaderListener is registered in web.xml in this case.
beans.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"> <!-- do not use import statements if CXFServlet init parameters link to this beans.xml --> <import resource="classpath:META-INF/cxf/cxf.xml" /> <import resource="classpath:META-INF/cxf/cxf-servlet.xml" /> <jaxrs:server id="customerService" address="/service1"> <jaxrs:serviceBeans> <ref bean="customerBean" /> </jaxrs:serviceBeans> </jaxrs:server> <bean id="customerBean" class="demo.jaxrs.server.CustomerService" /> </beans>
In the above configuration all resources will be configured as singletons, see below for information on creating per-request resources.
Configuring JAX-RS services using explicit bean configuration
Note that jaxrs:server (and jaxrs:client) declarations depend on 'http://cxf.apache.org/jaxrs' Spring NamespaceHandler be available on classpath. Sometimes, due to classloading restrictions or bugs in underlying containers which are exposed during complex deployments or due to multiple Spring libraries interfering with each other, NamespaceHandler can not be located and thus jaxrs endpoints can not be created.
Please report such issues to the team working on developing the container itself.
If you need to do Spring configuration and get an error to do with a missing NamespaceHandler then, as a workaround, consider configuring jaxrs endpoints using CXF beans which actually handle the creation of jaxrs:server endpoints. This is marginally more complex, but overall, the configuration ends up being quite similar, for example, the above jaxrs:server endpoint can be configured like this instead:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <import resource="classpath:META-INF/cxf/cxf.xml" /> <import resource="classpath:META-INF/cxf/cxf-servlet.xml" /> <bean class="org.apache.cxf.jaxrs.JAXRSServerFactoryBean" init-method="create"> <property name="address" value="/service1"/> <property name="serviceBeans"> <ref bean="customerBean" /> </property> </bean> <bean id="customerBean" class="demo.jaxrs.server.CustomerService" /> </beans>
Spring AOP
CXF JAX-RS is capable of working with AOP interceptors applied to resource classes from Spring.
For example:
<beans xmlns:jaxrs="http://cxf.apache.org/jaxrs" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.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"/> <jaxrs:server id="bookservice" address="/"> <jaxrs:serviceBeans> <ref bean="bookstore"/> <ref bean="bookstoreInterface"/> </jaxrs:serviceBeans> </jaxrs:server> <bean id="bookstore" class="org.apache.cxf.systest.jaxrs.BookStore"/> <bean id="bookstoreInterface" class="org.apache.cxf.systest.jaxrs.BookStoreWithInterface"/> <aop:config> <aop:aspect id="loggingAspect" ref="simpleLogger"> <aop:before method="logBefore" pointcut="execution(* org.apache.cxf.systest.jaxrs.BookStore*.*(..))"/> <aop:after-returning method="logAfter" pointcut="execution(* org.apache.cxf.systest.jaxrs.BookStore*.*(..))"/> </aop:aspect> </aop:config> <bean id="simpleLogger" class="org.apache.cxf.systest.jaxrs.SimpleLoggingAspect"/> </beans>
Note that some AOP configuration is applied to two JAX-RS resource classes. By default Spring uses JDK dynamic proxies if a class to be proxified implements at least one interface or CGLIB proxies otherwise.
For example, here's how org.apache.cxf.systest.jaxrs.BookStoreWithInterface looks like:
public interface BookInterface { @GET @Path("/thosebooks/{bookId}/") @Produces("application/xml") Book getThatBook(Long id) throws BookNotFoundFault; } public class BookStoreWithInterface extends BookStoreStorage implements BookInterface { public Book getThatBook(@PathParam("bookId") Long id) throws BookNotFoundFault { return doGetBook(id); } @Path("/thebook") public Book getTheBook(@PathParam("bookId") Long id) throws BookNotFoundFault { return doGetBook(id); } }
In this case Spring will use a JDK dynamic proxy to wrap a BookStoreWithInterface class. As such it is important that the method which needs to be invoked such as getThatBook(...) will be part of the interface.
The other method, getTheBook() can not be dispatched to by a JAX-RS runtime as it's not possible to discover it through a JDK proxy. If this method also needs to be invoked then this method should either be added to the interface or CGLIB proxies have to be explicitly enabled (consult Spring AOP documentation for more details). For example:
<aop:config proxy-target-class="true"/>
Configuring JAX-RS services in container without Spring
If you prefer, you can register JAX-RS endpoints without depending on Spring with the help of CXFNonSpringJaxrsServlet :
<servlet> <servlet-name>CXFServlet</servlet-name> <display-name>CXF Servlet</display-name> <servlet-class> org.apache.cxf.jaxrs.servlet.CXFNonSpringJaxrsServlet </servlet-class> <init-param> <param-name>jaxrs.serviceClasses</param-name> <param-value> org.apache.cxf.systest.jaxrs.BookStore1, org.apache.cxf.systest.jaxrs.BookStore2 </param-value> </init-param> <init-param> <param-name>jaxrs.providers</param-name> <param-value> org.apache.cxf.systest.jaxrs.BookStoreProvider1, org.apache.cxf.systest.jaxrs.BookStoreProvider2 </param-value> </init-param> <!-- enables schema validation --> <init-param> <param-name>jaxrs.schemaLocations</param-name> <param-value> classpath:/WEB-INF/schemas/schema1.xsd classpath:/WEB-INF/schemas/schema2.xsd </param-value> </init-param> <!-- registers CXF in interceptors --> <init-param> <param-name>jaxrs.inInterceptors</param-name> <param-value> org.apache.cxf.systest.jaxrs.CustomInInterceptor </param-value> </init-param> <!-- registers CXF out interceptors --> <init-param> <param-name>jaxrs.outInterceptors</param-name> <param-value> org.apache.cxf.systest.jaxrs.CustomOutInterceptor </param-value> </init-param> <!-- registers extension mappings --> <init-param> <param-name>jaxrs.extensions</param-name> <param-value> xml=application/xml json=application/json </param-value> </init-param> <!-- registers contextual properties --> <init-param> <param-name>jaxrs.properties</param-name> <param-value> property1=value property2=value </param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
When service classes and providers are registered this way, the default life-cycle is 'singleton'. You can override it by setting a "jaxrs.scope" parameter with the value of 'prototype' (equivalent to per-request).
By default, the endpoint address is "/". One can provide a more specific value using a "jaxrs.address" parameter.
Note that multiple service or providers class names are separated by a comma. Users may want to use a "class.parameter.split.char" servlet parameter with the value "space" when
migrating from the older CXF versions were the space was used to separate multiple class names.
If the referenced service classes are not annotated with JAX-RS annotations then an external user model can also be linked to :
<servlet> <servlet-name>CXFServlet</servlet-name> <display-name>CXF Servlet</display-name> <servlet-class> org.apache.cxf.jaxrs.servlet.CXFNonSpringJaxrsServlet </servlet-class> <init-param> <param-name>jaxrs.serviceClasses</param-name> <param-value> org.apache.cxf.systest.jaxrs.BookStore </param-value> </init-param> <!-- link to the user model --> <init-param> <param-name>user.model</param-name> <param-value> classpath:/models/resources.xml </param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
A more portable way to register resource classes and providers with CXFNonSpringJaxrsServlet is to use a JAX-RS Application implementation :
<servlet> <servlet-name>CXFServlet</servlet-name> <display-name>CXF Servlet</display-name> <servlet-class> org.apache.cxf.jaxrs.servlet.CXFNonSpringJaxrsServlet </servlet-class> <init-param> <param-name>javax.ws.rs.Application</param-name> <param-value> org.apache.cxf.systest.jaxrs.BookApplication </param-value> </init-param> <!-- This parameter is recognized only starting from CXF 2.3.1 @ApplicationPath value will be ignored if this parameter is set to true --> <init-param> <param-name>jaxrs.application.address.ignore</param-name> <param-value>true</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
Note that Application.getClasses() method returns a set of per-request resource class names. Application.getSingletons() returns a list of singleton resource and provider classes.
Starting from CXF 2.3.7/2.4.3/2.5.0 it is possible to simple properties for resource and Application classes, providers and interceptors:
<servlet> <servlet-name>CXFServlet</servlet-name> <display-name>CXF Servlet</display-name> <servlet-class> org.apache.cxf.jaxrs.servlet.CXFNonSpringJaxrsServlet </servlet-class> <init-param> <param-name>javax.ws.rs.Application</param-name> <param-value> org.apache.cxf.systest.jaxrs.BookApplication (name=1 id=2) </param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
In the above example, org.apache.cxf.systest.jaxrs.BookApplication is expected to have setName and setId setters, with a single primitive or List parameter type.
Note that having the web-app_2_3.dtd DTD referenced from web.xml will likely prevent 'param-value' containing spaces and make it difficult to specify multiple providers like this:
<init-param> <param-name>jaxrs.providers</param-name> <param-value> mypackage.Provider1 mypackage.Provider2 </param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
In such cases consider moving to the web-app 2.5 schema or extending CXFNonSpringJaxrsProviders or introducing an Application.
Attaching JAXRS endpoints to an existing Jetty server
Here is a code fragment showing how it can be done with the help of CxfNonSpringJaxrsServlet :
CXFNonSpringJAXRSServlet cxf = new CXFNonSpringJaxrsServlet(); ... ServletHolder servlet = new ServletHolder(cxf); servlet.setInitParameter("javax.ws.rs.Application", "com.acme.MyServiceImpl"); servlet.setName("services"); servlet.setForcedPath("services"); root.addServlet(servlet, "/*");
JAX-RS RuntimeDelegate and Applications
If you have a JAX-RS Application implementation available and would like to minimize the interaction with the CXF JAX-RS specific API, you may want to use the JAX-RS RuntimeDelegate :
import javax.ws.rs.ext.RuntimeDelegate; import org.apache.cxf.endpoint.Server; import org.apache.cxf.jaxrs.JAXRSServerFactoryBean; RuntimeDelegate delegate = RuntimeDelegate.getInstance(); JAXRSServerFactoryBean bean = delegate.createEndpoint(new CustomApplication(), JAXRSServerFactoryBean.class); // before CXF 2.3.1 : // bean.setAddress("http://localhost:8080/services"); bean.setAddress("http://localhost:8080/services" + bean.getAddress()); Server server = bean.create(); server.start(); // and finally server.stop();
Note that the above code makes sure an @ApplicationPath value (if CustomApplication has this annotation) is taken into account.
Configuring JAX-RS services programmatically with Spring configuration file.
When using Spring explicitly in your code, you may want to follow this example :
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(new String[] {"/org/apache/cxf/jaxrs/spring/servers.xml"}); // 'simple' is the id of the jaxrs server bean JAXRSServerFactoryBean sfb = (JAXRSServerFactoryBean)ctx.getBean("simple"); sfb.create();
Note that in in this case your Spring configuration file should import cxf-extension-http-jetty.xml instead of cxf-servlet.xml :
<!-- <import resource="classpath:META-INF/cxf/cxf-servlet.xml" /> --> <import resource="classpath:META-INF/cxf/cxf-extension-http-jetty.xml" />
Lifecycle management
From Spring
By default, the service beans which are referenced directly from the jaxrs:server endpoint declarations are treated by the runtime as singleton JAX-RS root resources. For example:
<beans> <jaxrs:server id="customerService" address="/service1"> <jaxrs:serviceBeans> <ref bean="customerBean" /> </jaxrs:serviceBeans> </jaxrs:server> <!-- The scope attribute, if any, will be ignored. For example, adding the scope attribute with the value "request", "prototype", etc, won't have any effect, the customerBean will still be treated as a JAX-RS singleton by the runtime. --> <bean id="customerBean" class="demo.jaxrs.server.CustomerService" /> </beans>
Spring instantiates and injects the customerBean reference and the runtime will access this reference directly afterwards. Effectively, the scope attribute which may be present on the customerBean bean declaration is ignored in this case, unless the Spring AOP is used to enforce the required scope (see below for more information).
The 'serviceFactories' element or beanNames attribute has to be used for a 'prototype', 'request' and other Spring bean scopes be supported.
For example, the serviceFactories element can reference one or more beans of type 'org.apache.cxf.jaxrs.spring.SpringResourceFactory' which in turn reference the actual service beans.
<beans> <jaxrs:server id="customerService" address="/service1"> <jaxrs:serviceFactories> <ref bean="sfactory1" /> <ref bean="sfactory2" /> </jaxrs:serviceFactories> </jaxrs:server> <bean id="sfactory1" class="org.apache.cxf.jaxrs.spring.SpringResourceFactory"> <property name="beanId" value="customerBean1"/> </bean> <bean id="sfactory2" class="org.apache.cxf.jaxrs.spring.SpringResourceFactory"> <property name="beanId" value="customerBean2"/> </bean> <bean id="customerBean1" class="demo.jaxrs.server.CustomerRootResource1" scope="prototype"/> <bean id="customerBean2" class="demo.jaxrs.server.CustomerRootResource2" scope="prototype"/> </beans>
In this example, the jaxrs:server endpoint has two JAX-RS root resources (customerBean1 and customerBean2) with the Spring 'prototype' scope.
Other scopes can also be supported.
If using the jaxrs:serviceFactories element seems a bit verbose then the 'beanNames' attribute can be used instead:
<beans> <jaxrs:server id="customerService" address="/service1" beanNames="customerBean1 customerBean2"/> <bean id="customerBean1" class="demo.jaxrs.server.CustomerRootResource1" scope="prototype"/> <bean id="customerBean2" class="demo.jaxrs.server.CustomerRootResource2" scope="prototype"/> </beans>
The beanNames attribute lists the names/ids of service beans separated by space. The jaxrs:serviceFactories element has to be used when users register custom CXF JAX-RS ResourceProvider implementations.
Another approach toward supporting complex scopes in Spring is to use Spring AOP. For example, the following fragment shows how to have the Spring "request" scope supported:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:jaxrs="http://cxf.apache.org/jaxrs" xmlns:cxf="http://cxf.apache.org/core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd"> <import resource="classpath:META-INF/cxf/cxf.xml"/> <import resource="classpath:META-INF/cxf/cxf-servlet.xml"/> <bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"/> <jaxrs:server id="example" address="/"> <jaxrs:serviceBeans> <bean class="org.apache.cxf.systest.jaxrs.CustomerService" scope="request"><aop:scoped-proxy /></bean> </jaxrs:serviceBeans> </jaxrs:server> </beans>
in addition, the following servlet listener has to be added to the web.xml:
<listener> <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class> </listener>
The request-scoped service bean instances (example, org.apache.cxf.systest.jaxrs.CustomerService instances) are not actually available at the initialization time thus one limitation of the above configuration is that it is not possible to inject JAX-RS contexts into these service beans. This is not a show-stopper because contexts such as UriInfo can be passed in as resource method parameters. However, if the injection into the fields or via method setters is required then a little customization of the org.apache.cxf.jaxrs.spring.SpringResourceFactory will do the trick. Particularly, the Spring ApplicationContext reports that a request-scoped bean is a singleton but the JAX-RS runtime can not inject thread-local proxies given that the actual instance is not available as explained above; in fact, the request-scoped beans are not really JAX-RS singletons. Thus a simple custom factory like this one is needed and it has to be used the following way:
<beans> <jaxrs:server id="customerService" address="/service1"> <jaxrs:serviceFactories> <bean class="org.apache.cxf.systest.jaxrs.RequestScopeResourceFactory"> <property name="beanId" value="customerBean"/> </bean> </jaxrs:serviceFactories> </jaxrs:server> <bean id="customerBean" class="org.apache.cxf.systest.jaxrs.CustomerService" scope="request"><aop:scoped-proxy /></bean> </beans>
The above configuration makes sure that the CXF JAX-RS runtime injects the values at the request time given that the customerBean bean is not seen as a JAX-RS singleton. This approach is only needed if the injection of contexts is required.
With CXFNonSpringJaxrsServlet
CXFNonSpringJaxrsServlet uses 'Singleton' as a default scope for service classes specified by a "jaxrs.serviceClasses" servlet parameter. It can be overridden by setting a "jaxrs.scope" parameter to a "prototype" value or by not using the "jaxrs.serviceClasses" parameter at all and registering a JAXRS Application implementation instead. Please see the section describing CXFNonSpringJaxrsServlet for more details.
CXFNonSpringJaxrsServlet can support singleton scopes for classes with constructors expecting JAXRS contexts, at the moment it can only inject ServletContext or ServletConfig contexts :
@Path("/") public class SingletonResourceClass { public SingletonResourceClass(@Context ServletContext context, @Context ServletConfig context2) {} }
Programmatically
JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean(); sf.setResourceClass(CustomerService.class); sf.setResourceProvider(new SingletonResourceProvider(new CustomerService())); sf.setResourceClass(CustomerService2.class); sf.setResourceProvider(new PerRequestResourceProvider(CustomerService.class));
PostConstruct and PreDestroy
Bean methods annotated with @PostConstruct and @PreDestroy annotations will be called as expected by the scope rules.
Singleton beans will have their postconstruct method called when the endpoint is created. If a given singleton resource instance was created by Spring then its predestroy method will also be called after, for example, the web application which uses it is about to be unloaded. At the moment singletons created by CXFNonSpringJaxrsServlet or programmatically will only have their postconstruct method (if any) called.
Prototype beans will have their postconstruct and predestroy method called before a resource method is invoked and immediately after the invocation has returned but before the response has actually been serialized. You can indicate that the predestroy method has to be called after the request has completely gone out of scope (that is after the response body if any has been written to the output stream) by adding an "org.apache.cxf.service.scope" property with the value set to "request".
You can also register a custom Spring resource factory by extending org.apache.cxf.jaxrs.spring.SpringResourceFactory or providing a more sophisticated implementation.
Locating custom resources in web applications
Resources like schemas, custom XSLT templates and user models are typically referenced using a classpath: prefix. Thus one can add them to a WEB-INF/classes folder in a given web application.
Since CXF 2.2.3 one can put them directly under WEB-INF, for example into WEB-INF/xslt, WEB-INF/schemas, WEB-INF/model and referencing them like 'classpath:/WEB-INF/xslt/template.xsl'.
Multiple endpoints and resource classes
One can configure as many jaxrs:server endpoints as needed for a given application, with every endpoint possibly providing an alternative path to a single resource bean. Every endpoint can employ as many shared or unique resource classes as needed, and have common or different providers.
Sharing providers between multiple endpoints
One way to share multiple providers between multiple endpoints is to refer to the same provider bean from within jaxrs:provider sections:
<beans> <jaxrs:server id="customerService" address="/service1"> <jaxrs:serviceBeans> <bean class="org.apache.cxf.systest.jaxrs.CustomerService"/> </jaxrs:serviceBeans> <jaxrs:providers> <bean ref="customProvider"/> </jaxrs:providers> </jaxrs:server> <jaxrs:server id="customerService" address="/service2"> <jaxrs:serviceBeans> <bean class="org.apache.cxf.systest.jaxrs.CustomerService2"/> </jaxrs:serviceBeans> <jaxrs:providers> <bean ref="customProvider"/> </jaxrs:providers> </jaxrs:server> <bean id="customProvider" class="org.apache.cxf.systest.jaxrs.CustomerProvider"/> </beans>
Starting from CXF 2.7.2 it is possible to register provider directly on the bus as the bus properties and share them between all the providers using this bus:
<beans> <cxf:bus> <cxf:properties> <entry key="javax.ws.rs.ext.ExceptionMapper" value-ref="exceptionMapper"/> </cxf:properties> </cxf:bus <bean id="exceptionMapper" class="org.apache.cxf.systest.jaxrs.RuntimeExceptionMapper"/> <jaxrs:server id="customerService" address="/service1"> <jaxrs:serviceBeans> <bean class="org.apache.cxf.systest.jaxrs.CustomerService"/> </jaxrs:serviceBeans> </jaxrs:server> <jaxrs:server id="customerService" address="/service2"> <jaxrs:serviceBeans> <bean class="org.apache.cxf.systest.jaxrs.CustomerService2"/> </jaxrs:serviceBeans> </jaxrs:server> </beans>
Note a global exception mapper has been registered using the name of interface, "javax.ws.rs.ext.ExceptionMapper", which all the exception mappers have to implement.
Note that once can register global per-bus providers using "javax.ws.rs.ext.ExceptionMapper", "javax.ws.rs.ext.MessageBodyReader" or "javax.ws.rs.ext.MessageBodyWriter" bus properties with the registered providers expected to implement either of these interfaces.
Alternatively, one can have all the providers (JAX-RS and CXF-specific) registered with a bus using "org.apache.cxf.jaxrs.bus.providers" list property:
<beans xmlns:util="http://www.springframework.org/schema/util"> <cxf:bus> <cxf:properties> <entry key="org.apache.cxf.jaxrs.bus.providers" value-ref="busProviders"/> </cxf:properties> </cxf:bus <util:list id="busProviders"> <ref bean="exceptionMapper"/> <ref bean="customMessageBodyReader"/> <ref bean="customMessageBodyWriter"/> </util:list> <bean id="exceptionMapper" class="org.apache.cxf.systest.jaxrs.RuntimeExceptionMapper"/> <bean id="customMessageBodyReader" class="org.apache.cxf.systest.jaxrs.CustomReader"/> <bean id="customMessageBodyWriter" class="org.apache.cxf.systest.jaxrs.CustomWriter"/> <jaxrs:server id="customerService" address="/service1"> <jaxrs:serviceBeans> <bean class="org.apache.cxf.systest.jaxrs.CustomerService"/> </jaxrs:serviceBeans> </jaxrs:server> <jaxrs:server id="customerService" address="/service2"> <jaxrs:serviceBeans> <bean class="org.apache.cxf.systest.jaxrs.CustomerService2"/> </jaxrs:serviceBeans> </jaxrs:server> </beans>
Dynamic servlets and a single JAX-RS endpoint
Note: this is not required by default starting from CXF 3.0.0-milestone1
In some advanced cases you may want to dynamically add new servlets (CXFServlet or CXFNonSpringJaxrsServlet) with all of them serving the same JAX-RS endpoints. In this case you most likely want to configure servlets so that the CXF endpoint address is not overridden :
{code:xml} <servlet> <servlet-name>CXFServlet</servlet-name> <display-name>CXF Servlet</display-name> <servlet-class> org.apache.cxf.transport.servlet.CXFServlet </servlet-class> <init-param> <param-name>config-location</param-name> <param-value>/WEB-INF/beans1.xml</param-value> </init-param> <init-param> <param-name>disable-address-updates</param-name> <param-value>true</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
Auto-discovery of root resources and providers
Starting from CXF 3.0.0 it is possible to enable the auto-discovery of JAX-RS roots and providers with the regular CXF JAX-RS endpoint declarations done in XML . Currently it is only possible with Spring. Patch supporting it for Blueprint is available and will be dealt with asap.
Spring
<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-2.0.xsd http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd"> <import resource="classpath:META-INF/cxf/cxf.xml" /> <!-- Discover all root resources and providers in packages starting from "a.b.c" --> <jaxrs:server address="/customers" basePackages="a.b.c"/> <!-- Discover a root resource and providers in packages starting from "a.b.c" and "d.e.f", Note that in case of jaxrs:client, having multiple discoverable root resources can lead to unexpected results, as only the single root can be recognized --> <jaxrs:client address="http://customers" basePackages="a.b.c,d.e.f"/> </beans>
Note the above does not require Spring annotations such as @Component added to JAX-RS provider or resources.
If you prefer doing a pure Spring-based auto-discovery you can have @Component added to JAX-RS application classes and do
<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-2.0.xsd http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd"> <import resource="classpath:META-INF/cxf/cxf.xml" /> <!-- Discover all root resources and providers in packages starting from "a.b.c" --> <jaxrs:server address="/customers"/> <context:component-scan base-package="a.b.c"/> </beans>
Servlet and Application Container Configuration
Please see this page for more information.
Starting from CXF 3.1.0: org.apache.cxf.jaxrs.servlet.sci.JaxrsServletContainerInitializer implementing javax.servlet.ServletContainerInitializer is shipped in a cxf-rt-rs-http-sci module.
Adding this module will support no-web.xml and other JAX-RS deployments depending on the container auto-discovery mechanism as described in a 2.3.2 section of JSR-339 (JAX-RS 2.0).
3 Comments
Andres Gaudioso
At Spring-Boot example "implementor" is not defined.
spring-boot-starter-parent version 1.2.5-RELEASE
CXF version 3.1.3.
@SpringBootApplication
@ImportResource({ "classpath:META-INF/cxf/cxf.xml" })
public class SampleCxfApplication extends SpringBootServletInitializer {
@Autowired
private ApplicationContext applicationContext;
.....
@Bean
ServletRegistrationBean servletRegistrationBean(ApplicationContext context) {
return new ServletRegistrationBean(new CXFServlet(), "/*");
}
@Bean
Server helloRestService() {
Bus bus = (Bus) applicationContext.getBean(Bus.DEFAULT_BUS_ID);
JAXRSServerFactoryBean server = new JAXRSServerFactoryBean();
server.setBus(bus);
server.setAddress("/hello");
server.setServiceBean(new HelloWorld());
server.setProvider(new JacksonJsonProvider());
return server.create();
}
}
Sergey Beryozkin
This is the latest code, what do you think needs to be enhanced there ? Can you create a patch ?
Andres Gaudioso
Everything seems right. Thanks