Cet article fait suite aux précédents posts sur TDD et BDD avec Spring Batch.
Le but est de packager et exécuter directement un batch Spring Batch sans ajout de configuration spécifique de packaging ni d’installation préalable d’environnement d’exécution particulier.
Le projet Github springbatch-sample montre comment Spring Boot permet de construire de manière simple un livrable auto exécutable.
La documentation de référence spring détaille les étapes pour un projet avec une seule datasource contenant un seul job et sans paramètres de lancement.
On va voir dans l’article les configurations Spring Boot nécessaires pour lancer un job d’alimentation de données dans une base, avec datasources technique et fonctionnelle séparées et comment traiter les paramètres d’entrée et propriétés système.
Configuration maven
Déclaration projet parent Spring Boot
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.2.2.RELEASE</version> </parent>
Ajout dépendance maven Spring Boot pour les batchs (plus besoin de déclarer les dépendances spring)
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-batch</artifactId> </dependency>
Ajout du plugin spring boot
<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin>
Configuration java du runner
package com.giovanetti.sample.batch.job; | |
import com.giovanetti.support.batch.annotations.TechnicalDataSource; | |
import com.giovanetti.support.batch.function.Consumer; | |
import com.giovanetti.support.batch.function.Function; | |
import org.springframework.batch.core.explore.JobExplorer; | |
import org.springframework.batch.core.explore.support.JobExplorerFactoryBean; | |
import org.springframework.batch.core.launch.JobLauncher; | |
import org.springframework.beans.factory.FactoryBean; | |
import org.springframework.beans.factory.annotation.Value; | |
import org.springframework.boot.SpringApplication; | |
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; | |
import org.springframework.boot.autoconfigure.SpringBootApplication; | |
import org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration; | |
import org.springframework.boot.autoconfigure.batch.JobLauncherCommandLineRunner; | |
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; | |
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration; | |
import org.springframework.context.annotation.Bean; | |
import org.springframework.context.annotation.Configuration; | |
import javax.sql.DataSource; | |
@Configuration | |
@EnableAutoConfiguration(exclude={DataSourceTransactionManagerAutoConfiguration.class, BatchAutoConfiguration.class, DataSourceAutoConfiguration.class}) | |
@SpringBootApplication | |
public class BatchApplication { | |
@Bean | |
public JobExplorer jobExplorer(@TechnicalDataSource DataSource dataSource) { | |
JobExplorerFactoryBean factory = new JobExplorerFactoryBean(); | |
factory.setDataSource(dataSource); | |
Consumer.acceptWithRawException(factory, JobExplorerFactoryBean::afterPropertiesSet); | |
return Function.applyWithRawException(factory, FactoryBean::getObject); | |
} | |
@Bean | |
public JobLauncherCommandLineRunner jobLauncherCommandLineRunner(JobLauncher jobLauncher, JobExplorer jobExplorer, @Value( | |
"${job.name}") String jobName) { | |
JobLauncherCommandLineRunner runner = new JobLauncherCommandLineRunner(jobLauncher, jobExplorer); | |
runner.setJobNames(jobName); | |
return runner; | |
} | |
public static void main(String[] args) { | |
System.exit(SpringApplication.exit(SpringApplication.run(BatchApplication.class, args))); | |
} | |
} |
L’écriture de la configuration des beans JobExplorer et JobLauncherCommandLineRunner n’est pas nécessaire dans le cas de projets mono job et mono datasource.
Test du runner à vide
package com.giovanetti.sample.batch.job; | |
import com.giovanetti.support.batch.configuration.GenericTestConfiguration; | |
import com.giovanetti.support.batch.rule.BatchProperties; | |
import org.junit.Before; | |
import org.junit.ClassRule; | |
import org.junit.Rule; | |
import org.junit.Test; | |
import org.junit.contrib.java.lang.system.ExpectedSystemExit; | |
import org.junit.contrib.java.lang.system.ProvideSystemProperty; | |
import org.junit.rules.TemporaryFolder; | |
import org.springframework.batch.core.launch.support.ExitCodeMapper; | |
import java.io.IOException; | |
public class BatchApplicationAlimentationIT { | |
private final static String FUNCTIONAL_SCRIPT = "schema-functional.sql"; | |
@ClassRule | |
public final static BatchProperties batchProperties = BatchProperties.getDefault(); | |
@ClassRule | |
public static ProvideSystemProperty systemProperty = new ProvideSystemProperty("job.name", JobAlimentationConfiguration.JOB_NAME); | |
@Rule | |
public TemporaryFolder temporaryFolder = new TemporaryFolder(); | |
/** | |
* @see <a href="http://www.stefan-birkner.de/system-rules/index.html">System Rules</a> | |
*/ | |
@Rule | |
public final ExpectedSystemExit exit = ExpectedSystemExit.none(); | |
@Before | |
public void before() { | |
GenericTestConfiguration.buildFunctionalDataSource(FUNCTIONAL_SCRIPT); | |
GenericTestConfiguration.buildTechnicalDataSource(); | |
} | |
@Test | |
public void run() throws IOException { | |
exit.expectSystemExitWithStatus(ExitCodeMapper.JVM_EXITCODE_COMPLETED); | |
BatchApplication.main( | |
new String[]{JobAlimentationConfiguration.INPUT_FILE_PARAMETER + "=" + temporaryFolder.newFile() | |
.getPath()}); | |
} | |
@Test(expected = IllegalStateException.class) | |
public void run_SiParametreInvalide_AlorsExitWithError() { | |
BatchApplication.main(new String[]{}); | |
} | |
} |
La librairie system rule permet de gérer de manière simple et fiable les propriétés système et les codes de sortie.
Packager le livrable jar exécutable
maven clean package
Pré-requis d’environnement pour exécuter le job d’alimentation
- Une datasource technique
- Une datasource fonctionnelle
- Un fichier de données csv
- Un fichier de propriétés contenant les clés
- ds.technical.driverclassname
- ds.technical.url
- ds.technical.username
- ds.technical.password
- ds.functional.driverclassname
- ds.functional.url
- ds.functional.username
- ds.functional.password
- commit.interval
Exécuter le jar
avec maven
mvn spring-boot:run -Drun.arguments=input.file.path=alimentation.csv
plus les propriétés de VM -Dbatch.properties.path=file:batch.properties -Djob.name=alimentationJob
ou bien directement le jar
java -Dbatch.properties.path=file:batch.properties -Djob.name=alimentationJob -jar springbatch-sample.jar input.file.path=alimentation.csv
Voir la documentation de référence spring pour les détails des jar exécutable Spring Boot.