Aller au contenu principal
~/GiwiSoft
Camel 726344

Camel et Web Services CXF

Giwi 3 min de lecture Java, Camel

Aujourd’hui nous verrons comment publier un web service depuis Camel. Il y a plusieurs façons de le faire. En fait c’est très simple avec Spring.  Cependant, ça l’est moins quand il faut traiter une requête entrante et déclencher le bon routage en fonction de la méthode invoquée.

Il faut donc tester une en-tête du message qui contient la méthode invoquée. Pour se faire, il y a deux méthodes. Nous allons faire tourner le tout sur un Tomcat (cf article précédent). Prenons donc cette belle interface représentant notre non moins magnifique service :

package org.giwi.camel.ws;

import javax.jws.WebParam;
import javax.jws.WebResult;

@javax.jws.WebService(name = "sayHelloService", targetNamespace = "http://giwi.free.fr")
public interface SayHelloService {
@WebResult(name = "status")
public String sayHello(@WebParam(name = "name") String name);

@WebResult(name = "status")
public String sayGoodby(@WebParam(name = "name") String name);
}

Et ce splendide ApplicationContext.xml :

<?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://camel.apache.org/schema/cxf"
xmlns:http="http://cxf.apache.org/transports/http/configuration" xmlns:sec="http://cxf.apache.org/configuration/security"
xsi:schemaLocation="
http://cxf.apache.org/configuration/security http://cxf.apache.org/schemas/configuration/security.xsd
http://cxf.apache.org/transports/http/configuration http://cxf.apache.org/schemas/configuration/http-conf.xsd
http://camel.apache.org/schema/cxf http://camel.apache.org/schema/cxf/camel-cxf.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">

<!-- needed cxf imports -->
<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
<!-- use the CXF servlet -->
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
<!-- Déclaration des routes camel à éxécuter -->
<camelContext xmlns="http://camel.apache.org/schema/spring">
<package>org.giwi.camel.route</package>
</camelContext>
</beans>

La première consiste à utiliser l’EIP “Content Based Router“ :

package org.giwi.camel.route;

import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.cxf.CxfConstants;

public class SayHelloRoute extends RouteBuilder {

@Override
public void configure() throws Exception {
from("cxf:/helloService?serviceClass=org.giwi.camel.ws.SayHelloService")

.choice()

.when(header(CxfConstants.OPERATION\_NAME).isEqualTo("sayHello"))

.to("direct:sayHello1")

.when(header(CxfConstants.OPERATION\_NAME).isEqualTo("sayGoodby"))

.to("direct:sayGoodBy1")

.otherwise().throwException(new Exception("unknown method"));

from("direct:sayHello1").process(new Processor() {
@Override
public void process(Exchange ex) throws Exception {
ex.getOut().setBody("hello");
}
});

from("direct:sayGoodBy1").process(new Processor() {
@Override
public void process(Exchange ex) throws Exception {
ex.getOut().setBody("goodby");
}
});
}
}

Bref, c’est un peu lourd. La seconde méthode consiste à utiliser l’EIP “Recipient List“ :

package org.giwi.camel.route;

import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.builder.RouteBuilder;

public class SayBetterHello extends RouteBuilder {

@Override
public void configure() throws Exception {
from("cxf:/helloBetterService?serviceClass=org.giwi.camel.ws.SayHelloService")

.recipientList(simple("direct:${header.operationName}2"));

from("direct:sayHello2").process(new Processor() {
@Override
public void process(Exchange ex) throws Exception {
ex.getOut().setBody("hello");
}
});

from("direct:sayGoodby2").process(new Processor() {
@Override
public void process(Exchange ex) throws Exception {
ex.getOut().setBody("goodby");
}
});
}
}

Plus simple, non?

Camel CXF avec Spring Boot (approche moderne)

Quinze ans plus tard, les choses ont bien changé. Avec Spring Boot et les starters Camel, plus besoin de tout ce XML. Voici la version moderne avec camel-cxf-starter.

Dépendances Maven

<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-spring-boot-starter</artifactId>
<version>4.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-cxf-starter</artifactId>
<version>4.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>4.0.5</version>
</dependency>

Configuration dans application.yml

camel:
springboot:
name: CamelCxfService
cxf:
path: /services

camel:
component:
cxf:
address: http://localhost:8080/services/hello
service-class: org.giwi.camel.ws.SayHelloService

Route Spring Boot moderne

@Component
public class SayHelloRoute extends RouteBuilder {

@Override
public void configure() {
from("cxf:bean:helloEndpoint")
.choice()
.when(header(CxfConstants.OPERATION_NAME).isEqualTo("sayHello"))
.transform(constant("Hello depuis Spring Boot !"))
.when(header(CxfConstants.OPERATION_NAME).isEqualTo("sayGoodby"))
.transform(constant("Goodbye depuis Spring Boot !"))
.otherwise()
.throwException(new IllegalArgumentException("Méthode inconnue"));
}
}

Et la configuration du endpoint CXF

@Configuration
public class CxfConfig {

@Bean
public CxfEndpoint helloEndpoint() {
CxfEndpoint endpoint = new CxfEndpoint();
endpoint.setAddress("/helloBetterService");
endpoint.setServiceClass(SayHelloService.class);
return endpoint;
}
}

REST vs SOAP avec Camel

Aujourd’hui, on préfère souvent REST aux services SOAP. Camel le fait aussi en quelques lignes :

@Component
public class RestRoute extends RouteBuilder {
@Override
public void configure() {
restConfiguration()
.component("servlet")
.port(8080)
.bindingMode(RestBindingMode.json);

rest("/hello")
.get("/{name}")
.to("direct:sayHello")
.post()
.type(Personne.class)
.outType(String.class)
.to("direct:sayHelloPost");

from("direct:sayHello")
.transform(simple("Hello ${header.name} !"));

from("direct:sayHelloPost")
.log("Reçu: ${body}")
.transform(simple("Bonjour ${body.nom} !"));
}
}

La tendance actuelle est au REST JSON, mais SOAP reste très présent dans le monde de l’assurance, de la banque et des ERP. Camel gère les deux avec la même élégance.