You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 19 Next »

Introduction 

The JAX-RS 2.0 specification (JSR-339) mandates the support of CDI 1.1 (JSR-346) and Apache CXF starting from the version 3.0 introduces the initial support of this feature. As the starting point, the emphasis has been done on supporting embedded Jety 8/9 and Tomcat 7/8 containers as primary deployment (though other application servers will be supported in the future). 

Architecture and design 

At the moment, the integration of Apache CXF and CDI revolves around two key components, residing in new module called cxf-integration-cdi

  • CXFCdiServlet servlet
  • JAXRSCdiResourceExtension portable CDI extension

The fact of including cxf-integration-cdi as a dependency allows  JAXRSCdiResourceExtension  portable CDI extension to be discovered by CDI container. The  JAXRSCdiResourceExtension creates the instance of the Bus and registers it with BeanManager. From this point, the  Bus instance is a regular CDI bean (with @Application scope) available for injection. This instance of the  Bus is being injected into CXFCdiServlet servlet once it is initialized by servlet container.

Depending on the design, JAXRSCdiResourceExtension may use zero-based configuration approach or rely on particular JAX-RS Application instances. The org.apache.cxf.cdi.CXFCdiServlet should be configured as well (more examples for programmatic and WAR scenarios below).

Zero-based Configuration

If the Apache CXF application contains only one single instance of JAX-RS Application (annotated with @ApplicationPath) with no singletons and classes defined, the following rules are being applied by JAXRSCdiResourceExtension in order to configure and publish the configured JAX-RS resources:

  • the instance of the JAX-RS Application (annotated with @ApplicationPath) is being created and registered with BeanManager
  • all instances of the discovered JAX-RS providers (annotated with @Provider) are being created and registered with BeanManager
  • all instances of the discovered JAX-RS resources (annotated with @Path) are being created and registered with BeanManager

Lastly, the instance of the JAXRSServerFactoryBean is being created and configured with all service beans and providers discovered before. Additionally, the providers are enriched with the services for javax.ws.rs.ext.MessageBodyReader and javax.ws.rs.ext.MessageBodyWriter, loaded via ServiceLoader. From this moment, Apache CXF application is ready to serve the requests. The quick example is shown below.

The empty JAX-RS Application:

@ApplicationPath("/api")
public class BookStoreApplication extends Application {
}

And one JAX-RS resource:

@Path("/bookstore/")
public class BookStore {
    @Inject private BookStoreService service;
    
    @GET
    @Path("/books/{bookId}")
    @Produces(MediaType.APPLICATION_JSON)
    public Book getBook(@PathParam("bookId") String id) {
        return service.get(id);
    }
}

Customized Configuration

If the Apache CXF application contains one or more instances of JAX-RS Application (annotated with @ApplicationPath) with singletons or classes defined, the following rules are being applied by JAXRSCdiResourceExtension in order to configure and publish the configured JAX-RS resources:

  • the instance of each JAX-RS Application (annotated with @ApplicationPath) is being created and registered with BeanManager
  • for each JAX-RS Application instance created before, the instance of the JAXRSServerFactoryBean is being created and configured in a way that application's singletons/classes are being splitted to providers (annotated with @Provider), service beans (annotated with @Path) and features (implementation of org.apache.cxf.feature.Feature)

From this moment, Apache CXF application is ready to serve the requests. The quick example is shown below.

The configured JAX-RS Application:

@ApplicationPath("/custom")
public class BookStoreCustomApplication extends Application {
    @Inject private BookStore bookStore;
    
    @Override
    public Set< Object > getSingletons() {
        return Sets.< Object >newHashSet(
            bookStore, 
            new JacksonJsonProvider(),
            new ValidationExceptionMapper(),
            new JAXRSBeanValidationFeature());
    }
}

And one JAX-RS resource:

@Path("/bookstore/")
public class BookStore {
    @Inject private BookStoreService service;
    
    @GET
    @Path("/books/{bookId}")
    @Produces(MediaType.APPLICATION_JSON)
    public Book getBook(@PathParam("bookId") String id) {
        return service.get(id);
    }
}

Deploying with embedded Jetty 8/9 (programmatic configuration)

With Jetty 8/9 it possible to create fully embeddable REST / JAX-RS servers without web.xml or WAR files involved. For Apache CXF applications which are using CDI 1.1, the CXFCdiServlet servlet should be used as a starting point. Following example demonstrates the necessary configuration points in order to create embedded Jetty 8/9 instance. As a CDI 1.1 implementation, JBoss Weld 2.0 is being used.

System.setProperty("java.naming.factory.url", "org.eclipse.jetty.jndi");
System.setProperty("java.naming.factory.initial", "org.eclipse.jetty.jndi.InitialContextFactory");

// Register and map the dispatcher servlet
final Server server = new Server(<port>);
final ServletHolder servletHolder = new ServletHolder(new CXFCdiServlet());
final ServletContextHandler context = new ServletContextHandler();         
context.setContextPath(<context path>);          
context.addEventListener(new Listener());         
context.addEventListener(new BeanManagerResourceBindingListener());
context.addServlet(servletHolder, "/rest/*");
server.setHandler(context);
server.start();

This code snippet is enough to trigger the CDI portable extension discovery, to perform the configuration (depending on JAX-RS Applications present) and to wire up all defined dependencies together.

Deploying with embedded Jetty 8/9 (WAR-based deployment) 

Another option to deploy Apache CXF application with CDI 1.1 support and embedded Jetty 8/9 is by using web.xml descriptor and WAR-like deployment structure. In this case, the Apache CXF application needs to declare CXFCdiServlet servlet (and its mappings) and, if required, CDI-specific listeners (in the example below the JBoss Weld 2.0 is being used as CDI 1.1 container).

<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <listener>
        <listener-class>org.jboss.weld.environment.servlet.Listener</listener-class>
    </listener>

    <servlet>
        <servlet-name>CXFServlet</servlet-name>
        <display-name>CXF Servlet</display-name>
        <servlet-class>org.apache.cxf.cdi.CXFCdiServlet</servlet-class>    
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>CXFServlet</servlet-name>
        <url-pattern>/rest/*</url-pattern>
    </servlet-mapping>

    <resource-env-ref>
        <resource-env-ref-name>BeanManager</resource-env-ref-name>
        <resource-env-ref-type>javax.enterprise.inject.spi.BeanManager
        </resource-env-ref-type>
    </resource-env-ref>
</web-app>

The server initialization in this case looks simpler.

System.setProperty("java.naming.factory.url", "org.eclipse.jetty.jndi");
System.setProperty("java.naming.factory.initial", "org.eclipse.jetty.jndi.InitialContextFactory");

final Server server = new Server(<port>);
final WebAppContext context = new WebAppContext();
context.setContextPath(<context path>);
context.setWar(<path to WAR folder/file>);
context.setServerClasses(new String[] { "org.eclipse.jetty.servlet.ServletContextHandler.Decorator" });
        
HandlerCollection handlers = new HandlerCollection();
handlers.setHandlers(new Handler[] {context, new DefaultHandler()});
server.setHandler(handlers);
server.start();

Please notice, usage of Jetty-specific server classes ("org.eclipse.jetty.servlet.ServletContextHandler.Decorator") is very important to allow CDI 1.1 injections (backed by JBoss Weld 2.0) to work seamlessly across servlets / listeners / filters. It is not stricktly necessary for Apache CXF (everything will work as expected) but complex applications would definitely benefit from that.

Deploying with embedded Tomcat 7/8 (WAR-based deployment) 

In case of embedded Tomcat 7/8, Apache CXF application with CDI 1.1 support could be deployed with web.xml descriptor and WAR-like deployment structure, similarly to Jetty 8/9 WAR-based deployment. Apache CXF application needs to declare CXFCdiServlet servlet (and its mappings) and, if required, CDI-specific listeners (in the example below the JBoss Weld 2.0 is being used as CDI 1.1 container).

<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <listener>
        <listener-class>org.jboss.weld.environment.servlet.Listener</listener-class>
    </listener>

    <servlet>
        <servlet-name>CXFServlet</servlet-name>
        <display-name>CXF Servlet</display-name>
        <servlet-class>org.apache.cxf.cdi.CXFCdiServlet</servlet-class>    
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>CXFServlet</servlet-name>
        <url-pattern>/rest/*</url-pattern>
    </servlet-mapping>

    <resource-env-ref>
        <resource-env-ref-name>BeanManager</resource-env-ref-name>
        <resource-env-ref-type>javax.enterprise.inject.spi.BeanManager
        </resource-env-ref-type>
    </resource-env-ref>
</web-app>

Tomcat 7/8 server has a different API, by still quite simple initialization procedure.

final Tomcat server = new Tomcat();
server.setPort(<port>);

final File base = createTemporaryDirectory();
server.setBaseDir(base.getAbsolutePath());    
    
server.getHost().setAppBase(base.getAbsolutePath());
server.getHost().setAutoDeploy(true);
server.getHost().setDeployOnStartup(true);
            
server.addWebapp(<context path>, <path to WAR folder/file>);   
server.start();

 

 

  • No labels