Ce billet illustre l'emploi du composant Camel-SQL.
Dans notre démo le composant camel-sql s'appuie sur une datasource déclarée dans spring.
La démo est réalisée en cinq étapes:
I. CONFIGURER LE POM DU PROJET
II. CONFIGURER SPRING
III. DESSINER LA ROUTE CAMEL
IV. CONFIGURER L'ENVIRONNEMENT
V. TESTER
Passons à la pratique.
Voici les étapes de la démo qui utilise java 7, spring 3, camel 2.10:
ETAPE 1. CONFIGURER LE POM
Pour utiliser le composant camel-sql on a besoin uniquement de cette déclaration:
<dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-sql</artifactId> <version>2.10.2</version> </dependency>
Voici le pom.xml en entier:
<properties> <!-- Versions Dependances --> <commons-beanutils-version>1.8.3</commons-beanutils-version> <commons-codec-version>1.6</commons-codec-version> <commons-collections-version>3.2.1</commons-collections-version> <commons-lang-version>2.6</commons-lang-version> <commons-digester-version>2.1</commons-digester-version> <javassist-version>3.12.1.GA</javassist-version> <cglib-version>2.2.2</cglib-version> <!-- Log --> <log4j-version>1.2.17</log4j-version> <slf4j-version>1.6.6</slf4j-version> <!-- Spring --> <spring-version>3.1.2.RELEASE</spring-version> <!-- camel-version --> <camel-version>2.10.2</camel-version> <!-- Versions Dependances de test --> <junit-version>4.10</junit-version> <!-- PostgresSql --> <postgresql-version>9.1-901-1.jdbc4</postgresql-version> </properties> <dependencies> <dependency> <groupId>commons-dbcp</groupId> <artifactId>commons-dbcp</artifactId> <version>1.2.2</version> </dependency> <dependency> <groupId>commons-pool</groupId> <artifactId>commons-pool</artifactId> <version>1.2</version> </dependency> <!-- dep transitive de JavConfig see @Configuration --> <dependency> <groupId>javassist</groupId> <artifactId>javassist</artifactId> <version>${javassist-version}</version> <scope>compile</scope> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>${cglib-version}</version> <scope>compile</scope> </dependency> <!-- spring-core --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring-version}</version> <scope>compile</scope> </dependency> <!-- spring-expression --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>${spring-version}</version> <scope>compile</scope> </dependency> <!-- spring-asm --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-asm</artifactId> <version>${spring-version}</version> <scope>compile</scope> </dependency> <!-- spring-beans --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring-version}</version> <scope>compile</scope> </dependency> <!-- spring-context --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring-version}</version> <scope>compile</scope> </dependency> <!-- spring-context-support --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${spring-version}</version> <scope>compile</scope> </dependency> <!-- postgres --> <dependency> <groupId>postgresql</groupId> <artifactId>postgresql</artifactId> <version>${postgresql-version}</version> <scope>compile</scope> </dependency> <!-- CAMel --> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-core</artifactId> <version>${camel-version}</version> </dependency> <!-- camel-sql --> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-sql</artifactId> <version>${camel-version}</version> </dependency> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-spring</artifactId> <version>${camel-version}</version> </dependency> <!-- camel-spring-config --> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-spring-javaconfig</artifactId> <version>${camel-version}</version> </dependency> <!-- logging --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.6.6</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.6.6</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <!-- logging --> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.1</version> <scope>runtime</scope> </dependency> <!-- CAMEL -test-spring --> <dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-test-spring</artifactId> <version>${camel-version}</version> <scope>test</scope> </dependency> <!-- spring-test --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>3.1.2.RELEASE</version> <scope>test</scope> </dependency> <!-- DEPENDANCES DE TESTS junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit-version}</version> <scope>test</scope> </dependency> <!-- commons beanutils --> <dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> <version>${commons-beanutils-version}</version> </dependency> <!-- commons codec --> <dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>${commons-codec-version}</version> <scope>compile</scope> </dependency> <!-- --> <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>${commons-collections-version}</version> <scope>compile</scope> </dependency> <dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>${commons-lang-version}</version> <scope>compile</scope> </dependency> <!-- common DIGESTER --> <dependency> <groupId>commons-digester</groupId> <artifactId>commons-digester</artifactId> <version>${commons-digester-version}</version> </dependency> </dependencies> <build> <defaultGoal>install</defaultGoal> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.5.1</version> <configuration> <source>1.7</source> <target>1.7</target> </configuration> </plugin> <!-- allows the route to be ran via 'mvn camel:run' --> <plugin> <groupId>org.apache.camel</groupId> <artifactId>camel-maven-plugin</artifactId> <version>${camel-version}</version> </plugin> </plugins> </build> </project>
ETAPE 2. CONFIGURER SPRING
La configuration de spring est centrée dans le fichier xml suivant:
<context:property-placeholder location="db.properties"/> <context:component-scan base-package="fr.netapsys.camel"/> <camel:camelContext id="camel1" xmlns="http://camel.apache.org/schema/spring"> <camel:package>fr.netapsys.camel.routes</camel:package> </camel:camelContext> <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${bd.driverClassName}"/> <property name="url" value="${bd.url}"/> <property name="username" value="${bd.username}"/> <property name="password" value="${bd.password}"/> <property name="defaultAutoCommit" value="true" /> </bean>
Explications:
Un fichier (placeholder pour spring) contient les paramètres (propriétés) de définition de la datasource nommée myDataSource.
Les routes camel sont auto-injectées en les scannant dans le répertoire de <camel:package>.
Les autres composants à injecter par spring sont scannés depuis le <context:component-scan base-package.
Le fichier db.properties est donné à l'étape 4.
ETAPE 3. DESSINER LA ROURE CAMEL-SQL
La route camel permettant de rechercher ( et d'insérer) des données depuis (dans) la table CLIENTS de la base s'écrit :
public class BatchSqlRoute extends RouteBuilder { public void configure() { //handled(false) tell camel propagate the exception to caller. onException(SQLException.class).handled(false). log(LoggingLevel.ERROR,">>>Error in: ${body},\n ${exception}"); //using SqlEndpoint select from CLIENTS from("direct:simpleSelect") .to("sql:SELECT * FROM CLIENTS WHERE id=# ?dataSourceRef=myDataSource") .end(); //using SqlEndpoint to insert into CLIENTS from("direct:insertSql") .to("sql:INSERT INTO CLIENTS (id, nom,prenom) values (#,#,#)?dataSourceRef=myDataSource") .end(); } }
Noter que chacune des deux chaînes de .to doivent être sur la même ligne.
Vous pouvez constater que deux endpoints sont définis :
- le premier permet d'effectuer un simple select en fonction de l'id du client.
- le second effectue une insertion des trois champs de la table CLIENTS.
ETAPE 4. CONFIGURER L'ENVIRONNEMENT
A) Fichier db.properties
Ce fichier (placeholder de spring) contient les paramètres d'accès à la base postgres qu'il faudrait adapter.
#params db postgres bd.driverClassName=org.postgresql.Driver bd.url=jdbc:postgresql://localhost:5432/test bd.username=XXXXXX bd.password=XXXX
B) Script de création de la table CLIENTS
Voici le script pour créer la table clients
CREATE TABLE clients( id bigint PRIMARY KEY NOT NULL, nom varchar(255), prenom varchar(255) ); CREATE UNIQUE INDEX clients_prim_key ON clients(id);
ETAPE 4. TESTER AVEC JUNIT
package fr.netapsys.camel.tests; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.util.List; import java.util.Map; import org.apache.camel.Endpoint; import org.apache.camel.Exchange; import org.apache.camel.ProducerTemplate; import org.junit.Test; import org.springframework.dao.DuplicateKeyException; public class TestBatchSqlRoute extends TestParent { final static int ID_2_CREATE=110; @Test public void testInsertSql() throws Exception { ProducerTemplate template = camelCtx.createProducerTemplate(); Endpoint endpoint = camelCtx.getEndpoint("direct:insertSql"); Exchange exchange = endpoint.createExchange(); final long id = ID_2_CREATE+1001; final String nom = "toto"+id; final String prenom = "prenom"+id; exchange.getIn().setBody(new Object[]{id,nom,prenom}); Exchange exchangeOut = template.send(endpoint, exchange); assertNotNull(exchangeOut); assertNotNull(exchangeOut.getOut()); assertTrue(exchangeOut.getException() ==null || exchangeOut.getException() instanceof DuplicateKeyException); if(exchangeOut.getException() !=null) assertTrue( exchangeOut.getException() instanceof DuplicateKeyException); } }
Lancer la commande mvn test pour voir une ligne s'insérer dans la table CLIENTS.
Le test suivant vous permet de recherche un client par son id.
Vérifier que l'id passé existe en base.
@Test public void testSelectSql() throws Exception { final static int ID_4_SELECT=110; ProducerTemplate template = camelCtx.createProducerTemplate(); Endpoint endpoint = camelCtx.getEndpoint("direct:simpleSelect"); Exchange exchange = endpoint.createExchange(); exchange.getIn().setBody(ID_4_SELECT); Exchange out = template.send(endpoint, exchange); assertTrue(out != null); assertTrue(out.getException() == null); assertTrue(out.getOut() != null); int nb=out.getOut().getHeader ("CamelSqlRowCount",Integer.class); assertTrue(nb == 1); List<Map<String,Object>> l= (List<Map<String,Object>>) out.getOut().getBody(); assertTrue(l.size()>0); Map<String,Object> map=l.get(0); assertTrue(map.get("nom").contains("toto"); }
Pour conclure le composant camel-sql réduit la couche DAO (ici à base de jdbc) à presque quelques lignes de configuration.