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:
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:
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:
"-//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:
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:
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:
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:
<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:
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:
Otrzymacie taki oto wynik:
<elements>
<element>
<id>1</id>
<name>First</name>
</element>
<element>
<id>2</id>
<name>Second</name>
</element>
</elements>
Gdy wejdziecie na ten adres:
Zwrócona zostanie odpowiedź zawierająca tylko jeden “element”, którego id jest równe 1:
<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.




