WebService in Java con CXF e JAX-WS

JAX-WS+CXF+Spring: un mix di tecnologie per realizzare webservice in una maniera semplice e veloce.

Introduzione

In Java non esiste un solo modo per realizzare dei WebService, anche se esiste uno standard JAX-WS a cui bisognerebbe far riferimento. In questo articolo vedremo come poter realizzare dei WebService utilizzando CXF, framework opensource che si attiene alla specifica JAX-WS e come integrare il tutto con Spring

pom.xml del progetto

Per definire il nostro test, molto ricco di vari jar che dobbiamo portarci dietro come dipendenza, mi sembra giusto definire un progetto con Maven. Di seguito potete vedere il pom.xml del progetto, dove vengono elencate le dipendenze di cui abbiamo bisogno

<?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/maven-v4_0_0.xsd">     

    <modelversion>4.0.0</modelversion>     
    <groupid>com.javastaff</groupid>     
    <artifactid>cfxtest</artifactid>     
    <packaging>war</packaging>     
    <version>1.0</version>     
    <name>cfxtest</name>      
    <properties>         
        <cxf.version>2.2.1</cxf.version>         
        <spring.version>2.5.6</spring.version>         
        <netbeans.hint.deploy.server>
             Tomcat60
        </netbeans.hint.deploy.server>            
    </properties>          
    <build>         
        <plugins>             
            <plugin>                 
                <artifactid>maven-compiler-plugin</artifactid>                 
                <version>2.0.2</version>                 
                <configuration>                     
                    <source>1.6                     
                    <target>1.6</target>                 
                </configuration>             
            </plugin>         
        </plugins>     
    </build>      
    <dependencies>         
        <dependency>             
            <groupid>org.apache.cxf</groupid>             
            <artifactid>cxf-rt-frontend-jaxws</artifactid>             
            <version>${cxf.version}</version>         
        </dependency>         
        <dependency>             
            <groupid>org.apache.cxf</groupid>             
            <artifactid>cxf-rt-transports-http</artifactid>             
            <version>${cxf.version}</version>         
        </dependency>         
        <dependency>             
            <groupid>org.springframework</groupid>             
            <artifactid>spring</artifactid>             
            <version>${spring.version}</version>         
        </dependency>         
        <dependency>             
            <groupid>org.apache.cxf</groupid>             
            <artifactid>cxf-rt-databinding-aegis</artifactid>             
            <version>${cxf.version}</version>         
        </dependency>         
        <dependency>             
            <groupid>junit</groupid>             
            <artifactid>junit</artifactid>             
            <version>3.8.1</version>             
            <scope>test</scope>         
        </dependency>     
    </dependencies> 
</project>

Nel pom.xml abbiamo definito la dipendenza verso 3 diversi jar di CXF (che a loro volta avranno altre dipendenze necessarie) e verso Spring. Abbiamo inoltre aggiunto la dipendenza a JUnit, con scope test, visto che lo utilizzeremo alla fine per integrare un semplice test di quello che andremo a creare.

Il bean Book

Non avendo molta fantasia, utilizziamo il classico esempio dei libri da gestire. Definiamo quindi un bean che rappresenta la nostra informazione, quella che dovrà viaggiare tramite il WebService.

package com.javastaff.cfxtest.model;

import org.apache.cxf.aegis.type.java5.IgnoreProperty;

public class Book {

    private String titolo;
    private double prezzo;
    private String autore;
    private String trama;

    public String getTitolo() {
        return titolo;
    }

    public void setTitolo(String titolo) {
        this.titolo = titolo;
    }

    public double getPrezzo() {
        return prezzo;
    }

    public void setPrezzo(double prezzo) {
        this.prezzo = prezzo;
    }

    public String getAutore() {
        return autore;
    }

    public void setAutore(String autore) {
        this.autore = autore;
    }

    @IgnoreProperty
    public String getTrama() {
        return trama;
    }

    public void setTrama(String trama) {
        this.trama = trama;
    }

    @Override
    public String toString() {
        StringBuffer buffer = new StringBuffer();
        buffer.append("Book: {\n");
        buffer.append("titolo: ").append(titolo).append("\n");
        buffer.append("autore: ").append(autore).append("\n");
        buffer.append("prezzo: ").append(prezzo).append("\n");
        buffer.append("trama: ").append(trama).append("\n");
        buffer.append("}\n");
        return buffer.toString();
    }
}

Il bean è abbastanza banale, ma è presente un Annotation @IgnoreProperty che viene definita da Aegis. Andando per gradi dobbiamo prima dire che Aegis è una libreria integrata dentro CXF che permette di fare data-binding in maniera molto semplice. Dovendo noi realizzare un WebService è normale dover gestire un mapping Java-XML-Java, ma non dobbiamo preoccuparcene direttamente noi (almeno in casi banali come questo). L’Annotation che abbiamo utilizzato è per specificare che non abbiamo interesse a trattare quell’attributo nel nostro mapping, quindi quando avremo come ritorno un bean di questo tipo non avremo mai valorizzato il campo “trama”.

L’interfaccia e l’implementazione del servizio

Come prima cosa dobbiamo realizzare l’interfaccia del nostro servizio, utilizzando le Annotation che ci mette a disposizione JAX-WS

package com.javastaff.cfxtest.service;

import com.javastaff.cfxtest.model.Book;
import java.util.List;
import javax.jws.WebParam;
import javax.jws.WebService;

@WebService
public interface BookService {

    List<Book> getBooks();

    void addBook(@WebParam(name = "book") Book book);

    public void addBooks(@WebParam(name = "books") List<Book> books);
}

L’interfaccia è abbastanza banale, vengono definiti 3 metodi e l’unica cosa relativa a JAX-WS sono le Annotation che vengono utilizzate: @WebService per dire che ci troviamo di fronte ad un WebService e @WebParam per specificare il nome del parametro da tradurre all’interno dell’XML di richiesta. L’implementazione di questa interfaccia deve soltanto specificare il nome dell’endpoint che andremo ad utilizzare oltre ad includere la vera e propria implementazione dei metodi.

package com.javastaff.cfxtest.service;

import com.javastaff.cfxtest.model.Book;
import java.util.ArrayList;
import java.util.List;
import javax.jws.WebParam;
import javax.jws.WebService;

@WebService(endpointInterface = "com.javastaff.cfxtest.service.BookService")
public final class BookServiceImpl implements BookService {

    @Override
    public List<Book> getBooks() {
        List<Book> books = new ArrayList<Book>();
        Book book1 = new Book();
        book1.setAutore("Dante Alighieri");
        book1.setPrezzo(20);
        book1.setTitolo("La Divina Commedia");
        book1.setTrama("...");
        books.add(book1);
        Book book2 = new Book();
        book2.setAutore("Dan Brown");
        book2.setPrezzo(10);
        book2.setTitolo("Il codice Da Vinci");
        book2.setTrama("...");
        books.add(book2);
        return books;
    }

    @Override
    public void addBook(Book book) {
        System.out.println(book);
    }

    @Override
    public void addBooks(@WebParam(name = "books") List<Book> books) {
        for (Book book : books) {
            System.out.println(book);
        }
    }
}

Descrittori del nostro servizio

Dovendo includere il WebService all’interno di una web-application dobbiamo specificare due diversi descrittori:

    • web.xml: Serve per definire il file context di Spring e la Servlet di CXF che espone il WebService
    • applicationContext.xml: Definizione del WebService tramite CXF e del databinding Aegis

Ecco quindi il web.xml

<?xml version="1.0" encoding="UTF-8"?>     
<web-app id="services" version="2.5" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
    xsi:schemalocation="http://java.sun.com/xml/ns/javaee
                        http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> 
    <listener>         
        <listener-class>             
            org.springframework.web.context.ContextLoaderListener
        </listener-class>     
    </listener>      
    <context-param>         
        <param-name>contextConfigLocation</param-name>         
        <param-value>/WEB-INF/applicationContext.xml</param-value>     
    </context-param>      

    <!-- CXF Servlet -->     
    <servlet>         
        <servlet-name>CXFServlet</servlet-name>         
        <servlet-class> 
            org.apache.cxf.transport.servlet.CXFServlet
        </servlet-class>     
    </servlet>      

    <servlet-mapping>         
        <servlet-name>CXFServlet</servlet-name>         
        <url-pattern>/*</url-pattern>     
    </servlet-mapping> 
</web-app>

e l’applicationContext

<?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:context="http://www.springframework.org/schema/context" 
       xmlns:cxf="http://cxf.apache.org/core" 
       xmlns:jaxws="http://cxf.apache.org/jaxws" 
       xsi:schemalocation="http://www.springframework.org/schema/beans             
       			http://www.springframework.org/schema/beans/spring-beans-2.5.xsd             
            http://www.springframework.org/schema/context             
            http://www.springframework.org/schema/context/spring-context-2.5.xsd
            http://cxf.apache.org/core             
            http://cxf.apache.org/schemas/core.xsd
            http://cxf.apache.org/jaxws
            http://cxf.apache.org/schemas/jaxws.xsd" default-autowire="byName">  

    <!-- Caricamento moduli CXF da cxf.jar -->     
    <import resource="classpath:META-INF/cxf/cxf.xml">     
    <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml">     
    <import resource="classpath:META-INF/cxf/cxf-servlet.xml">      

    <!-- Service bean -->     
    <bean id="bookServiceImpl" 
          class="com.javastaff.cfxtest.service.BookServiceImpl"/>

    <!-- Aegis data binding -->     
    <bean id="aegisBean" 
          class="org.apache.cxf.aegis.databinding.AegisDatabinding" 
          scope="prototype"/>     

    <bean id="aegis-service" 
    		class="org.apache.cxf.jaxws.support.JaxWsServiceFactoryBean" 
    		scope="prototype">         
        <property name="dataBinding" ref="aegisBean"/>         
        <property name="serviceConfigurations">         
            <list>             
                <bean class="org.apache.cxf.jaxws.support.JaxWsServiceConfiguration"/>             
                <bean class="org.apache.cxf.aegis.databinding.AegisServiceConfiguration"/>             
                <bean class="org.apache.cxf.service.factory.DefaultServiceConfiguration"/>         
            </list>         
        </property>     
    </bean>      

    <!-- Service endpoint -->     
    <jaxws:endpoint id="bookService" 
    		implementorclass="com.javastaff.cfxtest.service.BookServiceImpl" 
    		implementor="#bookServiceImpl" address="/bookservice">          
        <jaxws:servicefactory>             
            <ref bean="aegis-service"/>         
        </jaxws:servicefactory>      
    </jaxws:endpoint>      

    <!-- CXF logging -->     
    <cxf:bus>         
        <cxf:features>             
            <cxf:logging/>         
        </cxf:features>     
    </cxf:bus>  
</beans>

Il test del WebService

Una volta che abbiamo impacchettato la nostra applicazione in un war, possiamo effetturare il deploy e richiamando l’url di default troveremo la seguente interfaccia che ci descrive il WebService che abbiamo definito

Test

 

Per eseguire il test del nostro WebService basta inserire nel nostro progetto Maven un semplice caso di test, che effettua la lookup del servizio e si fa restituire la lista dei libri

package com.javastaff.cfxtest;

import com.javastaff.cfxtest.model.Book;
import com.javastaff.cfxtest.service.BookService;
import java.util.List;
import junit.framework.TestCase;
import org.apache.cxf.aegis.databinding.AegisDatabinding;
import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.interceptor.LoggingOutInterceptor;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;

public class BookServiceTest extends TestCase {

    public BookServiceTest() {
    }

    public void testBookService() throws Exception {
        JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
        factory.getInInterceptors().add(new LoggingInInterceptor());
        factory.getOutInterceptors().add(new LoggingOutInterceptor());
        factory.setServiceClass(BookService.class);
        factory.setAddress("http://localhost:8084/cfxtest/bookservice");
        factory.getServiceFactory().setDataBinding(new AegisDatabinding());
        BookService client = (BookService) factory.create();
        List<Book> books = client.getBooks();
        if (books != null && books.size() > 0) 
        {
            System.out.println("Lista libri disponibili: " + books.size());
            for (Book book : books) {
                System.out.print(book);
            }
        }
    }
}

Eseguendo questo test, avremo come risultato il seguente output

Lista libri disponibili: 2 

Book: { 
    titolo: La Divina Commedia 
    autore: Dante Alighieri 
    prezzo: 20.0 
    trama: null 
} 

Book: { 
    titolo: Il codice Da Vinci 
    autore: Dan Brown 
    prezzo: 10.0 
    trama: null 
}

Come potete vedere ci viene restituita la lista dei libri definiti nel nostro WebService, escludendo l’attributo ‘trama’ come da noi indicato nel binding tramite Aegis

Riferimenti

Apache CXF
Introduzione a Aegis
JSR 224: Java API for XML-Based Web Services (JAX-WS) 2.0
JAX-WS reference implementation
Introducing JAX-WS 2.0 With the Java SE 6 Platform, Part 1 Introducing JAX-WS 2.0 With the Java SE 6 Platform, Part 2

Federico Paparoni

Looking for a right "about me"...

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.