Guide sur Apache CXF avec Spring

Un guide sur Apache CXF avec Spring

1. Vue d'ensemble

Ce didacticiel se concentre sur la configuration etusing the Apache CXF framework together with Spring - soit avec une configuration Java ou XML.

C’est le deuxième d’une série sur Apache CXF; the first one s'est concentré sur les principes fondamentaux de CXF en tant qu'implémentation des API standard JAX-WS.

2. Dépendances Maven

Semblable au tutoriel précédent, les deux dépendances suivantes doivent être incluses:


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


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

Pour les dernières versions des artefacts Apache CXF, veuillez consulterapache-cxf.

De plus, les dépendances suivantes sont nécessaires pour prendre en charge Spring:


    org.springframework
    spring-context
    4.3.1.RELEASE


    org.springframework
    spring-webmvc
    4.3.1.RELEASE

Les dernières versions des artefacts Spring peuvent être trouvéeshere.

Enfin, comme nous allons configurer l'application par programmation à l'aide de l'API Java Servlet 3.0+ au lieu d'un descripteur de déploiement traditionnel deweb.xml, nous aurons besoin de l'artefact ci-dessous:


    javax.servlet
    javax.servlet-api
    3.1.0

This est l'endroit où nous pouvons trouver la dernière version de l'API Servlet.

3. Composants côté serveur

Examinons à présent les composants qui doivent être présents côté serveur pour publier le point de terminaison du service Web.

3.1. InterfaceWebApplicationInitilizer

L'interfaceWebApplicationInitializer est implémentée pour configurer par programme l'interfaceServletContext pour l'application. Lorsqu'elle est présente sur le chemin de classe, sa méthodeonStartup est automatiquement invoquée par le conteneur de servlet et par la suite leServletContext est instancié et initialisé.

Voici comment une classe est définie pour implémenter l'interfaceWebApplicationInitializer:

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

La méthodeonStartup() est implémentée à l'aide des extraits de code ci-dessous.

Tout d'abord, un contexte d'application Spring est créé et configuré pour enregistrer une classe contenant des métadonnées de configuration:

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

La classeServiceConfiguration est annotée avec l'annotation@Configuration pour fournir des définitions de bean. Cette classe est discutée dans la sous-section suivante.

L'extrait suivant montre comment le contexte de l'application Spring est ajouté au contexte de servlet:

container.addListener(new ContextLoaderListener(context));

La classeCXFServlet, qui est définie par Apache CXF, est générée et enregistrée pour gérer les requêtes entrantes:

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

Le contexte d'application charge les éléments Spring définis dans un fichier de configuration. Dans ce cas, le nom du servlet estcxf, donc le contexte recherche ces éléments dans un fichier nommécxf-servlet.xml par défaut.

Enfin, le servlet CXF est mappé sur une URL relative:

dispatcher.addMapping("/services");

3.2. Le bon vieuxweb.xml

Alternativement, si nous voulons utiliser un descripteur de déploiement (quelque peu démodé) plutôt que l'interfaceWebApplicationInitilizer, le fichierweb.xml correspondant doit contenir les définitions de servlet suivantes:


    cxf
    org.apache.cxf.transport.servlet.CXFServlet
    1

    
    cxf
    /services/*

3.3. ClasseServiceConfiguration

Examinons maintenant la configuration du service - d'abord un squelette de base qui contient les définitions de bean pour le point de terminaison du service Web:

@Configuration
public class ServiceConfiguration {
    // Bean definitions
}

Le premier bean requis est leSpringBus - qui fournit des extensions pour qu'Apache CXF fonctionne avec Spring Framework:

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

Un beanEnpointImpl doit également être créé en utilisant le beanSpringBus et un service Webimplementor. Ce bean est utilisé pour publier le noeud final à l'adresse HTTP donnée:

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

La classeexampleImpl est utilisée pour implémenter l'interface du service Web. Sa définition est donnée dans la sous-section suivante.

Alternativement, nous pouvons également déclarer le noeud final du serveur dans un fichier de configuration XML. Plus précisément, le fichiercxf-servlet.xml ci-dessous fonctionne avec le descripteur de déploiementweb.xml tel que défini dans la sous-section 3.1 et décrit exactement le même point de terminaison:

Notez que le fichier de configuration XML est nommé d'après le nom du servlet défini dans le descripteur de déploiement, qui estcxf.

3.4. Définitions de type

Ensuite - voici la définition desimplementor qui a déjà été mentionnée dans la sous-section précédente:

@WebService(endpointInterface = "com.example.cxf.spring.example")
public class exampleImpl implements example {
    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;
    }
}

Cette classe fournit une implémentation pour l'interface de point de terminaisonexample qu'Apache CXF inclura dans les métadonnées WSDL publiées:

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

L'interface de point de terminaison ainsi que lesimplementor utilisent la classeStudent, qui est définie comme suit:

public class Student {
    private String name;

    // constructors, getters and setters
}

4. Beans côté client

Pour profiter du Spring Framework, nous déclarons un bean dans une classe annotée@Configuration:

@Configuration
public class ClientConfiguration {
    // Bean definitions
}

Un bean avec le nom declient est défini:

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

Le beanclient représente un proxy pour le service Webexample. Il est créé par un appel à la méthodecreate sur un beanJaxWsProxyFactoryBean, une fabrique pour la création de proxies JAX-WS.

L'objetJaxWsProxyFactoryBean est créé et configuré par la méthode suivante:

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

La propriétéserviceClass de la fabrique désigne l'interface du service Web, tandis que la propriétéaddress indique l'adresse URL du proxy pour effectuer des appels à distance.

De même, pour les beans Spring côté client, il est possible de revenir à un fichier de configuration XML. Les éléments suivants déclarent les mêmes beans que ceux que nous venons de configurer par programme ci-dessus:



    
    

5. Cas de test

Cette section décrit les scénarios de test utilisés pour illustrer la prise en charge d'Apache CXF pour Spring. Les cas de test sont définis dans une classe nomméeStudentTest.

Tout d'abord, nous devons charger un contexte d'application Spring à partir de la classe de configurationServiceConfiguration susmentionnée et le mettre en cache dans le champcontext:

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

Ensuite, un proxy pour l'interface de noeud final de service est déclaré et chargé à partir du contexte d'application:

private example exampleProxy = (example) context.getBean("client");

Ce proxyexample sera utilisé dans les cas de test décrits ci-dessous.

Dans le premier cas de test, nous prouvons que lorsque la méthodehello est appelée localement sur le proxy, la réponse est exactement la même que ce que le point de terminaisonimplementor renvoie du service Web distant:

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

Dans le deuxième cas de test, les étudiants s'inscrivent par exemple à des cours en appelant localement la méthoderegister sur le proxy, qui à son tour appelle le service Web. Ce service à distance calculera ensuite les numéros d'étudiant et les renverra à l'appelant. L'extrait de code suivant confirme nos attentes:

@Test
public void whenUsingRegisterMethod_thenCorrect() {
    Student student1 = new Student("Adam");
    Student student2 = new Student("Eve");
    String student1Response = exampleProxy.register(student1);
    String student2Response = exampleProxy.register(student2);

    assertEquals("Adam is registered student number 1", student1Response);
    assertEquals("Eve is registered student number 2", student2Response);
}

6. Test d'intégration

Pour être déployés en tant qu'application Web sur un serveur, les extraits de code de ce didacticiel doivent tout d'abord être empaquetés dans un fichier WAR. Ceci peut être réalisé en déclarant la propriétépackaging dans le fichier POM:

war

Le travail de packaging est implémenté par le plugin Maven WAR:


    maven-war-plugin
    2.6
    
        false
    

Ce plugin empaquette le code source compilé dans un fichier WAR. Puisque nous configurons le contexte de servlet à l'aide du code Java, le descripteur de déploiement traditionnel deweb.xmln'a pas besoin d'être existant. En conséquence, la propriétéfailOnMissingWebXml doit être définie surfalse pour éviter l'échec lors de l'exécution du plugin.

Nous pouvons suivrethis link pour la version la plus récente du plugin Maven WAR.

Afin d'illustrer les opérations du service Web, nous créons un test d'intégration. Ce test génère d'abord un fichier WAR et démarre un serveur intégré, puis fait en sorte que les clients invoquent le service Web, vérifie les réponses ultérieures et arrête enfin le serveur.

Les plugins suivants doivent être inclus dans le fichier Maven POM. Pour plus de détails, veuillez consulterthis Integration Testing tutorial.

Voici le plugin Maven Surefire:


    maven-surefire-plugin
    2.19.1
    
        
            StudentTest.java
        
    

La dernière version de ce plugin peut être trouvéehere.

Une sectionprofile avec lesid deintegration est déclarée pour faciliter le test d'intégration:


   
      integration
      
         
            ...
         
      
   

Le plugin Maven Cargo est inclus dans le profilintegration:


    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
            
        
    

Notez que les propriétés de configurationcargo.hostname etcargo.servlet.port sont simplement incluses dans un souci de clarté. Ces propriétés de configuration peuvent être omises sans aucun impact sur l'application car leurs valeurs sont identiques à celles par défaut. Ce plugin démarre le serveur, attend les connexions et finalement arrête le serveur pour libérer les ressources système.

This link nous permet de découvrir la dernière version du plugin Maven Cargo.

Le plugin Maven Surefire est à nouveau déclaré, dans le profilintegration, pour remplacer sa configuration dans la section principalebuild et pour exécuter les cas de test décrits dans la section précédente:


    maven-surefire-plugin
    2.19.1
    
        
            integration-test
            
                test
            
            
                
                    none
                
            
        
    

Maintenant, tout le processus peut être exécuté par la commande:mvn -Pintegration clean install.

7. Conclusion

Ce didacticiel illustre la prise en charge d’Apache CXF pour Spring. En particulier, il a été montré comment un service Web peut être publié à l'aide d'un fichier de configuration Spring et comment un client peut interagir avec ce service via un proxy créé par une fabrique de proxy Apache CXF, déclarée dans un autre fichier de configuration.

L'implémentation de tous ces exemples et extraits de code se trouve dansthe linked GitHub project.