Tworzenie RESTful Web Services przy użyciu Apache CXF

Na grupie dyskusyjnej Lódź JUG Paweł Włodarski zapoczątkował temat “Usługi typu REST “. W jednej z odpowiedzi napisałem, iż do tworzenia usług za pomocą JAX-RS można wykorzystać framework Apache CXF, w dzisiejszym wpisie przedstawię w jaki sposób zaimplementować taką usługę używając właśnie Apache CXF oraz Spring Framework.

Utwórzmy nasz projekt przy użyciu Maven 2:

mvn archetype:create -DgroupId=org.holewa.restapp -DartifactId=restapp -DarchetypeArtifactId=maven-archetype-webapp

Po chwili powinniśmy zobaczyć utworzoną strukturę aplikacji, która znajduje się w katalogu restapp.
Ponieważ w naszym projekcie użyjemy frameworków Spring i Apache CXF stąd też musimy je dodać do naszego pom.xml, po modyfikacji nasz pom powinien wyglądać następująco:

<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>org.holewa.restapp</groupId>
    <artifactId>restapp</artifactId>
    <packaging>war</packaging>
    <version>1.0-SNAPSHOT</version>
    <build>
        <finalName>restapp</finalName>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.0.2</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring</artifactId>
            <version>2.5.6</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-bundle-jaxrs</artifactId>
            <version>2.1.3</version>
        </dependency>
    </dependencies>
    <repositories>
        <repository>
            <id>Atlassian</id>
            <layout>default</layout>
            <url>https://m2proxy.atlassian.com/repository/public</url>
        </repository>
    </repositories>
</project>

Powyższy fragment zawiera zależności od Spring Framework i Apache CXF JAX-RS bundle, czyli specjalnej paczki Apache CXF przeznaczonej do implementacji RESTful Web Services w oparciu o specyfikację JAX-RS.
Dodałem również jedno repozytorium Maven 2, jest ono potrzebne aby Maven mógł pobrać zależność od Apache Abdera (jednej z zależności Apache CXF) i wtyczkę maven-compiler-plugin zdefiniowaną dla wersji 1.6 Javy gdyż kod, który zobaczycie w tym przykładzie był kompilowany właśnie z tą wersją.

Teraz czas na konfiguracje deskryptora naszej aplikacji webowej, modyfikujemy plik web.xml sprowadzając go do następującej postaci:

<!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>
    <display-name>REST Application</display-name>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/applicationContext.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>/services/*</url-pattern>
    </servlet-mapping>
</web-app>

Zdefiniowaliśmy tutaj listener który odpowiada za uruchomienie kontakstu Spring’a oraz servlet Apache CXF, będzie on odpowiedzialny za obsługę wywołań naszych usług.

Utworzymy teraz dwie klasy o nazwach Element i Elements, które zawierać będą adnotacje JAXB.

Klasa Element:

package org.holewa.restapp;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = “element”)
class Element {

    private Integer id;

    private String name;

    public Element() {
    }

    public Element(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

}

Klasa Elements:

package org.holewa.restapp;

import java.util.List;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElements;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;

@XmlRootElement(name = “elements”)
public class Elements {

    @XmlElements({
        @XmlElement(name = “element”, type = Element.class)
    })
    List<Element> elements;

    @XmlTransient
    public List<Element> getElements() {
        return elements;
    }

    public void setElements(List<Element> elements) {
        this.elements = elements;
    }
}

Klasy te zostaną wykorzystane do przedstawienia prostych możliwości interfejsu REST’owego, posłużą one jako przykładowe obiekty zwracane przez usługę RESTful web services.

Javowa implementacja tej usługi znajduje się poniżej:

Klasa ElementEndpointImpl:

package org.holewa.restapp;

import java.util.ArrayList;
import java.util.List;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.ProduceMime;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;

public class ElementEndpointImpl {

    @Context
    UriInfo uriInfo;
    private List<Element> elements;

    public ElementEndpointImpl() {
        elements = new ArrayList<Element>();
        elements.add(new Element(1, “First”));
        elements.add(new Element(2, “Second”));
    }

    @ProduceMime(value = “application/xml”)
    @GET
    @Path(value = “/elements”)
    public Elements getElements() {
        Elements result = new Elements();
        result.setElements(elements);
        return result;
    }

    @ProduceMime(value = “application/xml”)
    @GET
    @Path(value = “/elements/{id}”)
    public Element getElement(@PathParam(value = “id”) Integer id) {
        Element result = null;
        for (Element e : elements) {
            if (e.getId().equals(id)) {
                result = e;
                break;
            }
        }
        return result;
    }
   
}

Na koniec konfiguracja Apache CXF w pliku applicationContext.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”
       xmlns:cxf=“http://cxf.apache.org/core”
       xsi:schemaLocation=“http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
        http://cxf.apache.org/core
        http://cxf.apache.org/schemas/core.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-servlet.xml”/>
    <import resource=“classpath:META-INF/cxf/cxf-extension-jaxrs-binding.xml”/>

    <bean id=“elementEndpoint” class=“org.holewa.restapp.ElementEndpointImpl”/>

    <jaxrs:server id=“restWSServer” address=“/”>
        <jaxrs:serviceBeans>
            <ref bean=“elementEndpoint”/>
        </jaxrs:serviceBeans>
    </jaxrs:server>

    <cxf:bus>
        <cxf:features>
            <cxf:logging/>
        </cxf:features>
    </cxf:bus>

</beans>

To już wszystko, budujemy naszą aplikację za pomocą Maven’a:

mvn clean package

W wyniku otrzymujemy plik restapp.war, skopiujmy do go katalogu webapps w Tomcacie lub innym kontenerze servletów.

Po uruchomieniu Tomcata na standardowym porcie i podaniu w przeglądarce następującego adresu:

http://localhost:8080/restapp/services/elements/

Otrzymacie taki oto wynik:

<?xml version=“1.0″ encoding=“UTF-8″ standalone=“yes”?>
<elements>
    <element>
        <id>1</id>
        <name>First</name>
    </element>
    <element>
        <id>2</id>
        <name>Second</name>
    </element>
</elements>

Gdy wejdziecie na ten adres:

http://localhost:8080/restapp/services/elements/1

Zwrócona zostanie odpowiedź zawierająca tylko jeden “element”, którego id jest równe 1:

<?xml version=“1.0″ encoding=“UTF-8″ standalone=“yes”?>
<element>
    <id>1</id>
    <name>First</name>
</element>

To chyba wszystko na dzisiaj, jako ćwiczenie polecić mogę zmianę tej usługi tak aby wywołanie listy zwracało jedynie podstawowe informacje o wszystkich elementach, a wśrod nich linki do interfejsu REST’owego zwracającego szczegóły poszczególnych elementów.

January 9, 2009 | 6 Comments  Tags: , , ,