Una guida ad Apache CXF con Spring

1. Panoramica

Questo tutorial si concentra sulla configurazione e sull'utilizzo del framework Apache CXF insieme a Spring , con configurazione Java o XML.

È il secondo di una serie su Apache CXF; il primo si è concentrato sui fondamenti di CXF come implementazione delle API standard JAX-WS.

2. Dipendenze di Maven

Analogamente al tutorial precedente, è necessario includere le seguenti due dipendenze:

 org.apache.cxf cxf-rt-frontend-jaxws 3.1.6   org.apache.cxf cxf-rt-transports-http 3.1.6 

Per le ultime versioni degli artefatti di Apache CXF, controlla apache-cxf.

Inoltre, le seguenti dipendenze sono necessarie per supportare Spring:

 org.springframework spring-context 4.3.1.RELEASE   org.springframework spring-webmvc 4.3.1.RELEASE 

Le ultime versioni degli artefatti Spring possono essere trovate qui.

Infine, poiché configureremo a livello di programmazione l'applicazione utilizzando l'API Java Servlet 3.0+ invece del tradizionale descrittore di distribuzione web.xml , avremo bisogno del seguente artefatto:

 javax.servlet javax.servlet-api 3.1.0 

Qui è dove possiamo trovare l'ultima versione dell'API Servlet.

3. Componenti lato server

Diamo ora uno sguardo ai componenti che devono essere presenti sul lato server per poter pubblicare l'endpoint del servizio web.

3.1. Interfaccia WebApplicationInitilizer

L' interfaccia WebApplicationInitializer viene implementata per configurare a livello di codice l' interfaccia ServletContext per l'applicazione. Quando è presente sul classpath, il suo metodo onStartup viene richiamato automaticamente dal contenitore servlet e successivamente viene istanziato e inizializzato ServletContext .

Ecco come viene definita una classe per implementare l' interfaccia WebApplicationInitializer :

public class AppInitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext container) { // Method implementation } }

Il metodo onStartup () viene implementato utilizzando frammenti di codice mostrati di seguito.

Innanzitutto, viene creato e configurato un contesto dell'applicazione Spring per registrare una classe contenente i metadati di configurazione:

AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); context.register(ServiceConfiguration.class);

La classe ServiceConfiguration viene annotata con l' annotazione @Configuration per fornire le definizioni dei bean. Questa classe è discussa nella prossima sottosezione.

Il frammento di codice seguente mostra come il contesto dell'applicazione Spring viene aggiunto al contesto servlet:

container.addListener(new ContextLoaderListener(context));

La classe CXFServlet , definita da Apache CXF, viene generata e registrata per gestire le richieste in arrivo:

ServletRegistration.Dynamic dispatcher = container.addServlet("dispatcher", new CXFServlet());

Il contesto dell'applicazione carica gli elementi Spring definiti in un file di configurazione. In questo caso, il nome del servlet è cxf , quindi il contesto cerca questi elementi in un file denominato cxf-servlet.xml per impostazione predefinita.

Infine, il servlet CXF è mappato a un URL relativo:

dispatcher.addMapping("/services");

3.2. Il buon vecchio web.xml

In alternativa, se vogliamo utilizzare un descrittore di distribuzione (un po 'antiquato) piuttosto che l' interfaccia WebApplicationInitilizer , il file web.xml corrispondente dovrebbe contenere le seguenti definizioni servlet:

 cxf org.apache.cxf.transport.servlet.CXFServlet 1   cxf /services/* 

3.3. Classe ServiceConfiguration

Diamo ora uno sguardo alla configurazione del servizio - prima uno scheletro di base che racchiude le definizioni dei bean per l'endpoint del servizio web:

@Configuration public class ServiceConfiguration { // Bean definitions }

Il primo bean richiesto è SpringBus , che fornisce estensioni per Apache CXF per funzionare con Spring Framework:

@Bean public SpringBus springBus() { return new SpringBus(); }

Un EnpointImpl fagiolo ha anche bisogno di essere creato utilizzando lo SpringBus di fagioli e un servizio web implementatori . Questo bean viene utilizzato per pubblicare l'endpoint all'indirizzo HTTP specificato:

@Bean public Endpoint endpoint() { EndpointImpl endpoint = new EndpointImpl(springBus(), new BaeldungImpl()); endpoint.publish("//localhost:8080/services/baeldung"); return endpoint; }

La classe BaeldungImpl viene utilizzata per implementare l'interfaccia del servizio web. La sua definizione è data nella prossima sottosezione.

In alternativa, possiamo anche dichiarare l'endpoint del server in un file di configurazione XML. In particolare, il file cxf-servlet.xml di seguito funziona con il descrittore di distribuzione web.xml come definito nella sottosezione 3.1 e descrive esattamente lo stesso endpoint:

Notare che il file di configurazione XML prende il nome dal nome del servlet definito nel descrittore di distribuzione, che è cxf .

3.4. Definizioni di tipo

Avanti: ecco la definizione dell'implementatore che è già stata menzionata nella sottosezione precedente:

@WebService(endpointInterface = "com.baeldung.cxf.spring.Baeldung") public class BaeldungImpl implements Baeldung { private int counter; public String hello(String name) { return "Hello " + name + "!"; } public String register(Student student) { counter++; return student.getName() + " is registered student number " + counter; } }

Questa classe fornisce un'implementazione per l' interfaccia dell'endpoint Baeldung che Apache CXF includerà nei metadati WSDL pubblicati:

@WebService public interface Baeldung { String hello(String name); String register(Student student); }

Sia l'interfaccia endpoint che l' implementatore utilizzano la classe Student , definita come segue:

public class Student { private String name; // constructors, getters and setters }

4. Fagioli lato client

Per sfruttare il framework Spring, dichiariamo un bean in una classe annotata @Configuration :

@Configuration public class ClientConfiguration { // Bean definitions }

Viene definito un bean con il nome di client :

@Bean(name = "client") public Object generateProxy() { return proxyFactoryBean().create(); }

The client bean represents a proxy for the Baeldung web service. It is created by an invocation to the create method on a JaxWsProxyFactoryBean bean, a factory for the creation of JAX-WS proxies.

The JaxWsProxyFactoryBean object is created and configured by the following method:

@Bean public JaxWsProxyFactoryBean proxyFactoryBean() { JaxWsProxyFactoryBean proxyFactory = new JaxWsProxyFactoryBean(); proxyFactory.setServiceClass(Baeldung.class); proxyFactory.setAddress("//localhost:8080/services/baeldung"); return proxyFactory; }

The factory's serviceClass property denotes the web service interface, while the address property indicates the URL address for the proxy to make remote invocations.

Also for the Spring beans on the client side one may revert to an XML configuration file. The following elements declare the same beans as the ones we just have programmatically configured above:

5. Test Cases

This section describes test cases used to illustrate Apache CXF support for Spring. The test cases are defined in a class named StudentTest.

First, we need to load a Spring application context from the aforementioned ServiceConfiguration configuration class and cache it in the context field:

private ApplicationContext context = new AnnotationConfigApplicationContext(ClientConfiguration.class);

Next, a proxy for the service endpoint interface is declared and loaded from the application context:

private Baeldung baeldungProxy = (Baeldung) context.getBean("client");

This Baeldung proxy will be used in test cases described below.

In the first test case, we prove that when the hello method is locally invoked on the proxy, the response is exactly the same as what the endpoint implementor returns from the remote web service:

@Test public void whenUsingHelloMethod_thenCorrect() { String response = baeldungProxy.hello("John Doe"); assertEquals("Hello John Doe!", response); }

In the second test case, students register for Baeldung courses by locally invoking the register method on the proxy, which in turn calls the web service. That remote service will then calculate the student numbers and return them to the caller. The following code snippet confirms what we expect:

@Test public void whenUsingRegisterMethod_thenCorrect() { Student student1 = new Student("Adam"); Student student2 = new Student("Eve"); String student1Response = baeldungProxy.register(student1); String student2Response = baeldungProxy.register(student2); assertEquals("Adam is registered student number 1", student1Response); assertEquals("Eve is registered student number 2", student2Response); }

6. Integration Testing

In order to be deployed as a web application on a server, code snippets in this tutorial need to be packaged into a WAR file first. This can be achieved by declaring the packaging property in the POM file:

war

The packaging job is implemented by the Maven WAR plugin:

 maven-war-plugin 2.6  false  

This plugin packages the compiled source code into a WAR file. Since we configure the servlet context using Java code, the traditional web.xml deployment descriptor does not need to be existent. As the result, the failOnMissingWebXml property must be set to false to avoid failure when the plugin is executed.

We can follow this link for the most recent version of the Maven WAR plugin.

In order to illustrate operations of the web service, we create an integration test. This test first generates a WAR file and starts an embedded server, then makes clients invoke the web service, verifies subsequent responses and finally stops the server.

The following plugins need to be included in the Maven POM file. For more details, please check out this Integration Testing tutorial.

Here is the Maven Surefire plugin:

 maven-surefire-plugin 2.19.1   StudentTest.java   

The latest version of this plugin can be found here.

A profile section with the id of integration is declared to facilitate the integration test:

  integration   ...    

The Maven Cargo plugin is included in the integration profile:

 org.codehaus.cargo cargo-maven2-plugin 1.5.0   jetty9x embedded    localhost 8080      start-server pre-integration-test  start    stop-server post-integration-test  stop    

Note that the cargo.hostname and cargo.servlet.port configuration properties are merely included for the sake of clarity. These configuration properties may be left out without any impact on the application since their values are the same as the default values. This plugin starts the server, waits for connections and finally stops the server to release system resources.

This link allows us to check out the latest version of the Maven Cargo plugin.

Il plug-in Maven Surefire viene nuovamente dichiarato, all'interno del profilo di integrazione , per sovrascrivere la sua configurazione nella sezione build principale ed eseguire casi di test descritti nella sezione precedente:

 maven-surefire-plugin 2.19.1   integration-test  test    none     

Ora l'intero processo può essere eseguito dal comando: mvn -Pintegration clean install .

7. Conclusione

Questo tutorial ha illustrato il supporto di Apache CXF per Spring. In particolare, è stato mostrato come un servizio web possa essere pubblicato utilizzando un file di configurazione Spring e come un client possa interagire con quel servizio attraverso un proxy creato da una fabbrica proxy Apache CXF, che è stato dichiarato in un altro file di configurazione.

L'implementazione di tutti questi esempi e frammenti di codice può essere trovata nel progetto GitHub collegato.