<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet title="XSL formatting" type="text/xsl" href="http://blog.netapsys.fr/index.php/feed/rss2/xslt" ?><rss version="2.0"
  xmlns:dc="http://purl.org/dc/elements/1.1/"
  xmlns:wfw="http://wellformedweb.org/CommentAPI/"
  xmlns:content="http://purl.org/rss/1.0/modules/content/"
  xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
  <title>Netapsys Blog</title>
  <link>http://blog.netapsys.fr/index.php/</link>
  <atom:link href="http://blog.netapsys.fr/index.php/feed/author/abderrazek_chine/rss2" rel="self" type="application/rss+xml"/>
  <description></description>
  <language>fr</language>
  <pubDate>Wed, 19 Jun 2013 06:19:07 +0200</pubDate>
  <copyright>Netapsys 2008 - 2011</copyright>
  <docs>http://blogs.law.harvard.edu/tech/rss</docs>
  <generator>Dotclear</generator>
  
    
  <item>
    <title>Le cache de Guava- Partie 2</title>
    <link>http://blog.netapsys.fr/index.php/post/2013/05/28/Le-cache-de-Guava-Partie-2</link>
    <guid isPermaLink="false">urn:md5:f0a506f7a3f78baee844bef0f30e65a0</guid>
    <pubDate>Fri, 31 May 2013 18:05:00 +0200</pubDate>
    <dc:creator>Abderrazek CHINE</dc:creator>
        <category>Java J2EE</category>
        <category>@Configuration</category><category>cache</category><category>CacheLoader</category><category>Google</category><category>Google API</category><category>guava</category><category>Junit</category><category>LoadingCache</category><category>Spring</category><category>tests unitaires</category><category>tutoriel</category>    
    <description>&lt;p&gt;Nous poursuivons ici ce billet. La démo 2 est présentée avec détails.&lt;/p&gt;


&lt;p&gt;Dans cesdémos, nous donnons deux façons pratiques afin de mieux gérer le cache de Guava.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;La première s'appuie les interfaces &lt;em&gt;LoadingCache&lt;/em&gt; et &lt;em&gt;CacheLoader&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;La seconde fait appel à l'interface &lt;em&gt;Callable&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Nous partons du &lt;a href=&quot;http://blog.netapsys.fr/public/ach/guava/gestioncacheguava.zip&quot;&gt;projet&lt;/a&gt; de la première partie de ce billet.&lt;/p&gt;


&lt;p&gt;Nous allons le compléter et l'adapter au fur et à mesure pour construire les démos.&lt;/p&gt;


&lt;p&gt;Passons à la mise en pratique.&lt;/p&gt;    &lt;p&gt;&lt;ins&gt;&lt;strong&gt;DEMO2&lt;/strong&gt;&lt;/ins&gt;. Utilisation des interfaces &lt;em&gt;LoadingCache&lt;/em&gt; et &lt;em&gt;CacheLoader&lt;/em&gt;&lt;/p&gt;


&lt;p&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;strong&gt;1.A&lt;/strong&gt;) Modification de l'interface DestinationCacheManager&lt;/p&gt;



&lt;p&gt;Désormais la seule méthode utile est la méthode &lt;em&gt;get&lt;/em&gt;.&lt;/p&gt;

&lt;pre&gt;
public interface DestinationCacheManager {
	 Destination get(String key) throws ExecutionException ;
}
&lt;/pre&gt;


&lt;p&gt;Notez que la méthode &lt;em&gt;get&lt;/em&gt; lève une exception.&lt;/p&gt;


&lt;p&gt;Aussi que la méthode &lt;em&gt;put&lt;/em&gt; n'est plus utile.&lt;/p&gt;


&lt;p&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;strong&gt;1.B&lt;/strong&gt;) L'implémentation de cette interface est nommée DestinationCacheManagerImpl:&lt;/p&gt;

&lt;pre&gt;
package fr.netapsys.gestioncacheguava.services;
import java.util.concurrent.*;
import javax.annotation.PostConstruct;
import org.springframework.stereotype.Component;
import com.google.common.cache.*;
import fr.netapsys.gestioncacheguava.model.Destination;
@Component
public class DestinationCacheManagerImpl 
      implements DestinationCacheManager{
 private LoadingCache&amp;lt;String,Destination&amp;gt; cache;
 @PostConstruct
 public void init(){
	cache = CacheBuilder.newBuilder().
 	  maximumSize(10).
	  expireAfterWrite(1, TimeUnit.MINUTES).  
	  build( 
      	    new CacheLoader&amp;lt;String, Destination&amp;gt;() {
	    @Override
	     public Destination load(String key) throws Exception {
	      return getFavoriteDestination(key);//simule appel service 
	     }
	   }
	 );
	}
	public Destination get(String key) throws ExecutionException {
	  return cache.get(key);
	}
   //Simule l'appel an service qui genere la destination favorite 
    private Destination getFavoriteDestination(String key) {
	Destination destDispo=new Destination();
	destDispo.setLibelle(&amp;quot;destFavorite&amp;quot;);
	destDispo.setVille(&amp;quot;PARIS&amp;quot;);
	destDispo.setPays(&amp;quot;FRANCE&amp;quot;);
	destDispo.setCritere(&amp;quot;C&amp;quot;+key);
	return destDispo;
   }
}
&lt;/pre&gt;


&lt;p&gt;&lt;br /&gt;
&lt;strong&gt;&lt;ins&gt;&lt;em&gt;Explications&lt;/em&gt;&lt;/ins&gt;&lt;/strong&gt;:&lt;/p&gt;


&lt;p&gt;Quelques modifications majeures sont à observer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Le cache est de type &lt;em&gt;LoadingCache&lt;/em&gt;. Et on utilise la méthode V get(K,V) throws &lt;em&gt;ExecutionException&lt;/em&gt;,&lt;/li&gt;
&lt;li&gt;Nous passons à la méthode build du &lt;em&gt;CacheBuilder&lt;/em&gt; un objet anonyme de type &lt;em&gt;CacheLoader&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ceci permet donc d'appeler un service pour initialiser les éléments du cache.&lt;/p&gt;


&lt;p&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;strong&gt;1.C&lt;/strong&gt;) Test unitaire&lt;/p&gt;


&lt;p&gt;Voici le code du test unitaire&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
package fr.netapsys.testscache;
import java.util.concurrent.ExecutionException;
import org.junit.*;
import org.springframework.beans.factory.annotation.Autowired;
import fr.netapsys.gestioncacheguava.model.Destination;
import fr.netapsys.gestioncacheguava.services.DestinationCacheManager;
public class TestDestinationCache extends TestParent {
  @Autowired DestinationCacheManager dcm;
  @Test
  public void testCacheLoader() throws ExecutionException  {
	// get from cache
 	Destination d=dcm.get(&amp;quot;destFavorite&amp;quot;);
 	Assert.assertNotNull(d);
	Assert.assertEquals(d.getLibelle(),&amp;quot;destFavorite&amp;quot;  );	
  }

@Test
	public void test2() throws ExecutionException  {
		// get from cache
		Destination d1=dcm.get(&amp;quot;destSuper&amp;quot;);
		Assert.assertNotNull(d1);
		Assert.assertEquals(d1.getCritere(),&amp;quot;CdestSuper&amp;quot;  );
	}
}
&lt;/pre&gt;


&lt;p&gt;C'est simple non?
Le (&lt;em&gt;Laoding&lt;/em&gt;)&lt;em&gt;Cache&lt;/em&gt; est initialisé via la méthode init annotée avec @PostConstruct qui
appelle la méthode statique build en lui passant comme argument une instance de &lt;em&gt;CacheLoader&lt;/em&gt;.
Le test unitaire définit une instance DestinationCacheManager nommée dcm et injectée par Spring.
Les deux méthode du test montrent que cette instance permet, via la méthode get, de récupérer du cache la destination ayant la bonne clé.&lt;/p&gt;


&lt;p&gt;&lt;br /&gt;
&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;&lt;ins&gt;&lt;strong&gt;DEMO3&lt;/strong&gt;&lt;/ins&gt;. Utilisation de l'interface &lt;em&gt;Callable&lt;/em&gt;&lt;/p&gt;


&lt;p&gt;La prochaine partie détaille la démo3 basée sur les Callable.&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.netapsys.fr/index.php/post/2013/05/28/Le-cache-de-Guava-Partie-2#comment-form</comments>
      <wfw:comment>http://blog.netapsys.fr/index.php/post/2013/05/28/Le-cache-de-Guava-Partie-2#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.netapsys.fr/index.php/feed/atom/comments/607</wfw:commentRss>
      </item>
    
  <item>
    <title>Le cache de Guava</title>
    <link>http://blog.netapsys.fr/index.php/post/2013/05/27/Le-cache-de-Guava</link>
    <guid isPermaLink="false">urn:md5:b5ddbbfc7a5ed67cddb9d4a20da93ef3</guid>
    <pubDate>Mon, 27 May 2013 19:45:00 +0200</pubDate>
    <dc:creator>Abderrazek CHINE</dc:creator>
        <category>Java J2EE</category>
        <category>API Guava</category><category>Cache</category><category>CacheManager</category><category>Gestionnaire de cache</category><category>Google</category><category>Guava</category><category>JSR-250</category>    
    <description>&lt;p&gt;L'objet ici est de présenter avec trois démos détaillées la prise en main d'un gestionnaire de cache de l'API utilitaire Guava fournie par google.&lt;/p&gt;


&lt;p&gt;Cet article peut être rapproché de mon &lt;a href=&quot;http://blog.netapsys.fr/index.php/post/2013/02/27/Am%C3%A9liorez-les-performances-avec-Ehcache-et-Spring-3.x%3A-Spring-%40Cacheable&quot;&gt;billet&lt;/a&gt; qui traitait &lt;a href=&quot;http://ehcache.org/&quot;&gt;Ehcache&lt;/a&gt;.&lt;/p&gt;


&lt;p&gt;Chacune des démos est illustrée par un test unitaire.&lt;/p&gt;


&lt;p&gt;Les sources de la démo1 sont fournies.&lt;/p&gt;


&lt;p&gt;Passons à la mise en pratique.&lt;/p&gt;    &lt;p&gt;&lt;strong&gt;&lt;ins&gt;DEMO 1&lt;/ins&gt;&lt;/strong&gt;.&lt;/p&gt;


&lt;p&gt;Dans cette première démo, nous présentons un exemple simple permettant de prendre en main le gestionnaire de cache de Guava.&lt;/p&gt;


&lt;p&gt;&lt;ins&gt;Etape1&lt;/ins&gt;: Dépendances maven nécessaires&lt;/p&gt;


&lt;p&gt;Dans le pom du projet, en plus des dépendances spring deux autres sont nécessaires.&lt;/p&gt;


&lt;p&gt;La dépendance JSR-250 pour l'annotation @PostConstruct et celle de Guava.&lt;/p&gt;


&lt;p&gt;Donc, le pom.xml doit avoir ces lignes&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
      &amp;lt;!-- jsr 250 --&amp;gt;
      &amp;lt;dependency&amp;gt;
	&amp;lt;groupId&amp;gt;javax.annotation&amp;lt;/groupId&amp;gt;
	&amp;lt;artifactId&amp;gt;jsr250-api&amp;lt;/artifactId&amp;gt;
	&amp;lt;version&amp;gt;1.0&amp;lt;/version&amp;gt;
     &amp;lt;/dependency&amp;gt;
     &amp;lt;!-- guava --&amp;gt;
    &amp;lt;dependency&amp;gt;
	&amp;lt;groupId&amp;gt;com.google.guava&amp;lt;/groupId&amp;gt;
	&amp;lt;artifactId&amp;gt;guava&amp;lt;/artifactId&amp;gt;
	&amp;lt;version&amp;gt;12.0&amp;lt;/version&amp;gt;
  &amp;lt;/dependency&amp;gt;
&lt;/pre&gt;

&lt;p&gt;&lt;br /&gt;
&lt;br /&gt;&lt;/p&gt;



&lt;p&gt;&lt;ins&gt;Etape2&lt;/ins&gt;: Les classes java de la démo
&lt;br /&gt;
&lt;br /&gt;
Il y a essentiellement deux classes java dans cette première démo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;La première est un objet POJO de la couche model.&lt;/li&gt;
&lt;li&gt;La seconde est l'implémentation du service du cache.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;br /&gt;
La première classe est nommée Destination&amp;nbsp;:
&lt;br /&gt;&lt;/p&gt;
&lt;pre&gt;
public class Destination {
 private String libelle;
 private String ville;
 private String pays;
 private String critere;
    //..getters setters omis

}
&lt;/pre&gt;

&lt;p&gt;&lt;br /&gt;
La seconde classe nommée DestinationCacheManager est celle qui nous intéresse ici.&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;Elle permet de définir les opérations liées à la gestion du cache.&lt;br /&gt;
Ces opérations sont résumées dans l'interface suivant:
&lt;br /&gt;&lt;/p&gt;
&lt;pre&gt;
public interface DestinationCacheManager {
 void remove(String key) ;
 void put(String key,Destination d);
 Destination get(String key) ;
}
&lt;/pre&gt;


&lt;p&gt;Voici une implémentation de cette interface&amp;nbsp;:
&lt;br /&gt;&lt;/p&gt;
&lt;pre&gt;
package fr.netapsys.gestioncacheguava.services;
import java.util.concurrent.*;
import javax.annotation.PostConstruct;
import org.springframework.stereotype.Component;
import com.google.common.cache.*;
import fr.netapsys.gestioncacheguava.model.Destination;
@Component
public class DestinationCacheManagerImpl 
                  implements DestinationCacheManager{

	private Cache&amp;lt;String,Destination&amp;gt; cache;
	
	@PostConstruct
	public void init(){
	  cache = CacheBuilder.newBuilder().
			maximumSize(10).
			expireAfterWrite(1, TimeUnit.MINUTES).  
			/* expire 1 min apres ecriture (ttl)*/
			build();
	}
	
	public Destination get(String key) {
		return cache.getIfPresent(key);
	}
	public void remove(String key) {
		cache.invalidate(key);
	}
	public void put(String key,Destination d){
		cache.put(key, d);
	}
}
&lt;/pre&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;L'annotation @Component permet à Spring de gérer ce bean.&lt;/p&gt;


&lt;p&gt;L'annotation @PostConstruct de la jsr-250 permet d'initialiser, post création, le bean via la méthode annotée.&lt;/p&gt;


&lt;p&gt;Notez que l'implémentation de la méthode &lt;em&gt;get&lt;/em&gt; s'appuie sur &lt;em&gt;getIfPresent&lt;/em&gt; du &lt;em&gt;CacheBuilder&lt;/em&gt;.&lt;/p&gt;


&lt;p&gt;Celle-ci retourne null si l'objet n'est pas dans le cache sans lever d'exception.&lt;/p&gt;



&lt;p&gt;&lt;ins&gt;Etape3&lt;/ins&gt;: Configuration Spring&lt;/p&gt;


&lt;p&gt;Notre démo s'appuie sur le framework Spring, voici son fichier de configuration qui contient une seule ligne.&lt;/p&gt;


&lt;p&gt;Le fichier est nommé spring-context.xml et localisé dans src/main/resources/META-INF.&lt;/p&gt;

&lt;pre&gt;
?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;
&amp;lt;beans 
    xmlns=&amp;quot;http://www.springframework.org/schema/beans&amp;quot; 
    xmlns:xsi=&amp;quot;http://www.w3.org/2001/XMLSchema-instance&amp;quot;
    xmlns:context=&amp;quot;http://www.springframework.org/schema/context&amp;quot; 
    xmlns:p=&amp;quot;http://www.springframework.org/schema/p&amp;quot;
    xsi:schemaLocation=&amp;quot;
  http://www.springframework.org/schema/beans 
  http://www.springframework.org/schema/beans/spring-beans.xsd
  http://www.springframework.org/schema/context 
  http://www.springframework.org/schema/context/spring-context.xsd&amp;quot;&amp;gt;
 &amp;lt;context:component-scan base-package=&amp;quot;fr.netapsys&amp;quot;/&amp;gt;
&amp;lt;/beans&amp;gt;
&lt;/pre&gt;


&lt;p&gt;La seule chose à remarquer est la ligne context:component qui indique le package de base dans lequel Spring doit chercher les beans utiles.&lt;/p&gt;


&lt;p&gt;&lt;ins&gt;Etape4&lt;/ins&gt;: Test unitaire&lt;/p&gt;


&lt;p&gt;Nous générons une classe JUnit pour tester notre cache.&lt;/p&gt;


&lt;p&gt;Voici le code de cette classe:&lt;/p&gt;

&lt;pre&gt;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;

import fr.netapsys.gestioncacheguava.model.Destination;
import fr.netapsys.gestioncacheguava.services.DestinationCacheManager;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations=
     {&amp;quot;classpath*:META-INF/spring-context.xml&amp;quot;})
public class TestDestinationCache {
	@Autowired DestinationCacheManager dcm;
	@Test
	public void testSimple()  {
		Destination destFav=getDestDispo();
               ///mis en cache 
		dcm.put( &amp;quot;destFavorite&amp;quot;, destFav);
		
		//first get from cache
		Destination d1=dcm.get(&amp;quot;destFavorite&amp;quot;);
		Assert.assertSame(d1, destFav);
				
		//another get from cache
		Destination d2=dcm.get(&amp;quot;destFavorite&amp;quot;);
		Assert.assertSame(d2, destFav);	
	}
	private Destination getDestDispo() {
	 Destination destDispo=new Destination();
	 destDispo.setLibelle(&amp;quot;destinFavorite&amp;quot;);
	 destDispo.setVille(&amp;quot;Paris&amp;quot;);
	 destDispo.setPays(&amp;quot;France&amp;quot;);
	 destDispo.setCritere(&amp;quot;C1&amp;quot;);
	 return destDispo;
     }
}
&lt;/pre&gt;


&lt;p&gt;Quelques explications sont utiles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;l'annotation &lt;em&gt;@RunWith&lt;/em&gt; permet de lancer le test JUnit via le lanceur de Spring.&lt;/li&gt;
&lt;li&gt;l'annotation &lt;em&gt;@ContextConfiguration&lt;/em&gt; précise le fichier de configuration de Spring mentionné à l'étape précédente.&lt;/li&gt;
&lt;li&gt;l'annotation &lt;em&gt;@Autowired&lt;/em&gt; permet d'injecter le bean implémentant l'interface &lt;em&gt;DestinationCacheManager&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La méthode &lt;em&gt;testSimple()&lt;/em&gt; construit une instance du POJO Destination et le met en cache.&lt;/p&gt;


&lt;p&gt;Les appels successifs à la méthode get permettent de voir que, comme le prouve les assertions, l'objet est récupéré correctement du cache.&lt;/p&gt;


&lt;p&gt;Pour conclure, la façon de créer et gérer le cache dans la démo1 n'est pas idéale.&lt;/p&gt;


&lt;p&gt;Nous verrons dans la seconde démo comment mieux le gérer.&lt;/p&gt;


&lt;p&gt;Enfin, le projet &lt;ins&gt;démo1&lt;/ins&gt; peut-être téléchargé &lt;a href=&quot;http://blog.netapsys.fr/public/ach/guava/gestioncacheguava.zip&quot;&gt;ici&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;&lt;ins&gt;DEMO 2&lt;/ins&gt;&lt;/strong&gt;.&lt;/p&gt;


&lt;p&gt;Dans cette partie nous indiquons une autre façon (avancée) d'en tirer profit du gestionnaire de cache de Guava.
&lt;br /&gt;&lt;/p&gt;



&lt;p&gt;&lt;ins&gt;Partie 1&lt;/ins&gt;. Utilisation du &lt;em&gt;CacheLoader&lt;/em&gt;/&lt;em&gt;LoadingCache&lt;/em&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;ins&gt;Partie 2&lt;/ins&gt;. Utilisation du &lt;em&gt;Callable&lt;/em&gt;
&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;Nous nous basons sur les sources du projet de la démo1 que nous allons enrichir au fur et à mesure.&lt;/p&gt;


&lt;p&gt;C'est ce que nous ferons dans la seconde partie de ce billet.
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.netapsys.fr/index.php/post/2013/05/27/Le-cache-de-Guava#comment-form</comments>
      <wfw:comment>http://blog.netapsys.fr/index.php/post/2013/05/27/Le-cache-de-Guava#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.netapsys.fr/index.php/feed/atom/comments/606</wfw:commentRss>
      </item>
    
  <item>
    <title>Améliorez les performances avec Spring-Ehcache: Cache &amp; @Cacheable</title>
    <link>http://blog.netapsys.fr/index.php/post/2013/02/27/Am%C3%A9liorez-les-performances-avec-Ehcache-et-Spring-3.x%3A-Spring-%40Cacheable</link>
    <guid isPermaLink="false">urn:md5:929e194e60682c8bec22cbbaa38869a1</guid>
    <pubDate>Thu, 28 Feb 2013 18:32:00 +0100</pubDate>
    <dc:creator>Abderrazek CHINE</dc:creator>
        <category>Java J2EE</category>
        <category>@Cacheable</category><category>Cache</category><category>Cacheable</category><category>Caching</category><category>ehCache</category><category>Hibernate</category><category>Spring</category>    
    <description>&lt;p&gt;Le recours à l'emploi de caches dans nos projets permet d'améliorer significativement les performances.&lt;/p&gt;


&lt;p&gt;&lt;img src=&quot;http://blog.netapsys.fr/public/ach/logo-ehcache.png&quot; alt=&quot;logo-ehcache.png&quot; /&gt;&lt;/p&gt;


&lt;p&gt;Dans ce billet contrairement à &lt;a href=&quot;http://blog.netapsys.fr/index.php/post/2012/12/13/Int%C3%A9gration-de-EhCache-et-Spring&quot;&gt;celui-ci&lt;/a&gt; nous ne codons rien dans la partie métier pour éviter le couplage.&lt;/p&gt;


&lt;p&gt;Spring sait rendre les choses simples plus simples et les choses complexes possibles.&lt;/p&gt;


&lt;p&gt;Un cache sert à optimiser les accès aux données néanmoins ce n'est la solution miracle à tous les problèmes de performances.&lt;/p&gt;


&lt;p&gt;Mais une fois mis en œuvre à bon escient, on se demande comment peut-on s'en passer.&lt;/p&gt;


&lt;p&gt;Donc depuis Spring 3.1, implémenter une gestion de cache devient possible sans trop écrire du code.&lt;/p&gt;


&lt;p&gt;La démo ci-après, sans aborder les détails d'implémentation de la couche DAO, présente un exemple concret de mise en (EH)cache.&lt;/p&gt;


&lt;p&gt;Sachez néanmoins que les illustrations données reposent sur une implémentation basée sur &lt;a href=&quot;http://blog.netapsys.fr/index.php/post/2012/10/23/Pr%C3%A9sentation-%C3%A9tape-par-%C3%A9tape-de-Spring-DATA-JPA&quot; title=&quot;mon billet sir spring-data&quot;&gt;spring-data&lt;/a&gt;.&lt;/p&gt;


&lt;p&gt;Spring offre une implémentation light de mise en cache qui ne peut satisfaire les besoins de nos projets.&lt;/p&gt;


&lt;p&gt;C'est pour cette raison qu'ici nous utilisons &lt;a href=&quot;http://ehcache.org/&quot; title=&quot;Ehcache&quot;&gt;Ehcache&lt;/a&gt;.&lt;/p&gt;


&lt;p&gt;L'annotation @Cacheable de Spring apposée sur les méthodes (finder par exemple) suffit.&lt;/p&gt;


&lt;p&gt;L'AOP Spring rend la gestion du cache facile à l'instar des transactions déclaratives.
&lt;br /&gt;&lt;/p&gt;    &lt;p&gt;&lt;a href=&quot;http://ehcache.org/&quot; title=&quot;Ehcache&quot;&gt;Ehcache&lt;/a&gt; est une solution de cache Open Source.&lt;/p&gt;


&lt;p&gt;La solution Ehcache peut être utilisée en JEE comme en JSE.&lt;/p&gt;


&lt;p&gt;Hibernate utilise Ehcache.&lt;/p&gt;


&lt;p&gt;Passons à la mise en place de notre démo qui est un projet maven simple.&lt;/p&gt;


&lt;p&gt;Commençons par créer un projet maven à partir d'une template quickstart.&lt;/p&gt;


&lt;p&gt;Ensuite suivre les étapes suivantes.&lt;/p&gt;


&lt;p&gt;&lt;br /&gt;
&lt;strong&gt;ETAPE 1&lt;/strong&gt;.&lt;strong&gt; Ajouter les dépendances du pom&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Pour la mise en place de ehcache, ajouter ces lignes dans le fichier pom.xml:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;dependency&amp;gt;
  &amp;lt;groupId&amp;gt;org.hibernate&amp;lt;/groupId&amp;gt;
  &amp;lt;artifactId&amp;gt;hibernate-ehcache&amp;lt;/artifactId&amp;gt;
  &amp;lt;version&amp;gt;4.1.7&amp;lt;/version&amp;gt;
  &amp;lt;scope&amp;gt;compile&amp;lt;/scope&amp;gt;					
&amp;lt;/dependency&amp;gt;
&lt;/pre&gt;



&lt;p&gt;&lt;br /&gt;
&lt;strong&gt;ETAPE 2&lt;/strong&gt;. &lt;strong&gt;Configurer Spring&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Le fichier de configuration de spring doit être configuré en ajoutant ces lignes où les noms sont explicités.&lt;/p&gt;

&lt;pre&gt;
&amp;lt;!-- ehcache --&amp;gt;
 &amp;lt;!-- ehcache --&amp;gt;
   &amp;lt;cache:annotation-driven cache-manager=&amp;quot;ehcacheManager&amp;quot;/&amp;gt;
   &amp;lt;bean id=&amp;quot;ehcacheManager&amp;quot;
     class=&amp;quot;o.s.cache.ehcache.EhCacheCacheManager&amp;quot;
     p:cacheManager-ref=&amp;quot;ehcacheMFB&amp;quot;/&amp;gt;

     &amp;lt;bean id=&amp;quot;ehcacheMFB&amp;quot; 
      class=&amp;quot;o.s.cache.ehcache.EhCacheManagerFactoryBean&amp;quot;
     p:configLocation=&amp;quot;ehcache.xml&amp;quot;
     p:cacheManagerName=&amp;quot;ehcacheManager&amp;quot;
     p:shared=&amp;quot;true&amp;quot;/&amp;gt;
&lt;/pre&gt;


&lt;p&gt;&lt;ins&gt;NOTES&lt;/ins&gt;.&lt;/p&gt;


&lt;p&gt;Ici nous avons mis de côté le principe convention over configuration pour mieux détailler la configuration.&lt;/p&gt;


&lt;p&gt;L'abréviation o.s désigne org.springframework.&lt;/p&gt;


&lt;p&gt;Dans le bean ehcacheMFB nous faisons référence à &lt;em&gt;ehcache.xml&lt;/em&gt; qui sera détaillé ci-après.&lt;/p&gt;


&lt;p&gt;&lt;br /&gt;
&lt;strong&gt;ETAPE 3&lt;/strong&gt;. &lt;strong&gt;Configurer Ehcache&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;La configuration de ehcache est centrée dans le fichier ehcache.xml déjà mentionné dans la configuration de spring.&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;
&amp;lt;ehcache xmlns:xsi=&amp;quot;http://www.w3.org/2001/XMLSchema-instance&amp;quot; 
 xsi:noNamespaceSchemaLocation=&amp;quot;http://ehcache.org/ehcache.xsd&amp;quot;&amp;gt;
 &amp;lt;defaultCache eternal=&amp;quot;false&amp;quot; maxElementsInMemory=&amp;quot;100&amp;quot; 
    overflowToDisk=&amp;quot;false&amp;quot; /&amp;gt;
 &amp;lt;cache name=&amp;quot;cacheLivs&amp;quot;
  maxElementsInMemory=&amp;quot;10000&amp;quot; eternal=&amp;quot;false&amp;quot; 
  timeToIdleSeconds=&amp;quot;300&amp;quot;
  timeToLiveSeconds=&amp;quot;600&amp;quot;
  overflowToDisk=&amp;quot;false&amp;quot; /&amp;gt;
&amp;lt;/ehcache&amp;gt;
&lt;/pre&gt;


&lt;p&gt;&lt;br /&gt;
&lt;strong&gt;ETAPE 4&lt;/strong&gt;. &lt;strong&gt;Ecrire la classe du model (entité)&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;La classe Livraison est l'unique classe entité du model.&lt;/p&gt;

&lt;pre&gt;
package fr.netapsys.springehcache.model.common;

import java.util.Date;
import javax.persistence.*;

@Entity
public class Livraison  {
	@Id @GeneratedValue(strategy = GenerationType.AUTO)
	private Long id;
	private String libelle;
	private String quadriRegion;
	
	@Temporal(TemporalType.DATE)
	private Date dateLivraison;
	@Temporal(TemporalType.DATE)
	private Date dateDernInteg;
	@Temporal(TemporalType.DATE)
	
      /*omis ge(se)tters et toString*/
&lt;/pre&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;Vous remarquez l'absence d'annotations @Cache sur l'entité car nous ne souhaitons pas utiliser le cache de second niveau d'Hibernate.&lt;/p&gt;


&lt;p&gt;&lt;br /&gt;
&lt;strong&gt;ETAPE 5&lt;/strong&gt;. &lt;strong&gt;Ecrire l'interface du service&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Voici l'interface de la classe nommée ILivraisonService qui comporte des méthodes de recherche (finder).&lt;/p&gt;


&lt;p&gt;Ces méthodes sont annotées avec l'annotation de spring @Cacheable en indiquant comme attribut le nom du cache fourni à l'étape 3.&lt;/p&gt;

&lt;pre&gt;
public interface ILivraisonService {
	
	@Cacheable(value=&amp;quot;livraisons&amp;quot;)
	Livraison findOne(long id);
	
	@Cacheable(value=&amp;quot;livraisons&amp;quot;)
	List&amp;lt;Livraison&amp;gt; findAll();
	
	Livraison save(Livraison liv);
}
&lt;/pre&gt;


&lt;p&gt;Seules les deux méthodes findOne et findAll sont annotées avec @Cacheable en indiquant le nom du cache configuré dans ehcache.xml.&lt;/p&gt;


&lt;p&gt;&lt;br /&gt;
&lt;strong&gt;ETAPE 6&lt;/strong&gt;. &lt;strong&gt;Ecrire l'implémentation du service&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Ici aucun détail d'implémentation n'est abordée pour la couche DAO.&lt;/p&gt;


&lt;p&gt;La classe de service s’appuiera sur l'interface DAO.&lt;/p&gt;


&lt;p&gt;Le code de la classe LivraisonService est simple:&lt;/p&gt;

&lt;pre&gt;
package fr.netapsys.springehcache.service;

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import fr.netapsys.springehcache.model.common.Livraison;
import fr.netapsys.springehcache.repository.ILivraisonRepository;

@Service
public class LivraisonService implements ILivraisonService {

 @Autowired private ILivraisonRepository livRepo;

 /**** Methodes Finder *****/
 @Override
 public List&amp;lt;Livraison&amp;gt; findAll() {
 	return livRepo.findAll();
 }
 @Override
 public Livraison findOne(long id) {
	return livRepo.findOne(id);
 }
/**** Methodes de MAJ *****/

 @Override
 public Livraison save(Livraison liv) {
 	return livRepo.save(liv);
 }	
 @Override
 public void delete(Livraison liv) {
	livRepo.delete(liv);
 }	
}
&lt;/pre&gt;


&lt;p&gt;Notez que l'interface ILivraisonRepository n'apporte rien à notre démo.&lt;/p&gt;



&lt;p&gt;Pour les curieux, mon billet sur Spring-Data donne les détails sur cette implémentation utilisant Spring-DATA (voir &lt;a href=&quot;http://blog.netapsys.fr/index.php/post/2012/10/23/Pr%C3%A9sentation-%C3%A9tape-par-%C3%A9tape-de-Spring-DATA-JPA&quot; title=&quot;partie 1 de spring-data&quot;&gt;Partie1&lt;/a&gt; et &lt;a href=&quot;http://blog.netapsys.fr/index.php/post/2012/10/29/Pr%C3%A9sentation%2C-%C3%A9tape-par-%C3%A9tape%2C-de-Spring-DATA-JPA-%28-Partie-2%29&quot; title=&quot;Partie 2 de spring-data&quot;&gt;partie2&lt;/a&gt;).&lt;/p&gt;


&lt;p&gt;&lt;br /&gt;
&lt;strong&gt;ETAPE 7&lt;/strong&gt;. &lt;strong&gt;Désactiver le cache de second niveau Hibernate (&lt;em&gt;Optionnelle&lt;/em&gt;)&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Si vous utilisez l'implémentation Hibernate pour votre couche DAO, il serait utile lors la définition du bean &lt;em&gt;entityManagerFactory&lt;/em&gt; de mettre la propriété suivante à &lt;em&gt;false&lt;/em&gt;:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;prop key=&amp;quot;hibernate.cache.use_second_level_cache&amp;quot;&amp;gt;false&amp;lt;/prop&amp;gt;
&lt;/pre&gt;


&lt;p&gt;&lt;br /&gt;
&lt;strong&gt;ETAPE 8&lt;/strong&gt;. &lt;strong&gt;Tester&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Voici le code du test JUnit&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
package fr.netapsys.springehcache.tests;
import static junit.framework.Assert.assertSame;
import static junit.framework.Assert.assertTrue;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import fr.netapsys.springehcache.model.common.Livraison;
import fr.netapsys.springehcache.service.ILivraisonService;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations=
     {&amp;quot;classpath*:config/applicationContext.xml&amp;quot; })
public class CacheableLivraisonTest
{
    @Autowired ILivraisonService livService;
    
  @Test public void testFindOne(){
	/*** fixer la valeur de l'id en fonction de la BD**/
	final int id=89;
	Livraison liv1=livService.findOne(id);
	/** assret suivante depend du contenu de la BD***/
	assertTrue(liv1.getLibelle().contains(&amp;quot;pdl&amp;quot;));
	
	Livraison liv2=livService.findOne(id);
	
	assertSame(liv1,liv2);
	
	liv2=livService.findOne(id);
	
	assertSame(liv1,liv2);
 }
}
&lt;/pre&gt;


&lt;p&gt;Il ne reste qu'à lancer la commande mvn test.&lt;/p&gt;


&lt;p&gt;Vous constatez alors que le second appel à findOne ne génère pas un appel à la base de données.&lt;/p&gt;


&lt;p&gt;L'assertion assertSame met en évidence que c'est la même instance récupérée du cache.&lt;/p&gt;


&lt;p&gt;Avec mon implémentation de la couche DAO basée sur spring-data (JPA), j'obtiens les traces suivantes en ayant au préalable configuré log4j:&lt;/p&gt;

&lt;pre&gt;
DEBUG (AbstractFallbackCacheOperationSource.java:105) 
   Adding cacheable method 'findOne' with attribute:
 [CacheableOperation[public abstract 
   fr.netapsys.springehcache.model.common.Livraison 
  fr.netapsys.springehcache.service.ILivraisonService.findOne(long)]
  caches=[cacheLivs] | condition='' | key='']
INFO  (EhCacheManagerFactoryBean.java:100) 
  Initializing EHCache CacheManager
Hibernate: 
  /* load fr.netapsys.springehcache.model.common.Livraison */ 
 select
      livraison0_.id as id0_0_,
        livraison0_.dateData as dateData0_0_,
        livraison0_.dateDernInteg as dateDern3_0_0_,
        livraison0_.dateFichierEns as dateFich4_0_0_,
        livraison0_.dateFichierInt as dateFich5_0_0_,
        livraison0_.dateLivraison as dateLivr6_0_0_,
        livraison0_.dateLivraisonSoc as dateLivr7_0_0_,
        livraison0_.libelle as libelle0_0_,
        livraison0_.quadriRegion as quadriRe9_0_0_ 
    from
        public.Livraison livraison0_ 
    where
        livraison0_.id=?
TRACE (BasicBinder.java:83) binding parameter [1] as [BIGINT] - 89

TRACE (BasicExtractor.java:72) 
   Found [2013-02-22] as column [dateData0_0_]
....
TRACE (BasicExtractor.java:72) 
     Found [PDL] as column [quadriRe9_0_0_]
..
TRACE (CacheAspectSupport.java:294) Computed cache key 89 
  for operation CacheableOperation
 [public abstract fr.netapsys.springehcache.model.common.Livraison 
  fr.netapsys.springehcache.service.ILivraisonService.findOne(long)] 
  caches=[cacheLivs] | condition='' | key=''
TRACE (CacheAspectSupport.java:294) Computed cache key 89 for 
 operation CacheableOperation
 [public abstract fr.netapsys.springehcache.model.common.Livraison 
 fr.netapsys.springehcache.service.ILivraisonService.findOne(long)] 
  caches=[cacheLivs] | condition='' | key=''
INFO  (EhCacheManagerFactoryBean.java:133) 
     Shutting down EHCache CacheManager
&lt;/pre&gt;


&lt;p&gt;Les traces sont bien parlantes surtout à la fin.&lt;/p&gt;


&lt;p&gt;Vous pouvez voir que le  premier appel de findOne a généré une requête &lt;em&gt;select * from Livraison where id=?&lt;/em&gt;&lt;/p&gt;


&lt;p&gt;Mais le second appel à findOne se contente de chercher le résultat depuis le cache &lt;em&gt;cacheLivs&lt;/em&gt;.&lt;/p&gt;



&lt;p&gt;Concluons:&lt;/p&gt;


&lt;p&gt;Il suffit d'ajouter les dépendances maven, d'annoter les méthodes concernées, de configurer le cacheManager(Factory) d'Ehcache et de laisser l'AOP Spring opérer.&lt;/p&gt;


&lt;p&gt;Le réglage du cacheManager(Factory) est d'important pour garantir la cohésion des données mais ceci est un autre sujet.&lt;/p&gt;


&lt;p&gt;@Enjoy&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.netapsys.fr/index.php/post/2013/02/27/Am%C3%A9liorez-les-performances-avec-Ehcache-et-Spring-3.x%3A-Spring-%40Cacheable#comment-form</comments>
      <wfw:comment>http://blog.netapsys.fr/index.php/post/2013/02/27/Am%C3%A9liorez-les-performances-avec-Ehcache-et-Spring-3.x%3A-Spring-%40Cacheable#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.netapsys.fr/index.php/feed/atom/comments/567</wfw:commentRss>
      </item>
    
  <item>
    <title>Camel SQL composant: Composant Camel-SQL</title>
    <link>http://blog.netapsys.fr/index.php/post/2013/02/14/Camel-SQL-composant%3A-Composant-Camel-SQL</link>
    <guid isPermaLink="false">urn:md5:6d3cc1095e1cc02f15f4b14f80afcdd3</guid>
    <pubDate>Thu, 14 Feb 2013 18:53:00 +0100</pubDate>
    <dc:creator>Abderrazek CHINE</dc:creator>
        <category>Java J2EE</category>
        <category>camel</category><category>camel sql composant</category><category>camel-sql</category><category>java 7</category><category>junit</category><category>spring</category><category>sql</category><category>test</category>    
    <description>&lt;p&gt;Ce billet illustre l'emploi du composant Camel-SQL.
&lt;img src=&quot;http://blog.netapsys.fr/public/ach/camel/camel-logo.png&quot; alt=&quot;camel-logo.png&quot; /&gt;&lt;/p&gt;


&lt;p&gt;Dans notre démo le composant camel-sql s'appuie sur une datasource déclarée dans spring.&lt;/p&gt;


&lt;p&gt;La démo est réalisée en cinq étapes:&lt;/p&gt;


&lt;p&gt;I. CONFIGURER LE POM DU PROJET&lt;/p&gt;


&lt;p&gt;II. CONFIGURER SPRING&lt;/p&gt;


&lt;p&gt;III. DESSINER LA ROUTE CAMEL&lt;/p&gt;


&lt;p&gt;IV. CONFIGURER L'ENVIRONNEMENT&lt;/p&gt;


&lt;p&gt;V. TESTER&lt;/p&gt;


&lt;p&gt;Passons à la pratique.&lt;/p&gt;    &lt;p&gt;Voici les étapes de la démo qui utilise java 7, spring 3, camel 2.10:&lt;/p&gt;


&lt;p&gt;&lt;strong&gt;ETAPE 1&lt;/strong&gt;. &lt;strong&gt;CONFIGURER LE POM&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Pour utiliser le composant camel-sql on a besoin uniquement de cette déclaration:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;dependency&amp;gt;
 &amp;lt;groupId&amp;gt;org.apache.camel&amp;lt;/groupId&amp;gt;
 &amp;lt;artifactId&amp;gt;camel-sql&amp;lt;/artifactId&amp;gt;
 &amp;lt;version&amp;gt;2.10.2&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/pre&gt;


&lt;p&gt;
Voici le pom.xml en entier:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;properties&amp;gt;
	&amp;lt;!-- Versions Dependances --&amp;gt;
	&amp;lt;commons-beanutils-version&amp;gt;1.8.3&amp;lt;/commons-beanutils-version&amp;gt;
	&amp;lt;commons-codec-version&amp;gt;1.6&amp;lt;/commons-codec-version&amp;gt;
	&amp;lt;commons-collections-version&amp;gt;3.2.1&amp;lt;/commons-collections-version&amp;gt;
	&amp;lt;commons-lang-version&amp;gt;2.6&amp;lt;/commons-lang-version&amp;gt;
	&amp;lt;commons-digester-version&amp;gt;2.1&amp;lt;/commons-digester-version&amp;gt;
	&amp;lt;javassist-version&amp;gt;3.12.1.GA&amp;lt;/javassist-version&amp;gt;
	&amp;lt;cglib-version&amp;gt;2.2.2&amp;lt;/cglib-version&amp;gt;

	&amp;lt;!-- Log --&amp;gt;
	&amp;lt;log4j-version&amp;gt;1.2.17&amp;lt;/log4j-version&amp;gt;
	&amp;lt;slf4j-version&amp;gt;1.6.6&amp;lt;/slf4j-version&amp;gt;

	&amp;lt;!-- Spring --&amp;gt;
	&amp;lt;spring-version&amp;gt;3.1.2.RELEASE&amp;lt;/spring-version&amp;gt;
	&amp;lt;!-- camel-version --&amp;gt;
	&amp;lt;camel-version&amp;gt;2.10.2&amp;lt;/camel-version&amp;gt;
	&amp;lt;!-- Versions Dependances de test --&amp;gt;
	&amp;lt;junit-version&amp;gt;4.10&amp;lt;/junit-version&amp;gt;

	&amp;lt;!-- PostgresSql --&amp;gt;
	&amp;lt;postgresql-version&amp;gt;9.1-901-1.jdbc4&amp;lt;/postgresql-version&amp;gt;

&amp;lt;/properties&amp;gt;

&amp;lt;dependencies&amp;gt;

	&amp;lt;dependency&amp;gt;
		&amp;lt;groupId&amp;gt;commons-dbcp&amp;lt;/groupId&amp;gt;
		&amp;lt;artifactId&amp;gt;commons-dbcp&amp;lt;/artifactId&amp;gt;
		&amp;lt;version&amp;gt;1.2.2&amp;lt;/version&amp;gt;
	&amp;lt;/dependency&amp;gt;
	&amp;lt;dependency&amp;gt;
		&amp;lt;groupId&amp;gt;commons-pool&amp;lt;/groupId&amp;gt;
		&amp;lt;artifactId&amp;gt;commons-pool&amp;lt;/artifactId&amp;gt;
		&amp;lt;version&amp;gt;1.2&amp;lt;/version&amp;gt;
	&amp;lt;/dependency&amp;gt;

	&amp;lt;!-- dep transitive de JavConfig see @Configuration --&amp;gt;
	&amp;lt;dependency&amp;gt;
		&amp;lt;groupId&amp;gt;javassist&amp;lt;/groupId&amp;gt;
		&amp;lt;artifactId&amp;gt;javassist&amp;lt;/artifactId&amp;gt;
		&amp;lt;version&amp;gt;${javassist-version}&amp;lt;/version&amp;gt;
		&amp;lt;scope&amp;gt;compile&amp;lt;/scope&amp;gt;
	&amp;lt;/dependency&amp;gt;
	&amp;lt;dependency&amp;gt;
		&amp;lt;groupId&amp;gt;cglib&amp;lt;/groupId&amp;gt;
		&amp;lt;artifactId&amp;gt;cglib&amp;lt;/artifactId&amp;gt;
		&amp;lt;version&amp;gt;${cglib-version}&amp;lt;/version&amp;gt;
		&amp;lt;scope&amp;gt;compile&amp;lt;/scope&amp;gt;
	&amp;lt;/dependency&amp;gt;

	&amp;lt;!-- spring-core --&amp;gt;
	&amp;lt;dependency&amp;gt;
		&amp;lt;groupId&amp;gt;org.springframework&amp;lt;/groupId&amp;gt;
		&amp;lt;artifactId&amp;gt;spring-core&amp;lt;/artifactId&amp;gt;
		&amp;lt;version&amp;gt;${spring-version}&amp;lt;/version&amp;gt;
		&amp;lt;scope&amp;gt;compile&amp;lt;/scope&amp;gt;
	&amp;lt;/dependency&amp;gt;
	
	&amp;lt;!-- spring-expression --&amp;gt;
	&amp;lt;dependency&amp;gt;
		&amp;lt;groupId&amp;gt;org.springframework&amp;lt;/groupId&amp;gt;
		&amp;lt;artifactId&amp;gt;spring-expression&amp;lt;/artifactId&amp;gt;
		&amp;lt;version&amp;gt;${spring-version}&amp;lt;/version&amp;gt;
		&amp;lt;scope&amp;gt;compile&amp;lt;/scope&amp;gt;
	&amp;lt;/dependency&amp;gt;
	&amp;lt;!-- spring-asm --&amp;gt;
	&amp;lt;dependency&amp;gt;
		&amp;lt;groupId&amp;gt;org.springframework&amp;lt;/groupId&amp;gt;
		&amp;lt;artifactId&amp;gt;spring-asm&amp;lt;/artifactId&amp;gt;
		&amp;lt;version&amp;gt;${spring-version}&amp;lt;/version&amp;gt;
		&amp;lt;scope&amp;gt;compile&amp;lt;/scope&amp;gt;
	&amp;lt;/dependency&amp;gt;
	&amp;lt;!-- spring-beans --&amp;gt;
	&amp;lt;dependency&amp;gt;
		&amp;lt;groupId&amp;gt;org.springframework&amp;lt;/groupId&amp;gt;
		&amp;lt;artifactId&amp;gt;spring-beans&amp;lt;/artifactId&amp;gt;
		&amp;lt;version&amp;gt;${spring-version}&amp;lt;/version&amp;gt;
		&amp;lt;scope&amp;gt;compile&amp;lt;/scope&amp;gt;
	&amp;lt;/dependency&amp;gt;
	&amp;lt;!-- spring-context --&amp;gt;
	&amp;lt;dependency&amp;gt;
		&amp;lt;groupId&amp;gt;org.springframework&amp;lt;/groupId&amp;gt;
		&amp;lt;artifactId&amp;gt;spring-context&amp;lt;/artifactId&amp;gt;
		&amp;lt;version&amp;gt;${spring-version}&amp;lt;/version&amp;gt;
		&amp;lt;scope&amp;gt;compile&amp;lt;/scope&amp;gt;
	&amp;lt;/dependency&amp;gt;
	&amp;lt;!-- spring-context-support --&amp;gt;
	&amp;lt;dependency&amp;gt;
		&amp;lt;groupId&amp;gt;org.springframework&amp;lt;/groupId&amp;gt;
		&amp;lt;artifactId&amp;gt;spring-context-support&amp;lt;/artifactId&amp;gt;
		&amp;lt;version&amp;gt;${spring-version}&amp;lt;/version&amp;gt;
		&amp;lt;scope&amp;gt;compile&amp;lt;/scope&amp;gt;
	&amp;lt;/dependency&amp;gt;
	&amp;lt;!-- postgres --&amp;gt;
	&amp;lt;dependency&amp;gt;
		&amp;lt;groupId&amp;gt;postgresql&amp;lt;/groupId&amp;gt;
		&amp;lt;artifactId&amp;gt;postgresql&amp;lt;/artifactId&amp;gt;
		&amp;lt;version&amp;gt;${postgresql-version}&amp;lt;/version&amp;gt;
		&amp;lt;scope&amp;gt;compile&amp;lt;/scope&amp;gt;
	&amp;lt;/dependency&amp;gt;
	&amp;lt;!-- CAMel --&amp;gt;
	&amp;lt;dependency&amp;gt;
		&amp;lt;groupId&amp;gt;org.apache.camel&amp;lt;/groupId&amp;gt;
		&amp;lt;artifactId&amp;gt;camel-core&amp;lt;/artifactId&amp;gt;
		&amp;lt;version&amp;gt;${camel-version}&amp;lt;/version&amp;gt;
	&amp;lt;/dependency&amp;gt;
	&amp;lt;!--    camel-sql --&amp;gt;		
	&amp;lt;dependency&amp;gt;
		&amp;lt;groupId&amp;gt;org.apache.camel&amp;lt;/groupId&amp;gt;
		&amp;lt;artifactId&amp;gt;camel-sql&amp;lt;/artifactId&amp;gt;
		&amp;lt;version&amp;gt;${camel-version}&amp;lt;/version&amp;gt;
	&amp;lt;/dependency&amp;gt;
	&amp;lt;dependency&amp;gt;
		&amp;lt;groupId&amp;gt;org.apache.camel&amp;lt;/groupId&amp;gt;
		&amp;lt;artifactId&amp;gt;camel-spring&amp;lt;/artifactId&amp;gt;
		&amp;lt;version&amp;gt;${camel-version}&amp;lt;/version&amp;gt;
	&amp;lt;/dependency&amp;gt;
	&amp;lt;!-- camel-spring-config --&amp;gt;
	&amp;lt;dependency&amp;gt;
		&amp;lt;groupId&amp;gt;org.apache.camel&amp;lt;/groupId&amp;gt;
		&amp;lt;artifactId&amp;gt;camel-spring-javaconfig&amp;lt;/artifactId&amp;gt;
		&amp;lt;version&amp;gt;${camel-version}&amp;lt;/version&amp;gt;
	&amp;lt;/dependency&amp;gt;
	&amp;lt;!-- logging --&amp;gt;
	&amp;lt;dependency&amp;gt;
		&amp;lt;groupId&amp;gt;org.slf4j&amp;lt;/groupId&amp;gt;
		&amp;lt;artifactId&amp;gt;slf4j-api&amp;lt;/artifactId&amp;gt;
		&amp;lt;version&amp;gt;1.6.6&amp;lt;/version&amp;gt;
	&amp;lt;/dependency&amp;gt;
	&amp;lt;dependency&amp;gt;
		&amp;lt;groupId&amp;gt;org.slf4j&amp;lt;/groupId&amp;gt;
		&amp;lt;artifactId&amp;gt;slf4j-log4j12&amp;lt;/artifactId&amp;gt;
		&amp;lt;version&amp;gt;1.6.6&amp;lt;/version&amp;gt;
	&amp;lt;/dependency&amp;gt;
	&amp;lt;dependency&amp;gt;
		&amp;lt;groupId&amp;gt;log4j&amp;lt;/groupId&amp;gt;
		&amp;lt;artifactId&amp;gt;log4j&amp;lt;/artifactId&amp;gt;
		&amp;lt;version&amp;gt;1.2.17&amp;lt;/version&amp;gt;
	&amp;lt;/dependency&amp;gt;
	&amp;lt;!-- logging --&amp;gt;
	&amp;lt;dependency&amp;gt;
		&amp;lt;groupId&amp;gt;commons-logging&amp;lt;/groupId&amp;gt;
		&amp;lt;artifactId&amp;gt;commons-logging&amp;lt;/artifactId&amp;gt;
		&amp;lt;version&amp;gt;1.1&amp;lt;/version&amp;gt;
		&amp;lt;scope&amp;gt;runtime&amp;lt;/scope&amp;gt;
	&amp;lt;/dependency&amp;gt;
	&amp;lt;!-- CAMEL -test-spring  --&amp;gt;
	&amp;lt;dependency&amp;gt;
		&amp;lt;groupId&amp;gt;org.apache.camel&amp;lt;/groupId&amp;gt;
		&amp;lt;artifactId&amp;gt;camel-test-spring&amp;lt;/artifactId&amp;gt;
		&amp;lt;version&amp;gt;${camel-version}&amp;lt;/version&amp;gt;
		&amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
	&amp;lt;/dependency&amp;gt;
	&amp;lt;!-- spring-test --&amp;gt;
	&amp;lt;dependency&amp;gt;
		&amp;lt;groupId&amp;gt;org.springframework&amp;lt;/groupId&amp;gt;
		&amp;lt;artifactId&amp;gt;spring-test&amp;lt;/artifactId&amp;gt;
		&amp;lt;version&amp;gt;3.1.2.RELEASE&amp;lt;/version&amp;gt;
		&amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
	&amp;lt;/dependency&amp;gt;
	&amp;lt;!-- DEPENDANCES DE TESTS junit --&amp;gt;
	&amp;lt;dependency&amp;gt;
		&amp;lt;groupId&amp;gt;junit&amp;lt;/groupId&amp;gt;
		&amp;lt;artifactId&amp;gt;junit&amp;lt;/artifactId&amp;gt;
		&amp;lt;version&amp;gt;${junit-version}&amp;lt;/version&amp;gt;
		&amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
	&amp;lt;/dependency&amp;gt;
 &amp;lt;!-- commons beanutils --&amp;gt;
 &amp;lt;dependency&amp;gt;
	&amp;lt;groupId&amp;gt;commons-beanutils&amp;lt;/groupId&amp;gt;
	&amp;lt;artifactId&amp;gt;commons-beanutils&amp;lt;/artifactId&amp;gt;
	&amp;lt;version&amp;gt;${commons-beanutils-version}&amp;lt;/version&amp;gt;
 &amp;lt;/dependency&amp;gt;
	&amp;lt;!-- commons codec --&amp;gt;
	&amp;lt;dependency&amp;gt;
		&amp;lt;groupId&amp;gt;commons-codec&amp;lt;/groupId&amp;gt;
		&amp;lt;artifactId&amp;gt;commons-codec&amp;lt;/artifactId&amp;gt;
		&amp;lt;version&amp;gt;${commons-codec-version}&amp;lt;/version&amp;gt;
		&amp;lt;scope&amp;gt;compile&amp;lt;/scope&amp;gt;
	&amp;lt;/dependency&amp;gt;
	&amp;lt;!-- --&amp;gt;
	&amp;lt;dependency&amp;gt;
		&amp;lt;groupId&amp;gt;commons-collections&amp;lt;/groupId&amp;gt;
		&amp;lt;artifactId&amp;gt;commons-collections&amp;lt;/artifactId&amp;gt;
		&amp;lt;version&amp;gt;${commons-collections-version}&amp;lt;/version&amp;gt;
		&amp;lt;scope&amp;gt;compile&amp;lt;/scope&amp;gt;
	&amp;lt;/dependency&amp;gt;
	&amp;lt;dependency&amp;gt;
		&amp;lt;groupId&amp;gt;commons-lang&amp;lt;/groupId&amp;gt;
		&amp;lt;artifactId&amp;gt;commons-lang&amp;lt;/artifactId&amp;gt;
		&amp;lt;version&amp;gt;${commons-lang-version}&amp;lt;/version&amp;gt;
		&amp;lt;scope&amp;gt;compile&amp;lt;/scope&amp;gt;
	&amp;lt;/dependency&amp;gt;
	&amp;lt;!-- common DIGESTER --&amp;gt;
	&amp;lt;dependency&amp;gt;
	 &amp;lt;groupId&amp;gt;commons-digester&amp;lt;/groupId&amp;gt;
	 &amp;lt;artifactId&amp;gt;commons-digester&amp;lt;/artifactId&amp;gt;
	 &amp;lt;version&amp;gt;${commons-digester-version}&amp;lt;/version&amp;gt;
	&amp;lt;/dependency&amp;gt;
&amp;lt;/dependencies&amp;gt;
&amp;lt;build&amp;gt;
 &amp;lt;defaultGoal&amp;gt;install&amp;lt;/defaultGoal&amp;gt;
 &amp;lt;plugins&amp;gt;
  &amp;lt;plugin&amp;gt;
  &amp;lt;groupId&amp;gt;org.apache.maven.plugins&amp;lt;/groupId&amp;gt;
  &amp;lt;artifactId&amp;gt;maven-compiler-plugin&amp;lt;/artifactId&amp;gt;
  &amp;lt;version&amp;gt;2.5.1&amp;lt;/version&amp;gt;
  &amp;lt;configuration&amp;gt;
 	&amp;lt;source&amp;gt;1.7&amp;lt;/source&amp;gt;
	&amp;lt;target&amp;gt;1.7&amp;lt;/target&amp;gt;
  &amp;lt;/configuration&amp;gt;
&amp;lt;/plugin&amp;gt;
&amp;lt;!-- allows the route to be ran via 'mvn camel:run' --&amp;gt;
&amp;lt;plugin&amp;gt;
	&amp;lt;groupId&amp;gt;org.apache.camel&amp;lt;/groupId&amp;gt;
	&amp;lt;artifactId&amp;gt;camel-maven-plugin&amp;lt;/artifactId&amp;gt;
	&amp;lt;version&amp;gt;${camel-version}&amp;lt;/version&amp;gt;
&amp;lt;/plugin&amp;gt;
&amp;lt;/plugins&amp;gt;
&amp;lt;/build&amp;gt;
&amp;lt;/project&amp;gt;
&lt;/pre&gt;



&lt;p&gt;&lt;strong&gt;ETAPE 2&lt;/strong&gt;. &lt;strong&gt;CONFIGURER SPRING&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;La configuration de spring est centrée dans le fichier xml suivant:&lt;/p&gt;

&lt;pre&gt;

&amp;lt;context:property-placeholder location=&amp;quot;db.properties&amp;quot;/&amp;gt;
&amp;lt;context:component-scan base-package=&amp;quot;fr.netapsys.camel&amp;quot;/&amp;gt;
&amp;lt;camel:camelContext id=&amp;quot;camel1&amp;quot;
	xmlns=&amp;quot;http://camel.apache.org/schema/spring&amp;quot;&amp;gt;
	&amp;lt;camel:package&amp;gt;fr.netapsys.camel.routes&amp;lt;/camel:package&amp;gt; 
&amp;lt;/camel:camelContext&amp;gt;

&amp;lt;bean id=&amp;quot;myDataSource&amp;quot; class=&amp;quot;org.apache.commons.dbcp.BasicDataSource&amp;quot; 
     destroy-method=&amp;quot;close&amp;quot;&amp;gt;
    &amp;lt;property name=&amp;quot;driverClassName&amp;quot; value=&amp;quot;${bd.driverClassName}&amp;quot;/&amp;gt;
    &amp;lt;property name=&amp;quot;url&amp;quot; value=&amp;quot;${bd.url}&amp;quot;/&amp;gt;
    &amp;lt;property name=&amp;quot;username&amp;quot; value=&amp;quot;${bd.username}&amp;quot;/&amp;gt;
    &amp;lt;property name=&amp;quot;password&amp;quot; value=&amp;quot;${bd.password}&amp;quot;/&amp;gt;
    &amp;lt;property name=&amp;quot;defaultAutoCommit&amp;quot; value=&amp;quot;true&amp;quot; /&amp;gt;
&amp;lt;/bean&amp;gt;
&lt;/pre&gt;



&lt;p&gt;Explications:&lt;/p&gt;


&lt;p&gt;Un fichier (placeholder pour spring) contient les paramètres (propriétés) de définition de la datasource nommée myDataSource.&lt;/p&gt;


&lt;p&gt;Les routes camel sont auto-injectées en les scannant dans le répertoire de &amp;lt;camel:package&amp;gt;.&lt;/p&gt;


&lt;p&gt;Les autres composants à injecter par spring sont scannés depuis le &amp;lt;context:component-scan base-package.&lt;/p&gt;


&lt;p&gt;Le fichier&lt;em&gt; db.properties&lt;/em&gt; est donné à l'étape 4.&lt;/p&gt;


&lt;p&gt;&lt;strong&gt;ETAPE 3&lt;/strong&gt;. &lt;strong&gt;DESSINER LA ROURE CAMEL-SQL&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;La route camel permettant de rechercher ( et d'insérer) des données depuis (dans) la table CLIENTS de la base s'écrit&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
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,&amp;quot;&amp;gt;&amp;gt;&amp;gt;Error in:  ${body},\n ${exception}&amp;quot;);
        	
  //using SqlEndpoint select from CLIENTS
 from(&amp;quot;direct:simpleSelect&amp;quot;)
 .to(&amp;quot;sql:SELECT * FROM CLIENTS 
           WHERE id=# ?dataSourceRef=myDataSource&amp;quot;)
 .end();

 //using SqlEndpoint to insert into CLIENTS
from(&amp;quot;direct:insertSql&amp;quot;) 
.to(&amp;quot;sql:INSERT INTO CLIENTS
     (id, nom,prenom) values (#,#,#)?dataSourceRef=myDataSource&amp;quot;)
.end();
 }
}
&lt;/pre&gt;



&lt;p&gt;Noter que chacune des deux chaînes de&lt;em&gt; .to&lt;/em&gt; doivent être sur la même ligne.&lt;/p&gt;


&lt;p&gt;Vous pouvez constater que deux endpoints sont définis&amp;nbsp;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;le premier permet d'effectuer un simple select en fonction de l'id du client.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;le second effectue une insertion des trois champs de la table CLIENTS.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;&lt;strong&gt;ETAPE 4&lt;/strong&gt;. &lt;strong&gt;CONFIGURER L'ENVIRONNEMENT&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;&lt;strong&gt;A&lt;/strong&gt;) &lt;ins&gt;Fichier db.properties&lt;/ins&gt;&lt;/p&gt;


&lt;p&gt;Ce fichier (placeholder de spring) contient les paramètres d'accès à la base postgres qu'il faudrait adapter.&lt;/p&gt;

&lt;pre&gt;
#params db postgres
bd.driverClassName=org.postgresql.Driver
bd.url=jdbc:postgresql://localhost:5432/test
bd.username=XXXXXX
bd.password=XXXX
&lt;/pre&gt;


&lt;p&gt;&lt;br /&gt;
&lt;strong&gt;B&lt;/strong&gt;) &lt;ins&gt;Script de création de la table CLIENTS&lt;/ins&gt;&lt;/p&gt;


&lt;p&gt;Voici le script pour créer la table clients&lt;/p&gt;

&lt;pre&gt;
CREATE TABLE clients(
  id bigint PRIMARY KEY NOT NULL,
  nom varchar(255),
  prenom varchar(255)
);
CREATE UNIQUE INDEX clients_prim_key ON clients(id);
&lt;/pre&gt;



&lt;p&gt;&lt;br /&gt;
&lt;strong&gt;ETAPE 4&lt;/strong&gt;. &lt;strong&gt;TESTER AVEC JUNIT&lt;/strong&gt;&lt;/p&gt;

&lt;pre&gt;
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(&amp;quot;direct:insertSql&amp;quot;);
	Exchange exchange = endpoint.createExchange();
	final long id = ID_2_CREATE+1001;
	final String nom = &amp;quot;toto&amp;quot;+id;
	final String prenom = &amp;quot;prenom&amp;quot;+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);
   }
}
&lt;/pre&gt;


&lt;p&gt;Lancer la commande &lt;em&gt;mvn test&lt;/em&gt; pour voir une ligne s'insérer dans la table CLIENTS.&lt;/p&gt;


&lt;p&gt;Le test suivant vous permet de recherche un client par son id.&lt;/p&gt;


&lt;p&gt;Vérifier que l'id passé existe en base.&lt;/p&gt;

&lt;pre&gt;
@Test 
public void testSelectSql() throws Exception {

	final static int ID_4_SELECT=110;
	ProducerTemplate template = camelCtx.createProducerTemplate();
	
	Endpoint endpoint = camelCtx.getEndpoint(&amp;quot;direct:simpleSelect&amp;quot;);
	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
                   (&amp;quot;CamelSqlRowCount&amp;quot;,Integer.class);
	assertTrue(nb == 1);
	List&amp;lt;Map&amp;lt;String,Object&amp;gt;&amp;gt; l= (List&amp;lt;Map&amp;lt;String,Object&amp;gt;&amp;gt;) 
            out.getOut().getBody();
	assertTrue(l.size()&amp;gt;0);
	Map&amp;lt;String,Object&amp;gt; map=l.get(0);
	assertTrue(map.get(&amp;quot;nom&amp;quot;).contains(&amp;quot;toto&amp;quot;);
}
&lt;/pre&gt;



&lt;p&gt;Pour conclure le composant camel-sql réduit la couche DAO (ici à base de jdbc) à presque quelques lignes de configuration.&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.netapsys.fr/index.php/post/2013/02/14/Camel-SQL-composant%3A-Composant-Camel-SQL#comment-form</comments>
      <wfw:comment>http://blog.netapsys.fr/index.php/post/2013/02/14/Camel-SQL-composant%3A-Composant-Camel-SQL#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.netapsys.fr/index.php/feed/atom/comments/564</wfw:commentRss>
      </item>
    
  <item>
    <title>Améliorez les performances: Parallel processing with Camel &amp; Spring (Partie 1/2)</title>
    <link>http://blog.netapsys.fr/index.php/post/2013/01/23/Am%C3%A9liorez-les-performances%3A-Parallel-processing-with-Camel-Spring-ou-ex%C3%A9cution-en-parall%C3%A8le-avec-Camel-Spring.</link>
    <guid isPermaLink="false">urn:md5:b81981e3f666ca2c05441e89333732f0</guid>
    <pubDate>Mon, 04 Feb 2013 17:57:00 +0100</pubDate>
    <dc:creator>Abderrazek CHINE</dc:creator>
        <category>Java J2EE</category>
        <category>@Configuration</category><category>Camel</category><category>camel spring</category><category>Camel-bindy</category><category>camel-spring</category><category>CSV to Java</category><category>Executors</category><category>jUnit</category><category>maven</category><category>parallel</category><category>parallel processing</category><category>parallel processing with camel</category><category>TaskExecutor</category><category>tests unitaires</category><category>Thread</category><category>ThreadPool</category>    
    <description>&lt;p&gt;Ce billet s'inscrit dans la catégorie &lt;em&gt;optimisation des performances&lt;/em&gt;.&lt;/p&gt;


&lt;p&gt;Il se fixe comme objectif d'illustrer avec démo détaillée comment améliorer &lt;strong&gt;significativement&lt;/strong&gt; les performances de nos projets.&lt;/p&gt;


&lt;p&gt;Et cela sans se lancer dans l'aventure d'écrire des kilomètres de code lourd de gestion du 'parallel processing' car nous allons laisser la puissance du framework Camel opérer.&lt;/p&gt;


&lt;p&gt;Sachez néanmoins que de nombreux problèmes de mauvaise performance peuvent être traités sans recours aux implémentations complexes de parallélisme.&lt;/p&gt;


&lt;p&gt;Voici deux situations où il faudrait réfléchir sur l'apport du parallélisme&amp;nbsp;:&lt;/p&gt;


&lt;p&gt;- Si les tâches ou opérations ne prennent que très peu de temps (échelle quelque sec). La gestion/monitoring des threads devient coûteux.&lt;/p&gt;


&lt;p&gt;- Si les ressources mémoire, CPU et IO sont déjà fortement utilisées; sachez que le parallélisme consomme davantage de ressources.&lt;/p&gt;


&lt;p&gt;Le billet veut s'inspirer (&lt;strong&gt;librement&lt;/strong&gt;) des deux étapes décrites dans la démo du chapitre 10 du livre &quot;Camel in action&quot;.&lt;/p&gt;


&lt;p&gt;L'essentiel du code source est fourni &lt;a href=&quot;http://code.google.com/p/camelinaction/source/browse/trunk/?r=338#trunk%2Fchapter10%2Fbigfile%2Fsrc%2Ftest%2Fjava%2Fcamelinaction&quot;&gt;ici&lt;/a&gt;.&lt;/p&gt;


&lt;p&gt;Sachez néanmoins que le code présenté ci-après a été &lt;strong&gt;largement&lt;/strong&gt; adapté et pour certaines parties &lt;strong&gt;complètement&lt;/strong&gt; réécrits.&lt;/p&gt;


&lt;p&gt;En effet, comme j'utilise Spring, toutes les parties de code sont adaptées ou réécrites en fonction.&lt;/p&gt;


&lt;p&gt;En particulier j'utilise l'implémentation spring ThreadPoolTaskExecutor de l'interface TaskExecutor à la place de la méthode statique de Executors de java.&lt;/p&gt;


&lt;p&gt;Le billet est organisée en deux parties:&lt;/p&gt;


&lt;p&gt;- La première partie permet d'exécuter en séquentiel notre démo,&lt;/p&gt;


&lt;p&gt;- La seconde permet adapte la première partie pour une exécution parallèle.&lt;/p&gt;


&lt;p&gt;Nous donnons quelques indications sur le temps d'exécution de chacune des parties.&lt;/p&gt;


&lt;p&gt;Avec les bons paramètres nous pouvons &lt;strong&gt;diviser par quatre&lt;/strong&gt; le temps d'exécution voire plus.&lt;/p&gt;


&lt;p&gt;Le framework Camel offre avec son java DSL des fonctions permettant d'obtenir simplement de meilleurs performances en parallélisant les tâches.&lt;/p&gt;


&lt;p&gt;Comme d'habitude nous le combinons avec le framework Spring v3+ car peut-on faire autrement?&lt;/p&gt;


&lt;p&gt;Dans le domaine de performance, il est utile d'identifier les deux limites: CPU-bound et IO-bound.&lt;/p&gt;


&lt;p&gt;Distinguer ces deux limites permet d'identifier le bon choix pour améliorer les performances.&lt;/p&gt;


&lt;p&gt;En guise de conclusion, dans le domaine d'exécution parallèle, Camel simplifie grandement la vie des développeurs.&lt;/p&gt;


&lt;p&gt;Néanmoins, il leur reste la responsabilité de la cohésion des données.&lt;/p&gt;


&lt;p&gt;Passons à la mise en pratique.&lt;/p&gt;    &lt;p&gt;Nous allons partir d'un projet maven préconfiguré que nous devons compléter pas à pas.&lt;/p&gt;


&lt;p&gt;La première partie de ce billet est de montrer le fonctionnement de la démo en &lt;strong&gt;séquentiel&lt;/strong&gt;.&lt;/p&gt;


&lt;p&gt;La seconde partie introduit le &lt;strong&gt;parallélisme&lt;/strong&gt;.&lt;/p&gt;


&lt;h2&gt;USE-CASE / CAS D'ETUDE&lt;/h2&gt;


&lt;p&gt;Le cas d'étude consiste à construire un ensemble d'instances java (bean du model nommé Tache) depuis des lignes d'un fichier CSV.&lt;/p&gt;


&lt;p&gt;Nous le réalisons en exécution sequentielle dans la première partie puis en parallèle dans la seconde en considérant le cas où le fichier csv est trop gros.&lt;/p&gt;


&lt;p&gt;Le diagramme de classe suivant est utile pour les deux parties (vous pouvez aggrandir l'image):&lt;/p&gt;


&lt;p&gt;&lt;img src=&quot;http://blog.netapsys.fr/public/ach/camel/diagr_class_parallelCamel.PNG&quot; alt=&quot;diagr_class_parallelCamel.PNG&quot; /&gt;&lt;/p&gt;



&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h2&gt;PREMIERE PARTIE&lt;/h2&gt;


&lt;p&gt;&lt;strong&gt;ETAPE 1&lt;/strong&gt;. &lt;strong&gt;CREATION DU PROJET MAVEN PRECONFIGURE POUR CAMEL&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Dans le répertoire workspace, créer un projet maven en mode intéractif avec la commande suivante (en une seule ligne):&lt;/p&gt;
&lt;pre&gt;
mvn archetype:generate -Dfilter=camel-archetype-java 
   -DarchetypeVersion=2.10.3
&lt;/pre&gt;


&lt;p&gt;Répondre à la première question en acceptant la valeur par défaut.&lt;/p&gt;


&lt;p&gt;Saisir le nom de votre package pour la seconde question. Noter que ce nom sera utilisé plus loin.&lt;/p&gt;


&lt;p&gt;Entrer le nom de votre projet en réponse à la troisième question sur artifactId.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;ETAPE 2&lt;/strong&gt;. &lt;strong&gt;IMPORTER LE PROJET DANS ECLIPSE&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Cette étape est optionnelle car si vous utilisez l'IDE STS de Spring, vous pourriez importer votre projet maven directement dans STS.&lt;/p&gt;


&lt;p&gt;Pour contruire le projet pour eclipse, lancer cette commande:&lt;/p&gt;
&lt;pre&gt;
mvn eclipse:eclipse
&lt;/pre&gt;


&lt;p&gt;Ensuite, importer le projet dans eclipse.&lt;/p&gt;


&lt;p&gt;&lt;strong&gt;ETAPE 3&lt;/strong&gt;. &lt;strong&gt;AJOUTER DES DEPENDANCES DANS LE POM&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Nous complétons le pom.xml avec cette dépendance:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;!-- Camel-spring Component --&amp;gt;
	&amp;lt;dependency&amp;gt;
		&amp;lt;groupId&amp;gt;org.apache.camel&amp;lt;/groupId&amp;gt;
		&amp;lt;artifactId&amp;gt;camel-spring&amp;lt;/artifactId&amp;gt;
		&amp;lt;version&amp;gt;2.10.3&amp;lt;/version&amp;gt;
		&amp;lt;scope&amp;gt;compile&amp;lt;/scope&amp;gt;
	&amp;lt;/dependency&amp;gt;
&lt;/pre&gt;


&lt;p&gt;&lt;ins&gt;NOTE&lt;/ins&gt;. Le contenu complet du pom sera fourni en annexe.&lt;/p&gt;


&lt;p&gt;&lt;strong&gt;ETAPE 4&lt;/strong&gt;. &lt;strong&gt;CONFIGURER LE PLUGIN CAMEL&lt;/strong&gt;&lt;/p&gt;

&lt;pre&gt;
&amp;lt;!-- run the route via 'mvn camel:run' --&amp;gt;
	&amp;lt;plugin&amp;gt;
		&amp;lt;groupId&amp;gt;org.apache.camel&amp;lt;/groupId&amp;gt;
		&amp;lt;artifactId&amp;gt;camel-maven-plugin&amp;lt;/artifactId&amp;gt;
		&amp;lt;version&amp;gt;2.10.3&amp;lt;/version&amp;gt;
	&amp;lt;/plugin&amp;gt;
&lt;/pre&gt;


&lt;p&gt;&lt;br /&gt;
&lt;strong&gt;ETAPE 5&lt;/strong&gt;. &lt;strong&gt;AJOUTER LE FICHIER DE CONFIGURATION DE SPRING&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Ajouter le fichier camel-context.xml  dans src/main/resources/META-INF/spring (si besoin créer/ajouter les répertoires manquants).&lt;/p&gt;


&lt;p&gt;Il ne contient que la déclaration du contexte camel ainsi que l'unique route existante:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;
&amp;lt;!-- Configures the Camel Context --&amp;gt;
&amp;lt;beans xmlns=&amp;quot;http://www.springframework.org/schema/beans&amp;quot;
    xmlns:xsi=&amp;quot;http://www.w3.org/2001/XMLSchema-instance&amp;quot; 
    xmlns:camel=&amp;quot;http://camel.apache.org/schema/spring&amp;quot;
    xsi:schemaLocation=&amp;quot;
    http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://camel.apache.org/schema/spring 
    http://camel.apache.org/schema/spring/camel-spring.xsd&amp;quot;&amp;gt;
     &amp;lt;!-- camel context --&amp;gt;
      &amp;lt;camel:camelContext 
		xmlns=&amp;quot;http://camel.apache.org/schema/spring&amp;quot;&amp;gt;
	&amp;lt;camel:routeBuilder ref=&amp;quot;myRouteBuilder&amp;quot;/&amp;gt;
	&amp;lt;/camel:camelContext&amp;gt;
&amp;lt;/beans&amp;gt;
&lt;/pre&gt;


&lt;p&gt;Vous devez adapter son contenu en fonction de votre contexte et le nom de package déclaré au moment de créer le projet maven.&lt;/p&gt;


&lt;p&gt;Vous pouvez tester la construction de votre projet maven avec la commande sur la console:&lt;/p&gt;

&lt;pre&gt;
mvn camel:run
&lt;/pre&gt;


&lt;p&gt;Pour quitter le programme tapez CTRL-C.&lt;/p&gt;


&lt;p&gt;Si vous jetez un oeil sur le dossier target vous verrez un sous-dossier nommé messages contenant le contenu filtré des fichiers xml du répertoire src/data.&lt;/p&gt;


&lt;p&gt;Ok, or tout cela n'est pas notre sujet aujourd'hui mais nous avons maintenant un projet maven préconfiguré pour camel.&lt;/p&gt;


&lt;p&gt;&lt;ins&gt;NOTE IMPORTANTE&lt;/ins&gt;&lt;/p&gt;


&lt;p&gt;Pour la suite, les deux classes java MainApp et MyRouteBuilder ne seront plus utiles.&lt;/p&gt;


&lt;p&gt;Vous pouvez les supprimer mais n'oubliez de retirer toute référence sur MainApp dans les plugins du pom.xml.&lt;/p&gt;


&lt;p&gt;Il est temps de revenir à notre sujet.&lt;/p&gt;


&lt;p&gt;Commençons par écrire un bean model nommé &lt;em&gt;Inventory&lt;/em&gt; annoté avec les annotations du composant camel-bindy&lt;/p&gt;



&lt;p&gt;&lt;br /&gt;
&lt;strong&gt;ETAPE 6&lt;/strong&gt;. &lt;strong&gt;ECRIRE LE BEAN MODEL ANNOTE&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Voici donc le code source du bean model, de l'interface et de sa classe d'implémentation:&lt;/p&gt;


&lt;p&gt;Le bean model est Inventory annoté avec les annotations du composant &lt;a href=&quot;http://blog.netapsys.fr/admin/post.php?id=536&quot;&gt;camel-bindy&lt;/a&gt;:&lt;/p&gt;

&lt;pre&gt;
package fr.netapsys.blogs.camel.beans;
import java.io.Serializable;
import org.apache.camel.dataformat.bindy.annotation.CsvRecord;
import org.apache.camel.dataformat.bindy.annotation.DataField;
@CsvRecord(separator = &amp;quot;,&amp;quot;, skipFirstLine = true)
public class Inventory implements Serializable {
	private static final long serialVersionUID = 1L;
	@DataField(pos = 1)
	private String supplierId;
	@DataField(pos = 2)
	private String partId;
	@DataField(pos = 3)
	private String name;
	@DataField(pos = 4)
	private String amount;
	public Inventory(String supplierId, String partId,
                                  String name,String amount) {
		this.supplierId = supplierId;
		this.partId = partId;
		this.name = name;
		this.amount = amount;
	}
//... omis les getters/setters et toString
}
&lt;/pre&gt;


&lt;p&gt;Ce bean utilise le composant camel-bindy qui sera ajouté dans dépendances plus loin.&lt;/p&gt;


&lt;p&gt;Il est annoté pour  être mappé avec les lignes du fichier &lt;strong&gt;inventory.csv&lt;/strong&gt;.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;ETAPE 8&lt;/strong&gt;. &lt;strong&gt;FICHIER CSV&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Voici un exemple du fichier csv:&lt;/p&gt;

&lt;pre&gt;
supplierId,partId,name,amount
1,2,toto,123.90
2,2,titi,89000.45
&lt;/pre&gt;


&lt;p&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;strong&gt;ETAPE 9&lt;/strong&gt;.  &lt;strong&gt;DESSINER UNE (BELLE) ROUTE CAMEL&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Comme promis, nous nous inspirons du cas d'étude du chapitre 10 du livre &lt;em&gt;Camel in action&lt;/em&gt;.&lt;/p&gt;


&lt;p&gt;Comme vous pouvez constater, nous l'avons complètement adapté pour écrire encore moins de code.&lt;/p&gt;


&lt;p&gt;A titre d'exemple, la transformation des lignes CSV en un POJO est désormais géré par le composant &lt;a href=&quot;http://blog.netapsys.fr/admin/post.php?id=536&quot;&gt;camel-bindy&lt;/a&gt;.&lt;/p&gt;


&lt;p&gt;A l'aide de camel java DSL, nous dessinons notre route dans la classe MyRoute comme suit:&lt;/p&gt;

&lt;pre&gt;
package fr.netapsys.blogs.camel.routes;
import org.apache.camel.LoggingLevel;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.dataformat.bindy.csv.BindyCsvDataFormat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import fr.netapsys.blogs.camel.impl.InventoryServiceImpl;
import fr.netapsys.blogs.camel.processors.MyProcessor;
@Component
public class MyRoute extends RouteBuilder {
 @Autowired 
 private MyProcessor myProcessor;
 
 public void configure() throws Exception {
  from(&amp;quot;file:src/data/?fileName=inventory.csv&amp;amp;noop=true&amp;quot;)
   .log(LoggingLevel.INFO,&amp;quot;MyRoute&amp;quot;,
    &amp;quot;Starting process inventory.csv at ${date:now:dd/MM/yy HH:mm:ss}&amp;quot;)
   .unmarshal(new BindyCsvDataFormat(&amp;quot;fr.netapsys.blogs.camel.beans&amp;quot;))
   .process(myProcessor)
   .split(body())
   .streaming()
   .bean(InventoryServiceImpl.class, &amp;quot;updateInventory&amp;quot;)
  .end()
  .log(LoggingLevel.INFO,&amp;quot;MyRoute&amp;quot;,
     &amp;quot;Done processing file at ${date:now:dd/MM/yy HH:mm:ss}.&amp;quot;);	
  }	
}
&lt;/pre&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;&lt;strong&gt;Explications&lt;/strong&gt;:&lt;/p&gt;


&lt;p&gt;La route de camel commence par lire le fichier &lt;em&gt;inventory.csv&lt;/em&gt; défini via l'argument &lt;em&gt;fileName&lt;/em&gt; dans le répertoire src/data.&lt;/p&gt;


&lt;p&gt;Le second argument &lt;em&gt;noop=true&lt;/em&gt; indique à camel de ne pas déplacer le fichier après son traitement. Sinon, par défaut, camel déplace le fichier dans le répertoire caché &lt;em&gt;.camel&lt;/em&gt;.&lt;/p&gt;


&lt;p&gt;La fonction &lt;em&gt;&lt;/em&gt;unmarshal&lt;em&gt;&lt;/em&gt; transforme le contenu du fichier csv en une map d'instances &lt;em&gt;Inventory&lt;/em&gt;.&lt;/p&gt;


&lt;p&gt;Puis le processor récupère la liste des instances Inventory et définit le body de l&lt;em&gt;'exchange&lt;/em&gt;.&lt;/p&gt;


&lt;p&gt;Le code de la classe MyProcessor est donné ci-après.&lt;/p&gt;


&lt;p&gt;Le &lt;em&gt;split(body())&lt;/em&gt; décompose à juste titre la liste d'objets en une instance Inventory.&lt;/p&gt;


&lt;p&gt;Enfin, c'est la méthode &lt;em&gt;updateInventory&lt;/em&gt; qui opère sur chaucne des instances de la liste.&lt;/p&gt;


&lt;p&gt;Pour finir, une simple trace log termine la route camel.&lt;/p&gt;


&lt;p&gt;Le service &lt;em&gt;InventoryServiceImpl&lt;/em&gt; doit implémenter la seule méthode &lt;em&gt;updateInventory&lt;/em&gt;.&lt;/p&gt;


&lt;p&gt;L'étape suivante présente les détails d'implémentation.&lt;/p&gt;


&lt;p&gt;Mais avant cela, voici le code du processor &lt;em&gt;Myprocessor&lt;/em&gt;:&lt;/p&gt;

&lt;pre&gt;
package fr.netapsys.blogs.camel.processors;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.springframework.stereotype.Component;
import fr.netapsys.blogs.camel.beans.Inventory;

@Component
public class MyProcessor implements Processor{
  @Override
  public void process(Exchange exchange) throws Exception {
   List&amp;lt;Map&amp;lt;String, Inventory&amp;gt;&amp;gt; listMapInv=
                 exchange.getIn().getBody(List.class);
   List&amp;lt;Inventory&amp;gt; listInv=new ArrayList&amp;lt;&amp;gt;();	
   for(Map&amp;lt;String,Inventory&amp;gt; map:listMapInv){ 
     listInv.add(
        (Inventory)map.get(&amp;quot;fr.netapsys.blogs.camel.beans.Inventory&amp;quot;));
   }
   exchange.getIn().setBody(listInv);
 }
}
&lt;/pre&gt;


&lt;p&gt;Cette classe implémente le contrat Processor de camel d'où la définition de la méthode &lt;em&gt;process()&lt;/em&gt;.&lt;/p&gt;


&lt;p&gt;Dans &lt;em&gt;process()&lt;/em&gt;, nous récupérons les instances &lt;em&gt;Inventory&lt;/em&gt; à partir du fichier csv dans une liste, puis le body de l'exchange est défini avec cette liste.&lt;/p&gt;


&lt;p&gt;&lt;br /&gt;
&lt;strong&gt;ETAPE 10&lt;/strong&gt;. &lt;strong&gt;ECRIRE LE SERVICE METIER&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Contrairement à l'exemple du chapitre 10 du livre &quot;Camel in action&quot;, notre interface est mono car il déclare une seule méthode.&lt;/p&gt;

&lt;pre&gt;
package fr.netapsys.blogs.camel.interfaces;
import fr.netapsys.blogs.camel.beans.Inventory;
public interface InventoryService {
  void updateInventory(final Inventory inv) throws Exception;
}
&lt;/pre&gt;


&lt;p&gt;Son implémentation est:&lt;/p&gt;

&lt;pre&gt;
package fr.netapsys.blogs.camel.impl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import fr.netapsys.blogs.camel.beans.Inventory;
import fr.netapsys.blogs.camel.interfaces.InventoryService;
public class InventoryServiceImpl implements InventoryService {
  @Override
  public void updateInventory(Inventory inventory) throws Exception {
     // simulate updating using some CPU processing
     Thread.sleep(100);
  }
}
&lt;/pre&gt;



&lt;p&gt;&lt;br /&gt;
&lt;strong&gt;ETAPE 11&lt;/strong&gt;. &lt;strong&gt;AJOUTER LES DEPENDANCES DANS LE POM&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Ajouter la dépendance sur le composant camel-bindy:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;dependency&amp;gt;
  &amp;lt;groupId&amp;gt;org.apache.camel&amp;lt;/groupId&amp;gt;
  &amp;lt;artifactId&amp;gt;camel-bindy&amp;lt;/artifactId&amp;gt;
  &amp;lt;version&amp;gt;2.10.3&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/pre&gt;


&lt;p&gt;&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;&lt;strong&gt;ETAPE 12&lt;/strong&gt;.&lt;strong&gt;TESTER L'EXECUTION SEQUENTIELLE&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;&lt;br /&gt;
Voici une première classe abstraite pour faciliter l'écriture des tests JUnit.&lt;/p&gt;

&lt;pre&gt;
package fr.netapsys.blogs.camel.demo.tests;
import org.apache.camel.CamelContext;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.DirtiesContext.ClassMode;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({&amp;quot;classpath:META-INF/spring/camel-context.xml&amp;quot;})
@DirtiesContext(classMode = ClassMode.AFTER_CLASS)
public abstract class TestParent  {
	@Autowired protected ApplicationContext sprinCtx;
	@Autowired protected CamelContext camelCtx;	
}
&lt;/pre&gt;


&lt;p&gt;&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;Nous écrivons un premier test JUnit comme suit:&lt;/p&gt;

&lt;pre&gt;
package fr.netapsys.blogs.camel.demo.tests;
import org.junit.Test;
public class TestSequentielRouteTest extends TestParent {	
	@Test
	public void testSeqFile() throws InterruptedException{
		Thread.sleep(3000);
	}
}
&lt;/pre&gt;

&lt;p&gt;&lt;br /&gt;
Comme vous pouvez le constater nous avons là aussi simplifier l'écriture du test par rapport à celui du chapitre 10 du livre &quot;Camel in action&quot;.&lt;/p&gt;


&lt;p&gt;La classe test JUnit hérite de la classe abstraite TestParent qui se charge de démarrer le contexte de spring.&lt;/p&gt;


&lt;p&gt;Ainsi, la route camel est automatiquement lancée par le contexte camel démarré par Spring.&lt;/p&gt;


&lt;p&gt;&lt;br /&gt;&lt;/p&gt;

&lt;h2&gt;&lt;strong&gt;SECONDE PARTIE&lt;/strong&gt;: EXECUTION PARALLELE&lt;/h2&gt;



&lt;p&gt;Voici les étapes que nous développerons dans la deuxième partie de ce billet.&lt;/p&gt;


&lt;p&gt;&lt;br /&gt;
&lt;strong&gt;ETAPE 13&lt;/strong&gt;. &lt;strong&gt;REDESSINER LA ROUTE DE CAMEL&lt;/strong&gt;
&lt;br /&gt;
&lt;strong&gt;ETAPE 14&lt;/strong&gt;. &lt;strong&gt;DECLARER LA ROUTE DANS SPRING&lt;/strong&gt;
&lt;br /&gt;
&lt;strong&gt;ETAPE 15&lt;/strong&gt;. &lt;strong&gt;CONFIGURER L'EXECUTOR DE SPRING&lt;/strong&gt;
&lt;br /&gt;
&lt;strong&gt;ETAPE 16&lt;/strong&gt;. &lt;strong&gt;TESTER L'EXECUTION PARALLELE&lt;/strong&gt;
&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;A très bientôt.&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.netapsys.fr/index.php/post/2013/01/23/Am%C3%A9liorez-les-performances%3A-Parallel-processing-with-Camel-Spring-ou-ex%C3%A9cution-en-parall%C3%A8le-avec-Camel-Spring.#comment-form</comments>
      <wfw:comment>http://blog.netapsys.fr/index.php/post/2013/01/23/Am%C3%A9liorez-les-performances%3A-Parallel-processing-with-Camel-Spring-ou-ex%C3%A9cution-en-parall%C3%A8le-avec-Camel-Spring.#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.netapsys.fr/index.php/feed/atom/comments/549</wfw:commentRss>
      </item>
    
  <item>
    <title>Spring parallel processing. Exécution parallèle avec Spring</title>
    <link>http://blog.netapsys.fr/index.php/post/2013/01/27/spring-parallel-processing-%3A-Ex%C3%A9cution-parall%C3%A8le-avec-spring</link>
    <guid isPermaLink="false">urn:md5:b50c858e809c8be3ec8fe5e9bb1f774c</guid>
    <pubDate>Mon, 28 Jan 2013 17:56:00 +0100</pubDate>
    <dc:creator>Abderrazek CHINE</dc:creator>
        <category>Java J2EE</category>
        <category>@Configuration</category><category>callback</category><category>Exec</category><category>Executors</category><category>Junit4</category><category>Parallel processing</category><category>Parallèl</category><category>Runnable</category><category>TaskExecutor</category><category>tests unitaires</category><category>Thread</category><category>ThreadPool</category>    
    <description>&lt;p&gt;Ce billet tente de démystifier l'utilisation en java5+ des exécutions en parallèle des tâches (parallel processing) en s'appuyant sur les classes de Spring qui masquent les difficultés du parallélisme en java.&lt;/p&gt;


&lt;p&gt;Les machines de nos jours viennent avec plusieurs processeurs: des multi-core (quatre, huit ou plusieurs centaines cœurs).&lt;/p&gt;


&lt;p&gt;Or la puissance de ces ressources n'est pas constamment utilisée.&lt;/p&gt;


&lt;p&gt;L'exécution parallèle autorise des calculs parallèles qui consomment ces ressources pour améliorer les performances.&lt;/p&gt;


&lt;p&gt;Mais c’est comme tout, le parallélisme ça se mérite. Même si c'est un petit chouia difficile!&lt;/p&gt;


&lt;p&gt;En réalité, la difficulté n'est pas dans la mise en place technique mais réside dans la gestion de la cohésion des données.&lt;/p&gt;


&lt;p&gt;Techniquement, c'est le développeur qui doit réaliser les calculs parallèles.&lt;/p&gt;


&lt;p&gt;C'est sûr, demain apparaîtront côté JVM les méthodes de traitement du parallélisme comme c'était le cas de la gestion mémoire avec le garbage collector ou récemment la gestion des descripteurs de fichier avec try/catch en java7.&lt;/p&gt;


&lt;p&gt;Java 8+ nous proposera (peut-être) de faire le calcul paralléle à notre place (nous développeurs).&lt;/p&gt;


&lt;p&gt;Ce qui reste au développeur c'est de décrire les traitements atomiques (comme l'atomicité dans les transactions), et à partir de là c'est la JVM qui mène le traitement global parallélisé efficacement en fonction des ressources disponibles.&lt;/p&gt;


&lt;p&gt;Bel enjeu de demain.&lt;/p&gt;


&lt;p&gt;Signalons que nous n'utilisons pas directement la classe Executors de java qui offre des méthodes statiques pour instancier un Executor ou un ExecutorService.&lt;/p&gt;


&lt;p&gt;A la place nous utiliserons les classes de Spring.&lt;/p&gt;


&lt;p&gt;La démo ci-après est faite en java7 et spring 3.&lt;/p&gt;


&lt;p&gt;Passons à la mise en pratique.&lt;/p&gt;    &lt;p&gt;Voici les étapes pour concevoir notre démonstration simple.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;ETAPE 1&lt;/strong&gt;. ECRIRE UN BEAN MODEL&lt;/p&gt;


&lt;p&gt;La classe Tache décrit le model de notre simple démo:&lt;/p&gt;

&lt;pre&gt;
package fr.netapsys.executorservice.beans;

import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Tache implements Serializable {

	private final static transient SimpleDateFormat sdf=
                 new SimpleDateFormat(&amp;quot;dd/MM/yy HH:mm:ss&amp;quot;);
	private static final long serialVersionUID = 1L;

	private String libelle, strDate;
	
	public Tache(){
		super();
	}
	
	public Tache(final String libelle,final String strDate){
		setLibelle(libelle);
		setStrDate(strDate);
	}
	

	public Tache(final String libelle,final Date date){
		setLibelle(libelle);
		setStrDate(sdf.format(date));
	}
	//.... omis les getters/setters et toString
}

&lt;/pre&gt;



&lt;p&gt;&lt;br /&gt;
&lt;strong&gt;ETAPE 2&lt;/strong&gt;. AJOUTER LES DEPENDANCES&lt;/p&gt;


&lt;p&gt;Seules les dépendances de Spring sont ajoutés, le pom.xml est donné en annexe.&lt;/p&gt;


&lt;p&gt;&lt;br /&gt;
&lt;strong&gt;ETAPE 3&lt;/strong&gt;. CONFIGURER SPRING&lt;/p&gt;


&lt;p&gt;Nous retenons la configuration java-centric dans cette démo.&lt;/p&gt;


&lt;p&gt;La classe chargée de configurer Spring est JavaConfigurator:&lt;/p&gt;

&lt;pre&gt;
package fr.netapsys.executorservice.javacfg;
import java.util.concurrent.ExecutorService;
import org.springframework.context.annotation.*;
import org.springframework.core.task.TaskExecutor;
import org.springframework.core.task.support.ExecutorServiceAdapter;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@Configuration
public class JavaConfigurator {
    protected static int nbThreadPool=5;    
    /***
     * Use l'implementation spring 
       ThreadPoolTaskScheduler de l'interface TaskExecutor
    */
    @Bean
    public TaskExecutor taskExecutor() {
       ThreadPoolTaskExecutor tpte = new ThreadPoolTaskExecutor(); 
       tpte.setCorePoolSize(nbThreadPool);
       tpte.setMaxPoolSize(nbThreadPool*2);
       tpte.setQueueCapacity(nbThreadPool*10);
       return tpte;
    }
    /* Use l'adapter de spring ExecutorServiceAdapter pour 
         retourner une instance ExecutorService
         au lieu de Executors.newFixedThreadPool de java
    */
    @Bean
    public ExecutorService executorService() {
      return new ExecutorServiceAdapter(taskExecutor()); 
    }
}
&lt;/pre&gt;



&lt;p&gt;Explications:&lt;/p&gt;


&lt;p&gt;C'est ici que la configuration du contexte de spring est regroupée.&lt;/p&gt;


&lt;p&gt;Une instance de TaskExecutor est définie: précisément c'est l'implémentation ThreadPoolTaskExecutor de spring retenue.&lt;/p&gt;


&lt;p&gt;L'instance ThreadPoolTaskExecutor est configurée ainsi:&lt;/p&gt;


&lt;p&gt;- corePoolSize initialisé à 5. Le nombre de thread lancés simultanément,&lt;/p&gt;


&lt;p&gt;- maxPoolSize le nombre maximal de thread lancés en parallèle. Au dela il sera mis en pool,&lt;/p&gt;


&lt;p&gt;- queueCapacity la taille max du pool. Au délà une exception de rejet est levée (à moins de configuration particulière).&lt;/p&gt;


&lt;p&gt;Nous avons aussi défini une instance de ExecutorService qui autorise de lancer un objet Runnable ou Callable.&lt;/p&gt;


&lt;p&gt;Nous y reviendrons une autre fois sur les Callable.&lt;/p&gt;


&lt;p&gt;&lt;em&gt;&lt;ins&gt;NOTES&lt;/ins&gt;&lt;/em&gt;.&lt;/p&gt;


&lt;p&gt;- Les commentaires dans le code complètent ces explications,&lt;/p&gt;


&lt;p&gt;- C'est spring qui se charge d'appeler la méthode shutdown sur ExecutorService donc il n'est pas autorisé de l'appeler manuellement.&lt;/p&gt;



&lt;p&gt;&lt;br /&gt;
&lt;strong&gt;ETAPE 4&lt;/strong&gt;. ECRIRE UNE CLASSE RUNNABLE (THREAD)&lt;/p&gt;


&lt;p&gt;La classe MyThread implémente l'interface &lt;em&gt;Runnable&lt;/em&gt;:&lt;/p&gt;

&lt;pre&gt;
package fr.netapsys.executorservice;
import java.util.Date;
import java.util.concurrent.atomic.AtomicInteger;
import org.slf4j.*;
import fr.netapsys.executorservice.beans.Tache;

public class MyThread implements Runnable {
    private AtomicInteger num;
    private Logger logger=LoggerFactory.getLogger(&amp;quot;MyThread&amp;quot;);
    public MyThread(){super();    }
    public MyThread(AtomicInteger ai){
       this(); 
       this.num=ai;
    }
    @Override
    public void run() {
       logger.info(Thread.currentThread().getName()+&amp;quot; demarrage:&amp;quot;+num);
       process(); /*simulate long process*/
       logger.info(Thread.currentThread().getName()+&amp;quot; termine.&amp;quot;);
    }
    private void process() {
        Tache tache=new Tache(&amp;quot;libel&amp;quot;+num, new Date());	
    	logger.info(&amp;quot;processing tache:&amp;quot;+tache);
    }
}
&lt;/pre&gt;


&lt;p&gt;&lt;br /&gt;
Dans la classe MyThread qui redéfinit la méthode &lt;em&gt;run&lt;/em&gt; qui simule un long processus.&lt;/p&gt;


&lt;p&gt;Dans notre cas, une tache est créée puis logguée. Je n'utilise pas de Thread.sleep afin de ne pas masquer les difficulltés liées à l'exécution parallèle.&lt;/p&gt;


&lt;p&gt;Il serait utile de surcharger la méthode toString() du bean Tache pour un affichage clair.&lt;/p&gt;


&lt;p&gt;&lt;br /&gt;
&lt;strong&gt;ETAPE 5&lt;/strong&gt;. Tester avec JUnit&lt;/p&gt;


&lt;p&gt;Avec ce test nous mettons en pratique l'exécution parallèle en appelant la méthode &lt;em&gt;execute&lt;/em&gt; de
l'instance de TaskExecutor. Ce test hérite de la classe abstrainte TestParent donné ci-après.&lt;/p&gt;

&lt;pre&gt;
package fr.netapsys.executorservice.tests;

import static org.junit.Assert.assertNotNull;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.Test;
import org.slf4j.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.task.TaskExecutor;
import fr.netapsys.executorservice.MyThread;

public class TestSpringTaskExecutor extends TestParent {
	private Logger logger=LoggerFactory.getLogger(this.getClass());
	@Autowired TaskExecutor threadPoolTaskExecutor;
	@Test
	public void testSpringTaskExecutor() 
                             throws InterruptedException  {
		assertNotNull(threadPoolTaskExecutor);
		for (int k = 0; k &amp;lt; 5; k++) {
			Runnable myThread = 
                            new MyThread(new AtomicInteger(k));
			threadPoolTaskExecutor.execute(myThread);
		}
		Thread.sleep(500);
		logger.info(&amp;quot;Finished all threads&amp;quot;);
	}	
}
&lt;/pre&gt;



&lt;p&gt;La seule chose à noter est la boucle for qui crée &lt;strong&gt;cinq&lt;/strong&gt; threads.&lt;/p&gt;


&lt;p&gt;Ces threads sont lancés simultanément via la méthode execute de l'instance &lt;em&gt;ThreadPoolTaskExecutor&lt;/em&gt; spring.&lt;/p&gt;


&lt;p&gt;En effet, à chaque itération, un objet &lt;em&gt;Runnable&lt;/em&gt; est passée à la méthode &lt;em&gt;execute&lt;/em&gt;.&lt;/p&gt;


&lt;p&gt;Ainsi, plusieurs tâches sont lancées simultanément dont chacune réalise le traitement dans la méthode &lt;em&gt;run&lt;/em&gt;.&lt;/p&gt;


&lt;p&gt;Dans notre cas, chaque thread initialise une instance du bean Tache puis le loggue.&lt;/p&gt;


&lt;p&gt;La classe &lt;em&gt;abstraite&lt;/em&gt; &lt;strong&gt;TestParent&lt;/strong&gt; permet de charger le contexte spring à partir de JavaConfigurator:&lt;/p&gt;

&lt;pre&gt;
package fr.netapsys.executorservice.tests;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import fr.netapsys.executorservice.javacfg.JavaConfigurator;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes={JavaConfigurator.class})
public abstract class TestParent{
	@Autowired ApplicationContext sprinCtx;
}
&lt;/pre&gt;



&lt;p&gt;Il reste à lancer &lt;strong&gt;mvn test&lt;/strong&gt; et observer les traces logs.&lt;/p&gt;


&lt;p&gt;En fonction de votre environnement, les traces logs ne sont pas forcément ordonnés.&lt;/p&gt;


&lt;p&gt;Dans un proche billet nous ferons tout cela avec le framework &lt;strong&gt;Camel&lt;/strong&gt; ce qui rend la chose plus simple.&lt;/p&gt;


&lt;p&gt;&lt;br /&gt;
&lt;strong&gt;ANNEXE 1&lt;/strong&gt;.&lt;/p&gt;


&lt;p&gt;&lt;br /&gt;
Voici le contenu du &lt;strong&gt;pom.xml&lt;/strong&gt;:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;properties&amp;gt;
	&amp;lt;jdk-version&amp;gt;1.7&amp;lt;/jdk-version&amp;gt;
	&amp;lt;!-- Log --&amp;gt;
	&amp;lt;log4j-version&amp;gt;1.2.17&amp;lt;/log4j-version&amp;gt;
	&amp;lt;slf4j-version&amp;gt;1.6.6&amp;lt;/slf4j-version&amp;gt;
	&amp;lt;!-- Spring --&amp;gt;
	&amp;lt;spring-version&amp;gt;3.1.2.RELEASE&amp;lt;/spring-version&amp;gt;
	&amp;lt;!-- versions dependances de test --&amp;gt;
	&amp;lt;junit-version&amp;gt;4.10&amp;lt;/junit-version&amp;gt;
	&amp;lt;javassist-version&amp;gt;3.12.1.GA&amp;lt;/javassist-version&amp;gt;
	&amp;lt;cglib-version&amp;gt;2.2.2&amp;lt;/cglib-version&amp;gt;
&amp;lt;/properties&amp;gt;
	
&amp;lt;dependencies&amp;gt;
	&amp;lt;!-- 2 dep transitive de JavConfig see @Configuration --&amp;gt;
	&amp;lt;dependency&amp;gt;
		&amp;lt;groupId&amp;gt;javassist&amp;lt;/groupId&amp;gt;
		&amp;lt;artifactId&amp;gt;javassist&amp;lt;/artifactId&amp;gt;
		&amp;lt;version&amp;gt;${javassist-version}&amp;lt;/version&amp;gt;
		&amp;lt;scope&amp;gt;compile&amp;lt;/scope&amp;gt;
	&amp;lt;/dependency&amp;gt;
	&amp;lt;dependency&amp;gt;
		&amp;lt;groupId&amp;gt;cglib&amp;lt;/groupId&amp;gt;
		&amp;lt;artifactId&amp;gt;cglib&amp;lt;/artifactId&amp;gt;
		&amp;lt;version&amp;gt;${cglib-version}&amp;lt;/version&amp;gt;
		&amp;lt;scope&amp;gt;compile&amp;lt;/scope&amp;gt;
	&amp;lt;/dependency&amp;gt;
	&amp;lt;!-- spring-core --&amp;gt;
	&amp;lt;dependency&amp;gt;
		&amp;lt;groupId&amp;gt;org.springframework&amp;lt;/groupId&amp;gt;
		&amp;lt;artifactId&amp;gt;spring-core&amp;lt;/artifactId&amp;gt;
		&amp;lt;version&amp;gt;${spring-version}&amp;lt;/version&amp;gt;
		&amp;lt;scope&amp;gt;compile&amp;lt;/scope&amp;gt;
	&amp;lt;/dependency&amp;gt;

	&amp;lt;!-- spring-expression --&amp;gt;
	&amp;lt;dependency&amp;gt;
		&amp;lt;groupId&amp;gt;org.springframework&amp;lt;/groupId&amp;gt;
		&amp;lt;artifactId&amp;gt;spring-expression&amp;lt;/artifactId&amp;gt;
		&amp;lt;version&amp;gt;${spring-version}&amp;lt;/version&amp;gt;
		&amp;lt;scope&amp;gt;compile&amp;lt;/scope&amp;gt;
	&amp;lt;/dependency&amp;gt;
	&amp;lt;!-- spring-context --&amp;gt;
	&amp;lt;dependency&amp;gt;
		&amp;lt;groupId&amp;gt;org.springframework&amp;lt;/groupId&amp;gt;
		&amp;lt;artifactId&amp;gt;spring-context&amp;lt;/artifactId&amp;gt;
		&amp;lt;version&amp;gt;${spring-version}&amp;lt;/version&amp;gt;
		&amp;lt;scope&amp;gt;compile&amp;lt;/scope&amp;gt;
	&amp;lt;/dependency&amp;gt;
&amp;lt;!-- spring-asm --&amp;gt;
	&amp;lt;dependency&amp;gt;
		&amp;lt;groupId&amp;gt;org.springframework&amp;lt;/groupId&amp;gt;
		&amp;lt;artifactId&amp;gt;spring-asm&amp;lt;/artifactId&amp;gt;
		&amp;lt;version&amp;gt;${spring-version}&amp;lt;/version&amp;gt;
		&amp;lt;scope&amp;gt;compile&amp;lt;/scope&amp;gt;
	&amp;lt;/dependency&amp;gt;
&amp;lt;!-- spring-beans --&amp;gt;
	&amp;lt;dependency&amp;gt;
		&amp;lt;groupId&amp;gt;org.springframework&amp;lt;/groupId&amp;gt;
		&amp;lt;artifactId&amp;gt;spring-beans&amp;lt;/artifactId&amp;gt;
		&amp;lt;version&amp;gt;${spring-version}&amp;lt;/version&amp;gt;
		&amp;lt;scope&amp;gt;compile&amp;lt;/scope&amp;gt;
	&amp;lt;/dependency&amp;gt;
	&amp;lt;!-- spring-context-support --&amp;gt;
	&amp;lt;dependency&amp;gt;
		&amp;lt;groupId&amp;gt;org.springframework&amp;lt;/groupId&amp;gt;
		&amp;lt;artifactId&amp;gt;spring-context-support&amp;lt;/artifactId&amp;gt;
		&amp;lt;version&amp;gt;${spring-version}&amp;lt;/version&amp;gt;
		&amp;lt;scope&amp;gt;compile&amp;lt;/scope&amp;gt;
	&amp;lt;/dependency&amp;gt;
	&amp;lt;!-- logging --&amp;gt;
	&amp;lt;dependency&amp;gt;
		&amp;lt;groupId&amp;gt;org.slf4j&amp;lt;/groupId&amp;gt;
		&amp;lt;artifactId&amp;gt;slf4j-api&amp;lt;/artifactId&amp;gt;
		&amp;lt;version&amp;gt;1.6.6&amp;lt;/version&amp;gt;
	&amp;lt;/dependency&amp;gt;
	&amp;lt;dependency&amp;gt;
	 &amp;lt;groupId&amp;gt;org.slf4j&amp;lt;/groupId&amp;gt;
	 &amp;lt;artifactId&amp;gt;slf4j-log4j12&amp;lt;/artifactId&amp;gt;
	 &amp;lt;version&amp;gt;1.6.6&amp;lt;/version&amp;gt;
	&amp;lt;/dependency&amp;gt;
	&amp;lt;dependency&amp;gt;
	 &amp;lt;groupId&amp;gt;log4j&amp;lt;/groupId&amp;gt;
	 &amp;lt;artifactId&amp;gt;log4j&amp;lt;/artifactId&amp;gt;
	 &amp;lt;version&amp;gt;1.2.17&amp;lt;/version&amp;gt;
	&amp;lt;/dependency&amp;gt;
	&amp;lt;!-- logging --&amp;gt;
	&amp;lt;dependency&amp;gt;
	 &amp;lt;groupId&amp;gt;commons-logging&amp;lt;/groupId&amp;gt;
	 &amp;lt;artifactId&amp;gt;commons-logging&amp;lt;/artifactId&amp;gt;
	 &amp;lt;version&amp;gt;1.1&amp;lt;/version&amp;gt;
	 &amp;lt;scope&amp;gt;runtime&amp;lt;/scope&amp;gt;
	&amp;lt;/dependency&amp;gt;
	&amp;lt;!-- spring-test --&amp;gt;
	&amp;lt;dependency&amp;gt;
	 &amp;lt;groupId&amp;gt;org.springframework&amp;lt;/groupId&amp;gt;
	 &amp;lt;artifactId&amp;gt;spring-test&amp;lt;/artifactId&amp;gt;
	 &amp;lt;version&amp;gt;${spring-version}&amp;lt;/version&amp;gt;
	 &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
	&amp;lt;/dependency&amp;gt;
	&amp;lt;!-- DEPENDANCES DE TESTS junit --&amp;gt;
	&amp;lt;dependency&amp;gt;
	 &amp;lt;groupId&amp;gt;junit&amp;lt;/groupId&amp;gt;
	 &amp;lt;artifactId&amp;gt;junit&amp;lt;/artifactId&amp;gt;
	 &amp;lt;version&amp;gt;${junit-version}&amp;lt;/version&amp;gt;
	 &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
	&amp;lt;/dependency&amp;gt;
&amp;lt;/dependencies&amp;gt;
&amp;lt;build&amp;gt;
	&amp;lt;defaultGoal&amp;gt;install&amp;lt;/defaultGoal&amp;gt;
	&amp;lt;plugins&amp;gt;
	 &amp;lt;plugin&amp;gt;
	  &amp;lt;groupId&amp;gt;org.apache.maven.plugins&amp;lt;/groupId&amp;gt;
		&amp;lt;artifactId&amp;gt;maven-compiler-plugin&amp;lt;/artifactId&amp;gt;
		&amp;lt;version&amp;gt;2.5.1&amp;lt;/version&amp;gt;
		&amp;lt;configuration&amp;gt;
		 &amp;lt;source&amp;gt;1.7&amp;lt;/source&amp;gt;
		 &amp;lt;target&amp;gt;1.7&amp;lt;/target&amp;gt;
		&amp;lt;/configuration&amp;gt;
		&amp;lt;/plugin&amp;gt;
	&amp;lt;/plugins&amp;gt;
&amp;lt;/build&amp;gt;
&lt;/pre&gt;</description>
    
    
    
          <comments>http://blog.netapsys.fr/index.php/post/2013/01/27/spring-parallel-processing-%3A-Ex%C3%A9cution-parall%C3%A8le-avec-spring#comment-form</comments>
      <wfw:comment>http://blog.netapsys.fr/index.php/post/2013/01/27/spring-parallel-processing-%3A-Ex%C3%A9cution-parall%C3%A8le-avec-spring#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.netapsys.fr/index.php/feed/atom/comments/551</wfw:commentRss>
      </item>
    
  <item>
    <title>Composant Camel-Bindy: Comment transformer les données non structurées</title>
    <link>http://blog.netapsys.fr/index.php/post/2012/12/28/Composant-Camel-Bindy%3A-Comment-transformer-les-donn%C3%A9es-non-structur%C3%A9es</link>
    <guid isPermaLink="false">urn:md5:8b23eaea7ba686e897375ec4d8140496</guid>
    <pubDate>Tue, 22 Jan 2013 18:39:00 +0100</pubDate>
    <dc:creator>Abderrazek CHINE</dc:creator>
        <category>Java J2EE</category>
        <category>bindy</category><category>camel</category><category>camel-bindy</category><category>csv</category><category>java</category>    
    <description>&lt;p&gt;On s'intéresse ici au composant 'camel-bindy' qui permet de transformer des données non structurées (csv par exemple) en objets java et inversement.&lt;/p&gt;


&lt;p&gt;La démo est réalisée en quatre actes: configurer le pom du projet, dessiner et déclarer la route, annoter le bean du model et enfin tester.&lt;/p&gt;


&lt;p&gt;la v2.10.2 de camel-bindy est utilisée.
&lt;br /&gt;
&lt;img src=&quot;http://blog.netapsys.fr/public/ach/camel/camel-logo.png&quot; alt=&quot;camel-logo.png&quot; /&gt;&lt;/p&gt;


&lt;p&gt;Voici les détails de la démo.&lt;/p&gt;    &lt;p&gt;La démo du composant bindy est un projet maven réalisé en quatre actes:&lt;/p&gt;



&lt;p&gt;ACTE1. Configurer le pom&lt;/p&gt;

&lt;pre&gt;
&amp;lt;dependency&amp;gt;
  &amp;lt;groupId&amp;gt;org.apache.camel&amp;lt;/groupId&amp;gt;
  &amp;lt;artifactId&amp;gt;camel-bindy&amp;lt;/artifactId&amp;gt;
  &amp;lt;version&amp;gt;2.10.2&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/pre&gt;



&lt;p&gt;ACTE2. Dessiner la route et la déclarer dans spring&lt;/p&gt;

&lt;pre&gt;
package fr.netapsys.camel.routes;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.dataformat.bindy.csv.BindyCsvDataFormat;

public class BindyRoute extends RouteBuilder {
	@Override
	public void configure() throws Exception {
     from(&amp;quot;file://src/?noop=true&amp;amp;fileName=persons.csv&amp;quot;)
	  .unmarshal(new BindyCsvDataFormat(&amp;quot;fr.netapsys.camel.beans&amp;quot;)) 
	  .to(&amp;quot;mock:results.csv&amp;quot;)
	 .end();
	}
}
&lt;/pre&gt;


&lt;p&gt;La route pointe sur un fichier persons.csv localisé dans src.&lt;/p&gt;


&lt;p&gt;La méthode unmarshall se charge de convertir les données en csv via l'argument BindyCsvDataFormat.&lt;/p&gt;


&lt;p&gt;BindyCsvDataFormat prend en argument les noms des packages des classes annotées.&lt;/p&gt;


&lt;p&gt;Elle prend aussi comme argument la classe annotée (voir la javadoc).&lt;/p&gt;


&lt;p&gt;N'oublier pas d'ajouter dans le fichier camel-context.xml une référence sur BindyRoute comme suit:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;camel:camelContext 
	xmlns=&amp;quot;http://camel.apache.org/schema/spring&amp;quot;&amp;gt;
	&amp;lt;camel:routeBuilder ref=&amp;quot;bindyRoute&amp;quot; /&amp;gt;
&amp;lt;/camel:camelContext&amp;gt;
&lt;/pre&gt;


&lt;p&gt;Le principe de  &quot;Convention over configuration&quot; attend que le fichier camel-context.xml dans src/META-INF/spring.&lt;/p&gt;



&lt;p&gt;ACTE3. Annoter le bean du model pour camel-bindy&lt;/p&gt;

&lt;pre&gt;
package fr.netapsys.camel.beans;
import java.io.Serializable;
import org.apache.camel.dataformat.bindy.annotation.CsvRecord;
import org.apache.camel.dataformat.bindy.annotation.DataField;

@CsvRecord(separator = &amp;quot;,&amp;quot;, skipFirstLine=true)
public class Personne implements Serializable {
	@DataField(pos = 1)
	private String nom;
	@DataField(pos = 2)
	private String prenom;
	@DataField(pos = 3)
	private String age;	

        public Personne(){}

	public Personne(String nom,String prenom,String age){
		setNom(nom);
		setPrenom(prenom);
		setAge(age);
	}
//...  getters / setters toString omis...
}
&lt;/pre&gt;


&lt;p&gt;Explications:&lt;/p&gt;


&lt;p&gt;L'annotation @CsvRecord de org.apache.camel.dataformat.bindy précise que ce bean servira à la transformation csv.&lt;/p&gt;


&lt;p&gt;Les deux attributs de cette annotation permettent de définir le séparateur et d'ignorer la première ligne.&lt;/p&gt;


&lt;p&gt;Le reste des annotations de bindy servent à structurer les données.&lt;/p&gt;


&lt;p&gt;Attention, pensez à écrire le constructeur par défaut si vous en créez un ou plusieurs avec arguments.&lt;/p&gt;



&lt;p&gt;ACTE4. Tester&lt;/p&gt;


&lt;p&gt;Vérifions que, dans le répertoire src/, l'existence d'un fichier persons.csv contenant les lignes à transformer en objets java.&lt;/p&gt;


&lt;p&gt;Ces lignes doivent avoir trois colonnes nom, prénom et age comme suit:&lt;/p&gt;

&lt;pre&gt;[persons.csv]
nom,prenom,age
Toto,To,30
Titi,Ti,32
......
&lt;/pre&gt;



&lt;p&gt;Voici le code du test:&lt;/p&gt;

&lt;pre&gt;
package fr.netapsys.camel.tests;

import java.util.*;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.test.junit4.CamelTestSupport;
import org.junit.*;
import fr.netapsys.camel.beans.Personne;
import fr.netapsys.camel.routes.BindyRoute;

public class BindyRouteTest  extends CamelTestSupport {
final String clazzModel=&amp;quot;fr.netapsys.camel.beans.Personne&amp;quot;;
@Override
protected RouteBuilder createRouteBuilder() throws Exception {
	return new BindyRoute();
}

@Test
public void testBindyCamel() throws Exception {
  
      MockEndpoint mock = getMockEndpoint(&amp;quot;mock:results.csv&amp;quot;);
		
      mock.expectedMessageCount(1);
      assertMockEndpointsSatisfied();
		
       List&amp;lt;Map&amp;lt;String, Personne&amp;gt;&amp;gt; listMapPersons = 
               (List&amp;lt;Map&amp;lt;String, Personne&amp;gt;&amp;gt;) mock
		   .getReceivedExchanges().get(0).getIn().getBody();

	Assert.assertTrue(listMapPersons.size()==2);
	Assert.assertTrue(listMapPersons.get(0)
                  .get(&amp;quot;fr.netapsys.camel.beans.Personne&amp;quot;)!=null);
	
 }
}
&lt;/pre&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;Les lignes du fichier persons.csv sont transformées en liste d'instances Personne.&lt;/p&gt;


&lt;p&gt;&lt;br /&gt;
Voilà tout!&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.netapsys.fr/index.php/post/2012/12/28/Composant-Camel-Bindy%3A-Comment-transformer-les-donn%C3%A9es-non-structur%C3%A9es#comment-form</comments>
      <wfw:comment>http://blog.netapsys.fr/index.php/post/2012/12/28/Composant-Camel-Bindy%3A-Comment-transformer-les-donn%C3%A9es-non-structur%C3%A9es#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.netapsys.fr/index.php/feed/atom/comments/536</wfw:commentRss>
      </item>
    
  <item>
    <title>Camel, Spring et tests JUnit pour les nuls (partie 2/3)</title>
    <link>http://blog.netapsys.fr/index.php/post/2012/12/25/Camel%2C-Spring-et-tests-JUnit-pour-les-nuls-%28partie-2/3%29</link>
    <guid isPermaLink="false">urn:md5:293abfe46d40c9c5a216f63c815280d7</guid>
    <pubDate>Mon, 14 Jan 2013 19:39:00 +0100</pubDate>
    <dc:creator>Abderrazek CHINE</dc:creator>
        <category>Java J2EE</category>
        <category>Camel</category><category>cmd</category><category>cmd:exec</category><category>Exec</category><category>execProcessor</category><category>processor</category><category>route</category><category>RouteBuilder</category>    
    <description>&lt;p&gt;Ce billet est la suite de &lt;a href=&quot;http://blog.netapsys.fr/index.php/post/2012/12/09/Camel-et-Spring-pour-les-nuls-avec-d%C3%A9mo-d%C3%A9taill%C3%A9e&quot;&gt;Camel, Spring et tests JUnit pour les nuls (partie 1/3)&lt;/a&gt;.
&lt;img src=&quot;http://blog.netapsys.fr/public/ach/camel/camel-logo.png&quot; alt=&quot;camel-logo.png&quot; style=&quot;display:block; margin:0 auto;&quot; /&gt;&lt;/p&gt;    &lt;p&gt;&lt;br /&gt;
Voici donc la troisième démo avancée.&lt;/p&gt;


&lt;p&gt;&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;&lt;strong&gt;TROISIEME DEMO&lt;/strong&gt;: Exécuter script shell: Niveau avancé&lt;/p&gt;



&lt;p&gt;L'objet de cette démo avancée est de pouvoir envoyer des commandes batch windows.&lt;/p&gt;


&lt;p&gt;Le canal d'entrée attend un message (body) ayant le format suivant: &lt;strong&gt;RepertoireExec&lt;/strong&gt;;&lt;strong&gt;commande&lt;/strong&gt;;&lt;strong&gt;optionCmd&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;avec comme séparateur le point virgule.&lt;/p&gt;


&lt;p&gt;En résumé la route est composée de cet enchainement:&lt;/p&gt;


&lt;p&gt;&quot;direct://a&quot;-&amp;gt; Processor1 -&amp;gt;uriExecShell -&amp;gt;Processor2 -&amp;gt; uriFile.&lt;/p&gt;


&lt;p&gt;Nous allons détailler les trois actes.&lt;/p&gt;



&lt;p&gt;&lt;ins&gt;Acte1&lt;/ins&gt;: Dessiner une route plus complexe&lt;/p&gt;

&lt;pre&gt;
package fr.netapsys.camel.routes;
import org.apache.camel.Exchange;
import org.apache.camel.builder.RouteBuilder;

import fr.netapsys.camel.processors.MyExecProcessor;
import fr.netapsys.camel.processors.MyProcessor;

public class MyThirdRoute extends RouteBuilder {
 @Override
 public void configure() throws Exception {
/**
 * la route est configurée ainsi:
 * Le msg, arrivant à &amp;quot;direct://a&amp;quot; est traité par MyProcessor. 
 * MyProcessor prépare les arguments de la commande batch
 * Puis passe le msg au second endpoint &amp;quot;exec:cmd&amp;quot;. 
 * Arrivé à ce dernier, le msg est traité par MyExecProcessor qui
 * analyse le résultat de l'execution. Deux cas possibles: 
 * - En cas d'echec du shell, une exception est levée.
 * - Sinon, un header est setté puis le msg est passé à uriFile
 */

 from(&amp;quot;direct://a&amp;quot;).process(new MyProcessor())
	.to(&amp;quot;cmd:exec&amp;quot;).process(new MyExecProcessor())
	.setHeader(Exchange.FILE_NAME, constant(&amp;quot;camelResult.txt&amp;quot;))
	.to(&amp;quot;file:/temp/?fileExist=Override&amp;quot;)
	.end();
  }
}
&lt;/pre&gt;


&lt;p&gt;Noter que les commentaires de cette classe détaille l'enchaînement.&lt;/p&gt;


&lt;p&gt;Il reste à écrire les deux classes MyProcessor et MyExecProcessor qui doivent implémenter l'interface Processor.&lt;/p&gt;


&lt;p&gt;Ce dernier possède une seule méthode process(Exchange ex).&lt;/p&gt;


&lt;p&gt;La première classe MyProcessor:&lt;/p&gt;

&lt;pre&gt;
package fr.netapsys.camel.processors;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.component.exec.ExecBinding;
import fr.netapsys.camel.utiles.Utiles;
import org.springframework.stereotype.Component;

@Component
public class MyProcessor  implements Processor {	
/***Simple processor afin de traiter  la ligne de commande** */
   @Override
   public void process(final Exchange exchange) throws Exception {
     // Recup the body sent to uriEntree
     final String body = exchange.getIn().getBody(String.class);
     String[] bodySplit=body.split(&amp;quot;;&amp;quot;);	    
     if(bodySplit.length&amp;lt;3) {
	throw new IllegalArgumentException(
            &amp;quot;Erreur:body format not correct for the body :&amp;quot; +body);
     }
    final String currentDir=bodySplit[0];
    final String cmdBatch=bodySplit[1];
    final String option=bodySplit[2];
    exchange.getOut().setHeader(
           ExecBinding.EXEC_COMMAND_WORKING_DIR, currentDir);	 
    // Activation des canaux StdErr si StdOut n est pas open
    exchange.getOut().setHeader(
              ExecBinding.EXEC_USE_STDERR_ON_EMPTY_STDOUT, true);
    exchange.getOut().setHeader(ExecBinding.EXEC_COMMAND_ARGS,
                Utiles.getExecCommand(cmdBatch, option));
  }
}
&lt;/pre&gt;


&lt;p&gt;&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;L'extraction des éléments de la commande windows est effectuée en splittant le message à l'aide du séparateur &lt;strong&gt;;&lt;/strong&gt; .&lt;/p&gt;


&lt;p&gt;Ces éléments sont explicitement nommés dans le code.&lt;/p&gt;


&lt;p&gt;La méthode process du Processor prend en argument une instance exchange.&lt;/p&gt;


&lt;p&gt;La ligne exchange.getIn().getBody() illustre que l'objet exchange contient, entre autres, le message In (en entrée).&lt;/p&gt;


&lt;p&gt;La ligne exchange.getOut() permet d'accéder au message Out (de sortie).&lt;/p&gt;


&lt;p&gt;Plus loin, nous verrons comment accéder à d'autres informations (ex. exception) durant l'acheminement du message.&lt;/p&gt;


&lt;p&gt;Donc, la ligne&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
exchange.getOut().setHeader(ExecBinding.EXEC_COMMAND_ARGS, 
                   Utiles.getExecCommand(cmdBatch, option));
&lt;/pre&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;établit la liste des arguments de la commande batch en utilisant la méthode statique &lt;em&gt;getExecCommand&lt;/em&gt; de la classe Utiles.&lt;/p&gt;


&lt;p&gt;Le code de la classe &lt;strong&gt;Utiles&lt;/strong&gt;  contient une seule méthode statique dont voici son code:&lt;/p&gt;

&lt;pre&gt;
public static List&amp;lt;String&amp;gt; getExecCommand(
     final String commande, final String param) {
    return new ArrayList&amp;lt;String&amp;gt;() {
      private static final long serialVersionUID =1L;
     {
       	add(&amp;quot;/C&amp;quot;);
       	add(commande);
       	add(param);
      }	        
    };
}
&lt;/pre&gt;


&lt;p&gt;Une seule option (param) de la commande cmd.exe a été gérée (sans soin) dans cette méthode.&lt;/p&gt;


&lt;p&gt;Passons à la seconde classe nommée MyExecProcessor:&lt;/p&gt;

&lt;pre&gt;
package fr.netapsys.camel.processors;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.component.exec.ExecResult;
import org.springframework.stereotype.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Component
public class MyExecProcessor implements Processor {
 private Logger  logger=
         LoggerFactory.getLogger(MyExecProcessor.class);
  @Override
  public void process(Exchange exchange) throws Exception {
    // On recupere l'exec de la ligne de commande
    final ExecResult execRes = exchange.getIn().
                         getBody(ExecResult.class);
    if (execRes.getExitValue() != 0) {
      logger.error(&amp;quot;\n\t&amp;gt;&amp;gt;&amp;gt;&amp;gt;Exec cmd '&amp;quot; +execRes.getCommand()+
          &amp;quot;' echec avec  exit value:'&amp;quot;+execRes.getExitValue()+&amp;quot;'\n&amp;quot;);
    }
 }
}
&lt;/pre&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;Dans la méthode process, on accède:&lt;/p&gt;


&lt;p&gt;- Au résultat de l'exécution de la commande via l'instance ExecResult,&lt;/p&gt;


&lt;p&gt;- Au contenu de l'exception levée dans l'objet exchange.&lt;/p&gt;


&lt;p&gt;L&lt;em&gt;'objetExecResult&lt;/em&gt; permet de récupérer le code retour de l'exécution d'une commande.
En cas d'échec de l'exécution de la commande un code de retour différent de zéro est retournée.&lt;/p&gt;


&lt;p&gt;&lt;br /&gt;&lt;/p&gt;



&lt;p&gt;&lt;ins&gt;Acte2&lt;/ins&gt;: Configurer Spring&lt;/p&gt;


&lt;p&gt;Pas grand chose si la route est déclarée dans le même package.&lt;/p&gt;



&lt;p&gt;&lt;ins&gt;Acte3&lt;/ins&gt;: Tester avec JUnit&lt;/p&gt;


&lt;p&gt;Le test JUnit repose toujours sur Spring:&lt;/p&gt;


&lt;pre&gt;
package fr.netapsys.camel.tests;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.component.exec.ExecException;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class TestMyExecRoute  extends TestParent{	
 private Logger logger=LoggerFactory.getLogger(TestMyExecRoute.class);
 @Test
 public void testExecRoute() throws Exception {		
    ProducerTemplate template=camelCtx.createProducerTemplate();
    try{
 	template.sendBody(&amp;quot;direct://a&amp;quot;, &amp;quot;/temp/;dir;/W&amp;quot;);
    }catch(ExecException e){
	logger.error(&amp;quot;\n&amp;gt;&amp;gt;&amp;gt;Erreur produite:\n&amp;quot;+e,e);
    }
 }
}
&lt;/pre&gt;


&lt;p&gt;&lt;br /&gt;
Il suffit de lancer mvn test.&lt;/p&gt;


&lt;p&gt;Pour voir la log s'afficher passer en argument commande non reconnue par l'OS.&lt;/p&gt;


&lt;p&gt;&lt;br /&gt;
La troisième partie nous fait découvrir les composants Camel-JDBC et Camel-SQL, une autre manière de de faire du jdbc ou de la peristance sans écrire du code (de plomberie!).
&lt;br /&gt;&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.netapsys.fr/index.php/post/2012/12/25/Camel%2C-Spring-et-tests-JUnit-pour-les-nuls-%28partie-2/3%29#comment-form</comments>
      <wfw:comment>http://blog.netapsys.fr/index.php/post/2012/12/25/Camel%2C-Spring-et-tests-JUnit-pour-les-nuls-%28partie-2/3%29#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.netapsys.fr/index.php/feed/atom/comments/532</wfw:commentRss>
      </item>
    
  <item>
    <title>Bean validation avec camel: Composant camel-bean-validator</title>
    <link>http://blog.netapsys.fr/index.php/post/2012/12/28/Bean-validation-avec-camel%3A-Composant-camel-bean-validator</link>
    <guid isPermaLink="false">urn:md5:08af2f2c1967c4398a9e6ea6377351e1</guid>
    <pubDate>Wed, 09 Jan 2013 19:00:00 +0100</pubDate>
    <dc:creator>Abderrazek CHINE</dc:creator>
        <category>Java J2EE</category>
        <category>@NotNull</category><category>@Valid</category><category>@value</category><category>bean</category><category>Bean validation</category><category>bean-validation</category><category>camel</category><category>camel-validator</category><category>CamelTestSupport</category><category>jsr303</category><category>spring</category><category>validation</category>    
    <description>&lt;p&gt;L'objet de ce billet est de présenter la validation (jsr303) avec le composant camel-bean-validator.&lt;/p&gt;


&lt;p&gt;&lt;img src=&quot;http://blog.netapsys.fr/public/ach/camel/camel-logo.png&quot; alt=&quot;camel-logo.png&quot; /&gt;&lt;/p&gt;


&lt;p&gt;Ce composant permet de valider des pojos ou beans annotés.&lt;/p&gt;


&lt;p&gt;Deux démos sont fournies. La seconde un peu plus avancée.&lt;/p&gt;


&lt;p&gt;Chaque démo est un projet maven réalisée en trois actes:&lt;/p&gt;


&lt;p&gt;- Configurer la pom,&lt;/p&gt;


&lt;p&gt;- Annoter le bean,&lt;/p&gt;


&lt;p&gt;- Tester avec JUnit.&lt;/p&gt;



&lt;p&gt;Voici dons les étapes.&lt;/p&gt;    &lt;p&gt;&lt;strong&gt;PREMIERE PARTIE&lt;/strong&gt;: &lt;strong&gt;Valider des beans&lt;/strong&gt;&lt;/p&gt;



&lt;p&gt;ACTE1. Dépendances du pom&lt;/p&gt;

&lt;pre&gt;
&amp;lt;dependency&amp;gt;
&amp;lt;groupId&amp;gt;org.apache.camel&amp;lt;/groupId&amp;gt;
&amp;lt;artifactId&amp;gt;camel-bean-validator&amp;lt;/artifactId&amp;gt;
&amp;lt;version&amp;gt;2.10.2&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/pre&gt;


&lt;p&gt;La version doit être celle de camel-core.&lt;/p&gt;



&lt;p&gt;ACTE2. Ecrire un bean annoté pour validation&lt;/p&gt;

&lt;pre&gt;
package fr.netapsys.camel.beans;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
public class Utilisateur {
	@NotNull private String login;
	@Pattern(regexp=&amp;quot;[a-zA-Z0-9]+&amp;quot;)
	private String pwd;

        public Utilisateur(){}
	
	public Utilisateur(String login,String pwd){
	  setLogin(login);
	  setPwd(pwd);
	}

... omis setters/getters et toString
}
&lt;/pre&gt;



&lt;p&gt;ACTE3. Tester et déclarer la route&lt;/p&gt;

&lt;pre&gt;
package fr.netapsys.camel.tests;

import org.apache.camel.*;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.bean.validator.*;
import org.apache.camel.test.junit4.CamelTestSupport;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import fr.netapsys.camel.beans.Utilisateur;

public class TestCamelUtilisateurValidator 
                        extends CamelTestSupport {

 private static final String ENTREE = &amp;quot;direct:in&amp;quot;;	
  private static final Logger logger=
        LoggerFactory.getLogger(TestCamelUtilisateurValidator.class);
  //Route definie ici en surchargeant 
   //createRouteBuilder de CamelTestSupport.
  @Override
   protected RouteBuilder createRouteBuilder() 
                  throws Exception {
     return new RouteBuilder() {
	@Override
	public void configure() throws Exception
	{
 	   onException(BeanValidationException.class)
	    .handled(false)
            .log(LoggingLevel.ERROR,
                 &amp;quot;&amp;gt;&amp;gt;&amp;gt;Error in:  ${body},\nmsg=${exception.message}\n&amp;quot;)
	    .end();
			 	
	  from(ENTREE)
		.to(&amp;quot;bean-validator://v&amp;quot;)
		.convertBodyTo(String.class)
		.setHeader(Exchange.FILE_NAME,constant(&amp;quot;ok.log&amp;quot;))
		.to(&amp;quot;file:/temp&amp;quot;)
		.end();
		}
	 };
	}

  @Test
  public void testOk() throws Exception {
		
	ProducerTemplate producerTemplate =
                     context.createProducerTemplate();
	Utilisateur user = new Utilisateur(&amp;quot;test&amp;quot;,&amp;quot;secret&amp;quot;);
	logger.info(&amp;quot;body ok :&amp;quot;+user);
		
	producerTemplate.sendBody(ENTREE, user);
         //assertion todo
         //dans /temp le fichier ok.log est créé
	}

  @Test
  public void testValidKo() throws Exception {
	ProducerTemplate producerTemplate = 
                context.createProducerTemplate();
	Utilisateur user = new Utilisateur();
	user.setLogin(null); //not correct
		
	Endpoint ep=context.getEndpoint(ENTREE);
	Exchange exch=ep.createExchange(ExchangePattern.InOnly);
	exch.getIn().setBody(user);
	Exchange out=producerTemplate.send(ep,exch);
	//pliz note this assert depends on errorHandler and handled.
	assertTrue(
            &amp;quot;Lorsque handled(false), BeanValidationException levée&amp;quot;,
           out.getException() instanceof BeanValidationException);
      }
}
&lt;/pre&gt;




&lt;p&gt;Explications:&lt;/p&gt;


&lt;p&gt;La route camel est définie dans le test en surchargeant la méthode createRouteBuilder() de CamelTestSupport.&lt;/p&gt;


&lt;p&gt;Cette  route permet de stocker les messages valides dans un fichier et de logguer en erreur les messages non valides.&lt;/p&gt;


&lt;p&gt;Deux méthodes de test&amp;nbsp;:&lt;/p&gt;


&lt;p&gt;- La première, testOk(), fait opérer la validation sur des données valides,&lt;/p&gt;


&lt;p&gt;- La seconde testValidKo() permet de voir la propagation d'une BeanValidationException sur des données non valides.&lt;/p&gt;



&lt;p&gt;Enfin, les commentaires dans le code du test détaille le fonctionnement du composant et l'interception de l'exception générée.&lt;/p&gt;


&lt;p&gt;&lt;strong&gt;SECONDE PARTIE&lt;/strong&gt;: &lt;strong&gt;Valider des listes de beans&lt;/strong&gt;&lt;/p&gt;



&lt;p&gt;ACTE1. Annoter le bean&lt;/p&gt;


&lt;p&gt;Ici nous écrivons une classe, nommée Groupe, conteneur de beans Utilisateur.&lt;/p&gt;


&lt;p&gt;La classe Groupe est annotée avec @Valid de javax.validation.&lt;/p&gt;

&lt;pre&gt;
package fr.netapsys.camel.beans;
import java.util.*;
import javax.validation.Valid;
public class Groupe {
	@Valid
	private List&amp;lt;Utilisateur&amp;gt; users;
 	public Groupe(){
	 users=new ArrayList&amp;lt;&amp;gt;();
 	}
 	//methode pratique
	public void addUser(Utilisateur u){
		users.add(u);
	}
	...omis getters/setters
}
&lt;/pre&gt;



&lt;p&gt;ACTE2. Tester&lt;/p&gt;


&lt;p&gt;Ajouter à la classe de test la méthode testValidGroupeKo:&lt;/p&gt;

&lt;pre&gt;
@Test
public void testValidGroupeKo()  {
 //preparer le body
  Groupe groupe=new Groupe();		
  Utilisateur user = new Utilisateur(&amp;quot;anonym&amp;quot;,&amp;quot;anonym&amp;quot;);
  groupe.addUser(user);
  
  user = new Utilisateur(&amp;quot;anonym&amp;quot;,&amp;quot;--&amp;quot;);
  groupe.addUser(user);

   //setter le body de l'exchange
   Endpoint ep=context.getEndpoint(ENTREE);
   Exchange exch=ep.createExchange();
   logger.info(&amp;quot;Bad groupe utilisatuers :&amp;quot;+groupe);
   exch.getIn().setBody(groupe);
   //envoi de l exchange
   ProducerTemplate producerTemplate = 
            context.createProducerTemplate();
   Exchange out=producerTemplate.send(ep,exch);

   //assertion
   assertTrue(&amp;quot; BeanValidationException est levée&amp;quot;, 
       out.getException() instanceof BeanValidationException);
}
&lt;/pre&gt;



&lt;p&gt;Les commentaires du code explique la démarche de validation d'un conteneur d'objets.&lt;/p&gt;


&lt;p&gt;En résumé, le conteneur doit comporter l'annotation @Valid et ses éléments doivent également être annotées avec javax.validation.constraints.&lt;/p&gt;


&lt;p&gt;Nous avons réalisé la validation des beans et des conteneurs de beans avec une ou deux annotations et presque une ligne de code.&lt;/p&gt;


&lt;p&gt;Voilà c'est tout!&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.netapsys.fr/index.php/post/2012/12/28/Bean-validation-avec-camel%3A-Composant-camel-bean-validator#comment-form</comments>
      <wfw:comment>http://blog.netapsys.fr/index.php/post/2012/12/28/Bean-validation-avec-camel%3A-Composant-camel-bean-validator#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.netapsys.fr/index.php/feed/atom/comments/537</wfw:commentRss>
      </item>
    
  <item>
    <title>Camel, Spring et tests JUnit pour les nuls (partie 1/3)</title>
    <link>http://blog.netapsys.fr/index.php/post/2012/12/09/Camel-et-Spring-pour-les-nuls-avec-d%C3%A9mo-d%C3%A9taill%C3%A9e</link>
    <guid isPermaLink="false">urn:md5:1596cc460f7f20361f648c7cbb80066c</guid>
    <pubDate>Fri, 04 Jan 2013 17:28:00 +0100</pubDate>
    <dc:creator>Abderrazek CHINE</dc:creator>
        <category>Java J2EE</category>
            
    <description>&lt;p&gt;L'objet de ce billet est d'expliquer l'essentiel de Apache Camel avec Spring et accompagné de tests JUnit.&lt;/p&gt;


&lt;p&gt;&lt;img src=&quot;http://blog.netapsys.fr/public/ach/camel/camel-logo.png&quot; alt=&quot;camel-logo.png&quot; style=&quot;float:left; margin: 0 1em 1em 0;&quot; /&gt; Apache Camel est un framework puissant implémentant les patterns EIP (Enterprise Integration Patterns).&lt;/p&gt;


&lt;p&gt;Apache Camel permet l'échange, le routage et la transformations des données avec une variété de protocoles et connecteurs.&lt;/p&gt;


&lt;p&gt;L'architecture de Camel basée sur des composants pluggables. Il offre une panoplie de composants intégrables avec facilité à nos projets web ou non.&lt;/p&gt;


&lt;p&gt;Apache Camel n'est pas réservée à des applications Client/Serveur,  des Webservices ou des échanges JMS (Java Messaging Service).&lt;/p&gt;


&lt;p&gt;En pratiquant Camel, on découvre que c'est un couteau suisse fort utile.&lt;/p&gt;


&lt;p&gt;Les démos ci-après, projets java standards, aident à la prise en main rapide de Camel.&lt;/p&gt;


&lt;p&gt;La dernière démo introduit néanmoins des notions avancées de Camel.&lt;/p&gt;


&lt;p&gt;Chacune des démos de ce billet est organisé en trois actes&amp;nbsp;:&lt;/p&gt;


&lt;p&gt;- Acte 1: Dessiner une route Camel,&lt;/p&gt;


&lt;p&gt;- Acte 2: Configurer Camel dans Spring,&lt;/p&gt;


&lt;p&gt;- Acte3: Tester avec Junit&lt;/p&gt;



&lt;p&gt;Camel v2.10, Spring v3.x et Java 7 sont utilisés bien que java 5 devrait suffire.&lt;/p&gt;


&lt;p&gt;Les mots clés de ce billet sont&amp;nbsp;: Camel, EIP, route, processor, exchange, body, header, spring, encodage, test JUnit.&lt;/p&gt;


&lt;p&gt;&lt;br /&gt;
Passons à la mise en pratique.
&lt;br /&gt;&lt;/p&gt;    &lt;p&gt;Contrairement à mes habitudes, je vous propose de démarrer par télécharger le &lt;a href=&quot;http://blog.netapsys.fr/public/ach/camel/camelblogdemo.zip&quot;&gt;zip&lt;/a&gt; du projet démo.&lt;/p&gt;


&lt;p&gt;Ce projet nous servira de base et il sera complété à partir de la seconde partie.&lt;/p&gt;


&lt;p&gt;Dézipper le projet dans le workspace de votre eclipse.&lt;/p&gt;


&lt;p&gt;Puis lancer la commande:&lt;/p&gt;

&lt;pre&gt;
mvn test
&lt;/pre&gt;


&lt;p&gt;En principe les tests passent (au vert).&lt;/p&gt;


&lt;p&gt;Sinon créer le répertoire /temp à la racine de votre disque (sous windows c:\temp) et relancer les tests.&lt;/p&gt;


&lt;p&gt;Lancer la commande&lt;/p&gt;

&lt;pre&gt;
mvn eclipse:eclipse
&lt;/pre&gt;


&lt;p&gt;Puis importer le projet dans eclipse.&lt;/p&gt;


&lt;p&gt;Vous pouvez par la suite reproduire, étape par étape, les démos ci-après ou exécuter directement le projet dans eclipse.&lt;/p&gt;


&lt;p&gt;Seul le fichier pom.xml ne sera détaillé dans ce billet.&lt;/p&gt;


&lt;p&gt;Compléter les dépendances nécessaires en s'inspirant du pom.xml fourni dans le zip.&lt;/p&gt;


&lt;p&gt;&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;&lt;strong&gt;NOTIONS ET TERMINOLOGIE&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;On va expliquer rapidement les notions importantes de Camel ainsi que les termes provenant de l'EIP.&lt;/p&gt;


&lt;p&gt;Certains termes anglais ne seront pas traduits.&lt;/p&gt;


&lt;p&gt;1- &lt;strong&gt;Camel contexte &amp;amp; Composant&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Camel, au plus haut niveau, définit un contexte qui est une collection d'instances de composants.&lt;/p&gt;


&lt;p&gt;Chaque composant est principalement une fabrique (factory) d'instances de &lt;em&gt;endpoint&lt;/em&gt;. Voir après.&lt;/p&gt;


&lt;p&gt;2- &lt;strong&gt;Endpoint&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Ce terme est utilisé dans la communication inter-processus ou inter-composants.&lt;/p&gt;


&lt;p&gt;Par exemple, en communication Client/Serveur, le client est un endpoint et le serveur en est un autre.&lt;/p&gt;


&lt;p&gt;Selon l'utilisation, un endpoint peut référer à une adresse URL www.host:port,&lt;/p&gt;


&lt;p&gt;Et derrière cette URL il se peut qu'il y a un logiciel ou (web)service interrogeable.&lt;/p&gt;


&lt;p&gt;Donc, un endpoint agit comme une URI (voir ci-après) dans une application ou comme une destination JMS.&lt;/p&gt;


&lt;p&gt;On communique avec un endpoint de deux façons:&lt;/p&gt;


&lt;p&gt;- soit par l'envoi de message en créant un Producer attaché à cet endpoint,&lt;/p&gt;


&lt;p&gt;- soit par consommer le message en créant un Consumer sur cet endpoint.&lt;/p&gt;


&lt;p&gt;A noter que certains endpoints (ex. DirectEndpoint ) n'acceptent pas de consommateurs et d'autres (ex. JdbcEndpoint) n'acceptent pas de producteurs.&lt;/p&gt;



&lt;p&gt;3- &lt;strong&gt;Route &amp;amp; RouteBuilder&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Une route est l'enchainement des étapes qu'un message doit parcourir à partir de sa réception par le canal (endpoint) d'entrée.&lt;/p&gt;


&lt;p&gt;Camel fournit deux manières d'écrire une route:&lt;/p&gt;


&lt;p&gt;- soit par configuration XML,&lt;/p&gt;


&lt;p&gt;- soit avec Camel java DSL (domain specific language).&lt;/p&gt;


&lt;p&gt;&lt;ins&gt;Note&lt;/ins&gt;. Dans la suite on utilise plutôt le java DSL&lt;/p&gt;


&lt;p&gt;4- &lt;strong&gt;URI&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Camel utilise beaucoup cette notion pour construire une route.&lt;/p&gt;


&lt;p&gt;Quels sont les significations des URI, URN, URL?&lt;/p&gt;


&lt;p&gt;Un URI permet d'identifier une ressource de manière permanente.&lt;/p&gt;


&lt;p&gt;Un URI peut-être de type URL (locator) ou URN (name).&lt;/p&gt;


&lt;p&gt;Voir le &lt;a href=&quot;http://fr.wikipedia.org/wiki/Uniform_Resource_Identifier#Relation_avec_les_URL_et_URN&quot;&gt;wiki&lt;/a&gt; pour plus de détails.&lt;/p&gt;


&lt;p&gt;5- &lt;strong&gt;Message&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Le Message est une interface abstraite (public abstract interface org.apache.camel.Message).
Exemples:
-la requête en entrée, la réponse ou un message d' exception.&lt;/p&gt;


&lt;p&gt;Dans le jargon de Camel, la requête est nommée &lt;strong&gt;In&lt;/strong&gt;, le retour &lt;strong&gt;Out&lt;/strong&gt; et le message d'exception '&lt;strong&gt;Fault message&lt;/strong&gt;'.&lt;/p&gt;


&lt;p&gt;Des classes Camel concrètes implémentent cette interface (ex.GenericFileMessage, MailMessage).&lt;/p&gt;


&lt;p&gt;6- &lt;strong&gt;Exchange&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Exchange est une interface abstraite pour l'échange de messages.&lt;/p&gt;


&lt;p&gt;C'est un conteneur de messages qui tient à jour les informations durant l'acheminement du message reçu.&lt;/p&gt;


&lt;p&gt;DefaultExchange est une classe concrète dans Camel implémentant l'interface Exchange.&lt;/p&gt;



&lt;p&gt;7- &lt;strong&gt;CamelTemplate&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;La classe Camel nommée auparavant &lt;em&gt;CamelClient&lt;/em&gt; a été renommée en &lt;strong&gt;CamelTemplate&lt;/strong&gt;
afin de se conformer au nommage et convention rendus célèbres par Spring.&lt;/p&gt;


&lt;p&gt;La classe CamelTemplate est un wrapper qui permet l'envoi de message (exchange).&lt;/p&gt;


&lt;p&gt;&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;Avec ces notions, je crois que nous sommes bien armés pour débuter une première démo.&lt;/p&gt;


&lt;p&gt;&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;&lt;strong&gt;PREMIERE DEMO&lt;/strong&gt;: &lt;strong&gt;Loguer avec Camel&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Nous commençons par une démo rapide et facile pour illustrer la &lt;strong&gt;puissance&lt;/strong&gt; de Camel.&lt;/p&gt;


&lt;p&gt;Dans cette démo, on souhaite tracer des logs.&lt;/p&gt;


&lt;p&gt;Et il ne faudrait qu'une seule ligne de code.&lt;/p&gt;


&lt;p&gt;Mais on va détailler un peu et écrire les briques séparément en suivant les trois actes précitées.&lt;/p&gt;



&lt;p&gt;&lt;ins&gt;Acte1&lt;/ins&gt;: &lt;strong&gt;Dessiner une route&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;A l'aide du java DSL (Domain Specific Language), écrivons une première classe &lt;em&gt;MyFirstRoute&lt;/em&gt; qui déclare une route Camel:&lt;/p&gt;

&lt;pre&gt;
package fr.netapsys.camel.routes;
import org.apache.camel.Exchange;
import org.apache.camel.builder.RouteBuilder;

public class MyFirstRoute extends RouteBuilder {
  @Override
  public void configure() throws Exception {
    from(&amp;quot;direct://aa&amp;quot;).to(&amp;quot;log:fr.netapsys.camel?level=DEBUG&amp;quot;).end();
 }
}
&lt;/pre&gt;


&lt;p&gt;Noter que MyFirstRoute hérite de &lt;em&gt;RouteBuilder&lt;/em&gt; d'où la méthode &lt;em&gt;configure()&lt;/em&gt; surchargée.&lt;/p&gt;


&lt;p&gt;Le &quot;from&quot; précise le endpoint d'entrée qui est un composant Camel &lt;strong&gt;&lt;a href=&quot;http://camel.apache.org/direct.html&quot;&gt;DirectComponent&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;


&lt;p&gt;Le &quot;direct://aa&quot; veut dire simplement que la route démarre avec un DirectEndpoint nommé &quot;aa&quot;.&lt;/p&gt;


&lt;p&gt;DirectEndpoint assure une communication synchronisée.&lt;/p&gt;


&lt;p&gt;Pour envoyer un message à &quot;direct://aa&quot;, il faudrait instancier un ProducerTemplate puis appeler l'une des méthodes sendXXXX.&lt;/p&gt;


&lt;p&gt;Le &quot;to&quot; indique que c'est vers le composant &lt;strong&gt;log&lt;/strong&gt; que le message sera transmis.&lt;/p&gt;


&lt;p&gt;L'URI du composant log d'Apache Camel a le format suivant:&lt;/p&gt;

&lt;pre&gt;
log:loggingCategory[?options] 
&lt;/pre&gt;


&lt;p&gt;L'option, level=DEBUG, de notre cas indique le niveau DEBUG associé au logger (catégorie) &quot;fr.netapsys.camel&quot;. Voir la documentation de &lt;a href=&quot;http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/Category.html&quot;&gt;log4j&lt;/a&gt;.&lt;/p&gt;


&lt;p&gt;Nous venons de dessiner une route Camel qui a deux endpoints.&lt;/p&gt;


&lt;p&gt;L'une d'entrée &quot;direct://aa&quot; et l'autre est le composant log.&lt;/p&gt;


&lt;p&gt;Il ne reste qu'à envoyer un message à &quot;direct:/:aa&quot; et il sera loggué. C'est ce que nous ferons dans le test JUnit (acte3).&lt;/p&gt;


&lt;p&gt;Il est temps de configurer le contexte Camel dans Spring.
&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;&lt;ins&gt;Acte2&lt;/ins&gt;: &lt;strong&gt;Configurer Spring&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Le fichier spring doit par convention être localisé dans META-INF/spring et nommé camel-context.xml.&lt;/p&gt;


&lt;p&gt;En respectant cette convention, Spring charge automatiquement le contexte Camel et le démarre.&lt;/p&gt;


&lt;p&gt;Voici les quelques lignes xml valables pour toutes les démos:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;
&amp;lt;beans xmlns=&amp;quot;http://www.springframework.org/schema/beans&amp;quot;
  xmlns:xsi=&amp;quot;http://www.w3.org/2001/XMLSchema-instance&amp;quot; 
  xmlns:camel=&amp;quot;http://camel.apache.org/schema/spring&amp;quot;
  xsi:schemaLocation=&amp;quot;
  http://www.springframework.org/schema/beans 
  http://www.springframework.org/schema/beans/spring-beans.xsd
  http://camel.apache.org/schema/spring 
  http://camel.apache.org/schema/spring/camel-spring.xsd&amp;quot;&amp;gt;	
  
&amp;lt;context:component-scan base-package=&amp;quot;fr.netapsys.camel&amp;quot;/&amp;gt;
 &amp;lt;camel:camelContext
	xmlns=&amp;quot;http://camel.apache.org/schema/spring&amp;quot;&amp;gt;
     &amp;lt;camel:package&amp;gt;fr.netapsys.camel.routes&amp;lt;/camel:package&amp;gt;	
&amp;lt;/camel:camelContext&amp;gt;
&lt;/pre&gt;


&lt;p&gt;&lt;br /&gt;
&lt;strong&gt;&lt;em&gt;Explications&lt;/em&gt;&lt;/strong&gt;:&lt;/p&gt;


&lt;p&gt;Nous avons déclaré dans camelContext le package où sont déclarées nos routes camel.&lt;/p&gt;


&lt;p&gt;Et context:componet-scan permet à spring d'auto-injecter les beans (ex. les processors).&lt;/p&gt;


&lt;p&gt;C'est tout!&lt;/p&gt;


&lt;p&gt;&lt;ins&gt;Acte3&lt;/ins&gt;: &lt;strong&gt;Écrire le test Junit&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Le code du test JUnit est&amp;nbsp;:&lt;/p&gt;

&lt;pre&gt;
package fr.netapsys.camel.tests;
import org.apache.camel.ProducerTemplate;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({&amp;quot;classpath:META-INF/spring/camel-context.xml&amp;quot;})
public class TestMyFirstRoute {
 @Autowired CamelContext camelCtx;
 @Test
 public void testCamel() throws Exception {		
  ProducerTemplate template=camelCtx.createProducerTemplate();		
     //send msg to log
     template.sendBody(&amp;quot;direct://aa&amp;quot;, &amp;quot;\nloggue moi  \n&amp;quot;);
  }
}
&lt;/pre&gt;


&lt;p&gt;&lt;br /&gt;
&lt;strong&gt;&lt;em&gt;Explications&lt;/em&gt;&lt;/strong&gt;:&lt;/p&gt;


&lt;p&gt;Camel étant configuré dans Spring, le contexte Camel est démarré automatiquement, il suffit d'injecter (via @Autowired) une instance de CamelContext.&lt;/p&gt;


&lt;p&gt;Avec l'instance camelContext, on peut créer un ProducerTemplate: une template à la sauce de spring facilitant l'envoi de message.&lt;/p&gt;


&lt;p&gt;La méthode sendBody utilisée ici attend deux arguments: Un endpoint et un body de l'exchange (message).&lt;/p&gt;


&lt;p&gt;Ici, on lui a passé les deux arguments &quot;direct://aa&quot; et le contenu du message qui est un String.&lt;/p&gt;


&lt;p&gt;Un point de vigilance est de reprendre le endpoint &lt;strong&gt;direct://aa&lt;/strong&gt; tel qu'il a été déclaré dans la route Camel.&lt;/p&gt;


&lt;p&gt;Enfin, la commande mvn test affiche la sortie sur la console dos:&lt;/p&gt;

&lt;pre&gt;
Running fr.netapsys.camel.tests.TestMyFirstRoute
xxxxxx camel [DEBUG] Exchange[ExchangePattern:InOnly, 
   BodyType:String, Body: loggue moi]
&lt;/pre&gt;


&lt;p&gt;&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;&lt;ins&gt;&lt;em&gt;REMARQUE IMPORTANTE&lt;/em&gt;&lt;/ins&gt;&lt;/p&gt;


&lt;p&gt;Le même résultat aurait pu être obtenu avec la configuration suivante de notre route:&lt;/p&gt;

&lt;pre&gt;
 from(&amp;quot;direct://log&amp;quot;)
  .log(LoggingLevel.DEBUG,&amp;quot;fr.netapsys.camel&amp;quot;,&amp;quot;${body}&amp;quot;)
  .end();
&lt;/pre&gt;


&lt;p&gt;Et le test JUnit devient:&lt;/p&gt;

&lt;pre&gt;
template.sendBody(&amp;quot;direct://log&amp;quot;,&amp;quot;loggue moi&amp;quot;);
&lt;/pre&gt;


&lt;p&gt;&lt;br /&gt;
&lt;br /&gt;&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;DEUXIEME DEMO&lt;/strong&gt;: &lt;strong&gt;Transformer les données vers un fichier&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Dans la classe MySecondRoute, la route trace l'enchainement suivant:&lt;/p&gt;


&lt;p&gt;- Le direct endpoint &quot;direct://a&quot; est celui qui réceptionne le message,&lt;/p&gt;


&lt;p&gt;- Une transformation simple est opérée sur le message,&lt;/p&gt;


&lt;p&gt;- Une conversion du message selon un encodage prédéfini,&lt;/p&gt;


&lt;p&gt;- Sauvegarde du message dans un fichier.&lt;/p&gt;


&lt;p&gt;En résumé on a donc cet enchainement:&lt;/p&gt;


&lt;p&gt;uriDirect -&amp;gt; Processor (transformation) -&amp;gt; Encodage -&amp;gt;uriFile.&lt;/p&gt;



&lt;p&gt;&lt;ins&gt;Acte1&lt;/ins&gt;: &lt;strong&gt;Dessiner une route&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Voici la classe MySecondRoute qui hérite de RouteBuilder et définit la méthode configure():&lt;/p&gt;

&lt;pre&gt;
package fr.netapsys.camel.routes;
import org.apache.camel.Exchange;
import org.apache.camel.builder.RouteBuilder;

public class MySecondRoute extends RouteBuilder {
  @Override
  public void configure() throws Exception {
    from(&amp;quot;direct://starting&amp;quot;)
     .transform().simple(&amp;quot;Bonjour,\n ${in.body} \nSincères salutations&amp;quot;)
     .convertBodyTo(String.class,&amp;quot;iso-8859-1&amp;quot;)
     .setHeader(Exchange.FILE_NAME, constant(&amp;quot;camelroute2.txt&amp;quot;))     
     .to(&amp;quot;file:/temp/?fileExist=Override&amp;quot;)
     .end();
  }
}
&lt;/pre&gt;


&lt;p&gt;&lt;br /&gt;
Les déclarations importantes ici sont:&lt;/p&gt;


&lt;p&gt;- Un DirectEndpoint nommé &quot;//starting&quot;,&lt;/p&gt;


&lt;p&gt;- Une transformation simple du message est effectué à l'aide de la méthode &lt;em&gt;transform&lt;/em&gt;,&lt;/p&gt;


&lt;p&gt;- Une conversion du code en String avec l'encodage iso-8859-1,&lt;/p&gt;


&lt;p&gt;- Un FileEndpoint avec son header qui fixe le nom du fichier où stocker le message. L'option fileExist permet d'écraser le fichier s'il existe.&lt;/p&gt;



&lt;p&gt;&lt;br /&gt;&lt;/p&gt;


&lt;p&gt;&lt;ins&gt;Acte2&lt;/ins&gt;: &lt;strong&gt;Configurer Spring&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Rien à faire si la route est déclarée dans le package &lt;em&gt;fr.netapsys.camel.routes&lt;/em&gt;.&lt;/p&gt;


&lt;p&gt;&lt;br /&gt;
&lt;ins&gt;Acte3&lt;/ins&gt;: Tester avec JUnit&lt;/p&gt;

&lt;pre&gt;
package fr.netapsys.camel.tests;
import org.apache.camel.ProducerTemplate;
import org.junit.Test;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({&amp;quot;classpath:META-INF/spring/camel-context.xml&amp;quot;})
public class TestMySecondeRoute{
@Autowired CamelContext camelCtx;
@Test
public void testCamel() throws Exception {		
 ProducerTemplate template=camelCtx.createProducerTemplate();
 template.sendBody(&amp;quot;direct://starting&amp;quot;,
  &amp;quot;\nMonsieur Néçàn\nNous avons bien reçu votre demande...\nblabla\n&amp;quot;);
 }
}
&lt;/pre&gt;


&lt;p&gt;La commande mvn test produira un fichier nommé camelroute2.txt dans le répertoire /temp.&lt;/p&gt;


&lt;p&gt;&lt;br /&gt;
&lt;strong&gt;TROISIEME DEMO&lt;/strong&gt;: &lt;strong&gt;Exécuter script shell: Niveau avancé&lt;/strong&gt;&lt;/p&gt;



&lt;p&gt;L'objet de cette démo avancée est de pouvoir envoyer des commandes batch windows.&lt;/p&gt;


&lt;p&gt;Le canal d'entrée attend un message (body) ayant le format suivant:&lt;/p&gt;


&lt;p&gt;&lt;strong&gt;RepertoireExec&lt;/strong&gt;;&lt;strong&gt;commande&lt;/strong&gt;;&lt;strong&gt;optionCmd&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Noter que le séparateur est un point virgule.&lt;/p&gt;


&lt;p&gt;En résumé la route est composée de cet enchainement:&lt;/p&gt;


&lt;p&gt;uriDirect -&amp;gt; Processor1 -&amp;gt;uriExecShell -&amp;gt;Processor2 -&amp;gt; uriFile.&lt;/p&gt;


&lt;p&gt;&lt;br /&gt;
&lt;strong&gt;La suite dans la prochaine partie&lt;/strong&gt;.
&lt;br /&gt;&lt;/p&gt;</description>
    
    
    
          <comments>http://blog.netapsys.fr/index.php/post/2012/12/09/Camel-et-Spring-pour-les-nuls-avec-d%C3%A9mo-d%C3%A9taill%C3%A9e#comment-form</comments>
      <wfw:comment>http://blog.netapsys.fr/index.php/post/2012/12/09/Camel-et-Spring-pour-les-nuls-avec-d%C3%A9mo-d%C3%A9taill%C3%A9e#comment-form</wfw:comment>
      <wfw:commentRss>http://blog.netapsys.fr/index.php/feed/atom/comments/523</wfw:commentRss>
      </item>
    
</channel>
</rss>