IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Spring Java Discussion :

Quartz et injection de dépendance [Batch]


Sujet :

Spring Java

  1. #1
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut Quartz et injection de dépendance
    Bonjour,

    je vous devrais réaliser la situation suivante:

    -> J'ai un scheduler persistant
    -> un ensemble de classes implémentent statefulJob
    -> ces classes on certains getters / setters (setDataSource, par exemple)
    -> Je crée (spring) des beans sur base de ces classes. Appelons en une JobX. Cette classe contient des getters/setters pour accéder à dautres beans gérés par spring (configuration, collections de données à manipuler, etc). Bref, principe de l'IOC
    -> J'ai besoin de scheduler la chose suivante, par code:
    "Au moment X, exécuter le Job représenté par le bean 'JobX', avec la jobDataMap suivante: ...."
    Ce détail est important, car le JobX peut etre schedulé un 100aine de fois avec des paramètre différente, mais il uara toujours besoin des mêmes beans Spring pour faire le boulot. Que JobX soit instancié une seule fois ou à chauqe trigger, peut m'importer

    J'ai bien trouvé la JobFactory de spring, mais celle là se content d'injecter dans le Job le contenu du context, donc pas mes beans (sauf erreur de ma part?) comme je le pourrait avec un notation dans le beans-config.xml. Pareil avec QuartzJobBean

    Il y a aussi le
    MethodInvokingJobDetailFactoryBean, que je pourrais utiliser. Cependant, il ne passe pas le JobDataMap à la méthode appelée et n'est pas sérializable. De plus il résoud le nom du bean appelé au moment de créer le JobDetail, moi il faudrait qu'il soit résolu au moment d'appeler le job

    Bref, je suis un peu perdu, quelle est la méthode "recommandée"?

  2. #2
    Membre régulier
    Inscrit en
    Février 2008
    Messages
    123
    Détails du profil
    Informations forums :
    Inscription : Février 2008
    Messages : 123
    Points : 77
    Points
    77
    Par défaut
    J'ai cherché très longtemps (peut-être 1 ou 2 semaines) à faire ceci sans jamais parvenir à une solution simple.

    Quand ta classe hérite de QuartzJobBean et implementes StatefulJob.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    protected void executeInternal(JobExecutionContext jobContext) throws JobExecutionException
    Cette méthode correspond à un objet bien distinct qui vient d'être instancié pour l’exécution, il est donc impossible à ma connaissance de faire de l'injection de bean sur cet objet qui vient d'être instancié

    Je suis passé par la méthode que tu as évoqué en fournissant le contextApplication par l'intermédiaire du jobDataMap.

    Je crois qu'il est par contre possible d'avoir 2 maps (identifié par 2 identifiants) dans jobDataMap. Dans l'une, tu pourras mettre les beans Manager et dans l'autres, les paramètres.

    Ce qui permet au moins de différencier les 2 types. Désolé de pas pouvoir t'aider davantages.

    Voici mon code au cas ou:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
        <bean name="jobDetailCheckImportAuto" class="org.springframework.scheduling.quartz.JobDetailBean">
    	  	<property name="jobClass" value="demo.core.batch.JobCheckImportAuto" />
    	  	<property name="applicationContextJobDataKey" value="applicationContext" />
    	  	<property name="name" 		value="jobCheckImportAuto"/>
    		<property name="durability" value="true"/>
    	</bean>
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
     
    public class JobCheckImportAuto extends QuartzJobBean implements StatefulJob {
     
    	/**
             * Overwrite method.
             * 
             * @see org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean.MethodInvokingJob#executeInternal(org.quartz.JobExecutionContext)
             * @param jobContext
             *            jobContext
             * @throws JobExecutionException
             *             JobExecutionException
             */
    	@Override
    	protected void executeInternal(JobExecutionContext jobContext) throws JobExecutionException {
    		ApplicationContext applicationContext = (ApplicationContext) jobContext.getJobDetail().getJobDataMap().get("applicationContext");
    		MonManager monManager = (MonManager) applicationContext.getBean("monManager");
     
    	}
    }

  3. #3
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    Le problème de mettre les Beans dans la JobDataMap, c'est que c'est abherant pour des Job qui doivent persister sur le long terme, au delà du redémarrage de l'application. Je crosi que mon option la plus propre est d'implémenter ma propre JobFactory qui va utiliser le contexte plutot que d'instancier des objets Job. Je vous tiens au jus

  4. #4
    Membre régulier
    Inscrit en
    Février 2008
    Messages
    123
    Détails du profil
    Informations forums :
    Inscription : Février 2008
    Messages : 123
    Points : 77
    Points
    77
    Par défaut
    Les solutions les plus simples (à coder) sont malheureusement parfois les plus aberrante (fonctionnellement parlant).

    Mais si tu trouve la solution, je suis preneur.

  5. #5
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    c'est bon, trouvé. En me basant sur la SpringBeanJobFactory, j'ai cette classe:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public class BeanBasedJobFactory extends AdaptableJobFactory implements
    		SchedulerContextAware, ApplicationContextAware {
    //...
    @Override
    	protected Object createJobInstance(TriggerFiredBundle bundle)
    			throws Exception {
    		String beanName = bundle.getJobDetail().getJobDataMap().getString(
    				"jobBean");
    		BeanWrapper bw = null;
    		if (beanName != null) {
    			Object o = getApplicationContext().getBean(beanName);
    			if (o != null)
    				bw = new BeanWrapperImpl(o);
    		}
    		if (bw == null)
    			bw = new BeanWrapperImpl(bundle.getJobDetail().getJobClass());
    //.....
    }
    tout le reste du corps de la méthode est inchangé par rapport à la classe fournie par Spring.

    Au final j'ai ça dans comme Job de test:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    	public static class TheJob implements StatefulJob {
     
    		private String testDataString;
    		private Boolean testDataBoolean;
    		private TheResult result;
     
                    // getters setters
     
    		protected AssertionError error;
     
    		@Override
    		public void execute(JobExecutionContext ctx)
    				throws JobExecutionException {
                               // on modifie "theResult"
    		}
    	}
    et ça dans mon xml qui sert à mes tests
    Code xml : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
     
    <!-- le Scheduler avec MA factory -->
    <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean" id="scheduler">
       <property name="autoStartup" value="true"/>   
       <property name="jobFactory">
          <bean class="xxx.BeanBasedJobFactory"/>
        </property>
        <property name="jobDetails">
         <list>
          <ref bean="testJobDetail"/>
         </list>
        </property>
    </bean>
    <!-- un job details d'exemple utilisant un Bean Spring comme exécutant,
     on aurait pu faire ça avec du code aussi (ce qui est mon but final) -->
    <bean name="testJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
      <!-- seul "hic", même si je ne m'en sert pas, la classe est obligatoire -->
      <property name="jobClass" value="xxxx.TestContextJob$TheJob"/>
      <property name="jobDataAsMap">
        <map>
          <entry key="timeout" value="5" />
          <!-- on pointe un bean spring, qui est donc configuré par spring -->
          <entry key="jobBean">
            <idref local="iocJob"/>
          </entry>
        </map>
      </property>
      <property name="group" value="testGroup"/>
      <property name="name" value="testName"/>
    </bean>
    <!-- Mon job, qui est un bean JSF à part entière, en prototype pour avoir des instances séparées -->
    <bean class="xxx.TestContextJob$TheJob" scope="prototype" id="iocJob"> 
      <property name="testDataBoolean" value="#{JobSource.sourceBoolean}"/>
      <property name="testDataString" value="#{JobSource.sourceString}"/>
      <property name="result" ref="JobResult"/>
    </bean>
     
    <!-- les beans qu'on relie au job pour le test -->
    <bean class="xxx.TestContextJob$TheResult"  id="JobResult"/>
    <bean class="xxx.TestContextJob$TheSource" id="JobSource">
       <property name="sourceBoolean" value="true"/>
       <property name="sourceString" value="TESTSTRING"/>
    </bean>

    et enfin le code de test qui montre qu'on peux crée aussi avec du code le jobdetail:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    GenericXmlApplicationContext ctx = new GenericXmlApplicationContext(getClass().getResource("/contexts/spring-quartz-test.xml").toURI().toString());
    		Assert.assertNotNull(ctx.getBean("scheduler"));
    		Scheduler sc =  (Scheduler) ctx.getBean("scheduler");
    		Assert.assertNotNull(sc.getJobDetail("testName", "testGroup"));
                    // créer un jobdetail à la main
    		JobDetail jb = new JobDetail("test","test",TheJob.class);
    		jb.getJobDataMap().put("jobBean", "iocJob");
    		sc.addJob(jb, true);
                    // on altere la source, simuler la vie d'un programme
    		TheSource datas  = (TheSource) ctx.getBean("JobSource");
    		datas.setSourceBoolean(true);
    		datas.setSourceString("1234567violette");
    		TheResult result = (TheResult) ctx.getBean("JobResult");
    		Thread.sleep(500);
                    // on exécute le job
    		sc.triggerJobWithVolatileTrigger("test","test");
    		Thread.sleep(500);
                    // il a bien lu les données et les as transférées?
    		Assert.assertEquals(result.resultBoolean,datas.getSourceBoolean());
    		Assert.assertEquals(result.resultString,datas.getSourceString());
     
                    //idem avec le jobdetail configuré dans le xml
    		datas.setSourceBoolean(false);
    		datas.setSourceString("Halloween!!");
    		result.resultString=null;
    		result.resultBoolean=null;
    		Assert.assertNotSame(result.resultBoolean,datas.getSourceBoolean());
    		Assert.assertNotSame(result.resultString,datas.getSourceString());
    		sc.triggerJobWithVolatileTrigger("testName","testGroup");
    		Thread.sleep(500);
    		Assert.assertEquals(result.resultBoolean,datas.getSourceBoolean());
    		Assert.assertEquals(result.resultString,datas.getSourceString());

  6. #6
    Rédacteur
    Avatar de Hikage
    Profil pro
    Inscrit en
    Mai 2004
    Messages
    1 177
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : Belgique

    Informations forums :
    Inscription : Mai 2004
    Messages : 1 177
    Points : 6 301
    Points
    6 301
    Par défaut
    Tiens juste pour infos :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    GenericXmlApplicationContext ctx = new GenericXmlApplicationContext(getClass().getResource("/contexts/spring-quartz-test.xml").toURI().toString());
    Tu peux utiliser également ClasspathRessource pour faire plus joli

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    GenericXmlApplicationContext ctx = new GenericXmlApplicationContext(new ClasspathRessource("/contexts/spring-quartz-test.xml"));
    Hikage
    SCJP / SCWCD & SCWSJD Certified / Spring Framework Certified
    [Personal Web] [CV]

    F.A.Q Spring Framework - Participez !

  7. #7
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    ha ouais tiens

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [EJB3] [JBoss] Injection de dépendance circulaire ?
    Par Claythest dans le forum Java EE
    Réponses: 6
    Dernier message: 04/08/2009, 08h11
  2. [Framework] Injection de dépendances ; comment se passer du XML ?
    Par ummon99 dans le forum Spring
    Réponses: 3
    Dernier message: 12/01/2008, 09h19
  3. [EJB3] Injection de dépendance et Stateful
    Par newbeewan dans le forum Java EE
    Réponses: 1
    Dernier message: 15/05/2007, 07h33
  4. [Integration] [EasyMock] Injection de dépendance à l'éxécution
    Par frangin2003 dans le forum Spring
    Réponses: 2
    Dernier message: 06/03/2007, 11h06
  5. Spring + TagSupport et injection de dépendance
    Par worldchampion57 dans le forum Spring Web
    Réponses: 2
    Dernier message: 26/02/2007, 09h01

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo