Le projet CXF
CXF est un projet Apache qui est issu de la fusion entre XFire et Celtix. Il s’agit d’une implémentation JAX-WS qui s’intègre facilement avec Spring Framework. Il est donc plutôt orienté "Contract Last", mais il permet cependant de développer suivant l’approche "Contract First".
Il implémente des standards WS et Java :
-
SOAP, WS-I Basic Profile, WS-Security…
-
JAX-WS, JAX-WSA, JSR-181, SAAJ
Par ailleurs, il propose plusieurs types de transport (HTTP, JMS…), ainsi qu’un choix entre plusieurs frameworks de Data Binding (JAXB, Aegis…).
Dans cet article, nous traiterons uniquement de l’approche "Contract Last", i.e. le fichier WSDL sera généré à partir des classes Java annotées.
Principe de communication client / service
Processus de développement
Partie serveur
Pour écrire la partie serveur du service web, les étapes de développement sont les suivantes :
-
Fournir une interface + une classe d’implémentation qui seront annotées avec les annotations de l’API JAX-WS
-
Configurer Spring pour déclarer le bean de service web
-
Configurer la Servlet CXF dans le fichier web.xml
L’interface Java du service doit être annotée avec @WebService
:
@WebService
public interface CatalogueService {
Produit findProduitById(Long id);
...
}
Cette configuration peut être enrichie afin de contrôler plus précisément les éléments du fichier WSDL qui sera généré, notamment à l’aide des attributs de @WebService
et de l’annotation @WebMethod
:
@WebService(name="Catalogue",
targetNamespace="http://www.librairie.org")
public interface CatalogueService {
@WebMethod(operationName="getProduit")
Produit findProduitById(@WebParam(name="id") Long id);
...
}
La classe d’implémentation doit pour sa part être annotée avec @WebService
de façon à préciser l’interface qui définit le service web :
@WebService(endpointInterface="org.librairie.service.CatalogueService")
public class CatalogueServiceImpl implements CatalogueService {
private ProduitDao produitDao;
@Override
public Produit findProduitById(Long id) {
Produit produit = produitDao.findById(id);
return produit;
}
...
}
Au niveau de la configuration Spring, il faut :
-
importer les fichiers d’application context de CXF
-
déclarer le bean de service web avec le schéma jaxws fourni par CXF
-
déclarer les dépendances du bean de service web
Voici donc le fichier d’application context pour notre service web :
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.0.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
<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" />
<bean id="catalogueService"
class="org.librairie.service.CatalogueServiceImpl">
<property name="produitDao" ref="produitDao"/>
</bean>
<jaxws:endpoint id="catalogueServiceEndpoint"
implementor="#catalogueService"
address="/CatalogueService" />
</beans>
Dans l’exemple ci-dessus, <jaxws:endpoint>
permet de créer le bean de web service avec l’identifiant catalogueServiceEndpoint
. L’attribut implementor
est utilisé pour injecter un bean Spring dans le bean de web service. A noter que le nom du bean doit être préfixé avec le caractère #.
On configure enfin le fichier web.xml
afin de déclarer la servlet responsable du chargement de l’application context et du traitement des requêtes HTTP destinées aux endpoints :
<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>/*</url-pattern>
</servlet-mapping>
Remarque : Si le fichier Spring est nommé cxf.xml
et placé dans le classpath, alors la servlet CXF charge automatiquement l’application context. Sinon, il faut effectuer le chargement de l’application context via le context loader listener de Spring :
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext-cxf.xml</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
Partie client
Côté client, deux approches sont possibles :
-
Si l’interface Java du service est disponible, il suffit de créer un Factory Bean JAX-WS au niveau de la configuration Spring
-
Si on dispose uniquement du fichier WSDL, un outil permet de générer des stubs à partir du fichier WSDL
Quand on dispose de l’interface Java du service, la configuration Spring du bean client est effectué à l’aide d’un bean JaxWsProxyFactoryBean
fourni par CXF :
<bean id="catalogue"
class="org.librairie.service.CatalogueService"
factory-bean="clientFactory" factory-method="create"/>
<bean id="clientFactory"
class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean">
<property name="serviceClass"
value="org.librairie.service.CatalogueService"/>
<property name="address"
value="http://server/myapp/CatalogueService"/>
</bean>
Pour invoquer le service web depuis le code Java, il suffit alors d’instancier le conteneur Spring et de récupérer le bean client :
String[] paths = {"applicationContext-cxf-client.xml"};
ApplicationContext ctx = new ClassPathXmlApplicationContext(paths);
CatalogueService service = (CatalogueService)ctx.getBean("catalogue");
Produit produit = service.findProduitById(3);