JAX-RS services using CXF and Karaf

Apache CXF provides a really great layer to implement JAX-RS services. Especially, it fully supports OSGi, including Blueprint. It means that you can very easily create and deploy your REST services in a Apache Karaf container.

In this example, we will see how to list all Karaf features via a REST service.

This exemple is composed by three modules:
– common is an OSGi bundle containing resources shared between the JAX-RS server and the clients. Basically, it contains the service interface and the objects used in the service.
– service is an OSGi bundle providing the implementation of the service interface
– client is a simple Main class that use CXF JAX-RS client

Common bundle

This bundle contains the interface describing the behavior of the REST service. We define it in the FeaturesRestService:


package net.nanthrax.examples.jaxrs.common;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import java.util.Collection;

/**
* REST service to manipulate Karaf features
*/
@Path("/")
public interface FeaturesRestService {

  /**
  * Returns an explicit collection of all features in XML format in response to HTTP GET requests.
  * @return the collection of features
  */
  @GET
  @Path("/features")
  @Produces("application/xml")
  public Collection getFeatures() throws Exception;

}

In this interface, the getFeatures() method returns a collection of FeatureWrapper. This object is an object that will be sent to the client. It contains JAXB and JAX-RS annotations:


package net.nanthrax.examples.jaxrs.common;

import javax.ws.rs.Path;
import javax.xml.bind.annotation.XmlRootElement;

/**
* Wrapper to a Karaf feature including JAXB nad JAX-RS annotations.
*/
@XmlRootElement(name = "Feature")
public class FeatureWrapper {

  private String name;
  private String version;

  public FeatureWrapper() { }

  public FeatureWrapper(String name, String version) {
    this.name = name;
    this.version = version;
  }

  @Path("name")
  public String getName() {
    return this.name;
  }

  public void setName(String name) {
    this.name = name;
  }

  @Path("version")
  public String getVersion() {
    return this.version;
  }

  public void setVersion(String version) {
    this.version = version;
  }

}

Now, we just need to define the Maven POM to build an OSGi bundle:


<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">

  <modelVersion>4.0.0</modelVersion>

  <parent>
    <groupId>net.nanthrax.examples</groupId>
    <artifactId>jaxrs-blueprint</artifactId>
    <version>1.0-SNAPSHOT</version>
    <relativePath>../pom.xml</relativePath>
  </parent>

  <groupId>net.nanthrax.examples.jaxrs-blueprint</groupId>
  <artifactId>net.nanthrax.examples.jaxrs-blueprint.common</artifactId>
  <packaging>bundle</packaging>

  <dependencies>
    <dependency>
      <groupId>org.apache.cxf</groupId>
      <artifactId>cxf-rt-frontend-jaxrs</artifactId>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.felix</groupId>
        <artifactId>maven-bundle-plugin</artifactId>
        <configuration>
          <instructions>
            <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
            <Export-Package>
              net.nanthrax.examples.jaxrs.common*;version=${project.version}
            </Export-Package>
          </instructions>
        </configuration>
      </plugin>
    </plugins>
  </build>

</project>

We can note that we have a dependencies to cxf-rt-frontend-jaxrs. This artifact provides us the JAX-RS annotations.
We don’t need additional dependencies for JAXB as it’s included in the JDK.

Service bundle

This bundle contains the implementation of the REST service.

We find two things in this bundle:
– the FeaturesRestServiceImpl class implementing the FeaturesRestService interface.
– the Blueprint descriptor in OSGI-INF/blueprint containing the bean definition of the FeaturesRestServiceImpl and the configuration of the JAX-RS server

The FeaturesRestServiceImpl doesn’t contain any JAX-RS annotations. It’s a pure implementation which use the Karaf FeaturesService OSGi service to get the list of Karaf features. It populates a collection of FeatureWrapper:


package net.nanthrax.examples.jaxrs.service;

import net.nanthrax.examples.jaxrs.common.FeatureWrapper;
import net.nanthrax.examples.jaxrs.common.FeaturesRestService;
import org.apache.karaf.features.Feature;
import org.apache.karaf.features.FeaturesService;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
* Implementation of the Features REST service.
*/
public class FeaturesRestServiceImpl implements FeaturesRestService {

  private FeaturesService featuresService;

  public FeaturesService getFeaturesService() {
    return this.featuresService;
  }

  public void setFeaturesService(FeaturesService featuresService) {
    this.featuresService = featuresService;
  }

  @Override
  public Collection getFeatures() throws Exception {
    List featuresWrapper = new ArrayList();
    Feature[] features = featuresService.listFeatures();
    for (int i = 0; i < features.length; i++) {      FeatureWrapper wrapper = new FeatureWrapper(features[i].getName(), features[i].getVersion());      featuresWrapper.add(wrapper);    }    return featuresWrapper;  }}

The Blueprint descriptor (in OSGI-INF/blueprint/rest.xml) is responsible:
- to get the reference to the Karaf FeaturesService OSGi service and inject it in the FeaturesRestServiceImpl bean
- to configure the JAX-RS server and define the FeaturesRestServiceImpl as a service bean
- optionally, we enable debug on the CXF internal bus


<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.0.0"
  xmlns:jaxws="http://cxf.apache.org/blueprint/jaxws"
  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/jaxws http://cxf.apache.org/schemas/blueprint/jaxws.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="karafFeaturesService" address="/karaf">
    <jaxrs:serviceBeans>
      <ref component-id="karafFeaturesServiceBean"/>
    </jaxrs:serviceBeans>
  </jaxrs:server>

  <bean id="karafFeaturesServiceBean" class="net.nanthrax.examples.jaxrs.service.FeaturesRestServiceImpl">
    <property name="featuresService" ref="featuresService"/>
  </bean>

  <reference id="featuresService" interface="org.apache.karaf.features.FeaturesService"/>

&tt;/blueprint>

Finally, the service bundle POM creates an OSGi bundle importing package from Karaf (for the features service) and the common (for the interface and the FeatureWrapper):


<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">

  <modelVersion>4.0.0</modelVersion>

  <parent>
    <groupId>net.nanthrax.examples</groupId>
    <artifactId>jaxrs-blueprint</artifactId>
    <version>1.0-SNAPSHOT</version>
    <relativePath>../pom.xml</relativePath>
  </parent>

  <groupId>net.nanthrax.examples.jaxrs-blueprint</groupId>
  <artifactId>net.nanthrax.examples.jaxrs-blueprint.service</artifactId>
  <packaging>bundle</packaging>

  <dependencies>
    <dependency>
      <groupId>net.nanthrax.examples.jaxrs-blueprint</groupId>
      <artifactId>net.nanthrax.examples.jaxrs-blueprint.common</artifactId>
      <version>${project.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.karaf.features</groupId>
      <artifactId>org.apache.karaf.features.core</artifactId>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.felix</groupId>
        <artifactId>maven-bundle-plugin</artifactId>
        <configuration>
          <instructions>
            <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
            <Export-Package>
              net.nanthrax.examples.jaxrs.service*;version=${project.version}
            </Export-Package>
            <Import-Package>
              net.nanthrax.examples.jaxrs.common*;version=${project.version},
              org.apache.karaf.features*;version="[2,4)",
              *
            </Import-Package>
          </instructions>
        </configuration>
      </plugin>
    </plugins>
  </build>

</project>

Deployment of the REST service in Karaf

Now, we are ready to deploy our REST service into Karaf.

The first step to perform is the installation of the CXF feature:


karaf@root> features:addurl mvn:org.apache.cxf.karaf/apache-cxf/2.4.2/xml/features
karaf@root> features:install cxf-jaxrs

The CXF feature provides the CXF bundle (with the core engine) and the JAX-RS frontend.

Now, we can install the common bundle and the service bundle:


karaf@root> osgi:install -s mvn:net.nanthrax.examples.jaxrs-blueprint/net.nanthrax.examples.jaxrs-blueprint.common/1.0-SNAPSHOT
karaf@root> osgi:install -s mvn:net.nanthrax.examples.jaxrs-blueprint/net.nanthrax.examples.jaxrs-blueprint.service/1.0-SNAPSHOT

Our REST service is now available. The JAX-RS server uses the OSGi HTTP service of Karaf (the Karaf http feature is automatically installed by CXF). The default port number of the HTTP service (which use Jetty as web container) is 8181.

By default, CXF frontends/servlets use the cxf context root.

So it means that, if you point your browser on http://localhost:8181/cxf/karaf/features, we will see the XML formatted list of all Karaf features:


<Features>
  <Feature>
    <name>saaj-impl</name>
    <version>1.3.2</version>
   /Feature<
  <Feature>
    <name>abdera</name>
    <version>1.1.2</version>
  </Feature>
...

In detail, the URL http://localhost:8181/cxf/karaf/features comes from:
- the port (8181) is the default one of the Karaf HTTP service
- the context root (cxf) is the default one used by CXF
- the "karaf" context is defined in the JAX-RS server (in the Blueprint descriptor, by the address attribute)
- the "features" is defined in the FeaturesRestService interface (by the @Path("/features") annotation on the getFeatures() method)

REST client

CXF also provide a REST client, very easy to use:


package net.nanthrax.examples.jaxrs.client;

import net.nanthrax.examples.jaxrs.common.FeatureWrapper;
import org.apache.cxf.jaxrs.client.WebClient;

import java.util.ArrayList;
import java.util.List;

/**
* Simple JAX-RS client.
*/
public final class Main {

  public static void main(String[] args) throws Exception {
    WebClient webClient = WebClient.create("http://localhost:8181/cxf/karaf/features/");
    List features = new ArrayList(webClient.getCollection(FeatureWrapper.class));
    for (FeatureWrapper feature : features) {
      System.out.println("Feature " + feature.getName() + "/" + feature.getVersion());
    }
  }

}

Conclusion

REST service is really easy to do and Apache CXF fully supports the OSGi environment. We can use Blueprint (for OSGi services lookup) to describe and start the JAX-RS server. That's why Karaf is a great container for this kind of services (and a lot of others ;)).

You can find a bunch of others REST services examples in Talend Service Factory (TSF):

- the Talend SF runtime: http://www.talend.com/download.php?src=HomePage#AI
- the Talend SF examples: http://www.talend.com/resources/documentation.php#SF

Comments

Popular posts from this blog

Quarkus and "meta" extension

Getting started with Apache Karaf Minho

Apache Karaf Minho and OpenTelemetry