Změna verze Javy z 8 na 11

Nedávno jsem prováděl změny verze Javy na projektu, který používá Spring Boot. Měnil jsem verzi Javy z verze 8 na verzi 11. Nebylo to až tak strašné, jak jsem čekal (spíše naopak), přesto se ale některé komplikace objevily. V tomto příspěvku budu postupovat od chyby k chybě, tak jak se u mě objevovaly.

Číst dálZměna verze Javy z 8 na 11

Spring Boot a Spring Batch – jednoduchý job

V tomto příspěvku budeme pokračovat v tomto projektu. Aktuálně máme prázdny projekt v Eclipse IDE, který lze spustil. Přidáme si do něj jednoduchý job, který bude mazat soubor. Pro definování nových tříd (beans) použijeme java configuraci.

BeanConfig.java

import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import cz.vitfo.batch.processing.DeleteFileTasklet;

@Configuration
@EnableBatchProcessing
@ComponentScan("cz.vitfo")
public class BeanConfig {

	@Autowired
	private JobBuilderFactory jobBuilderFactory;
	
	@Autowired
	private StepBuilderFactory stepBuilderFactory;
	
	@Autowired
	private DeleteFileTasklet deleteFileTasklet;
	
	@Bean	
	public Job myJob() {
		return jobBuilderFactory.get("myJob")
			.start(deleteFileFromDirectoryStep())
			.build();
	}
	
	@Bean
	public Step deleteFileFromDirectoryStep() {
		return stepBuilderFactory.get("deleteFileFromDirectoryStep")
			.tasklet(deleteFileFromDirectoryTasklet())
			.build();
	}
	
	@Bean
	public Tasklet deleteFileFromDirectoryTasklet() {
		return deleteFileTasklet;
	}
}
  • Anotace @Configuration označuje tuto třídu jako tu, která obsahuje jednu nebo více anotací @Bean. Pokud na tuto anotaci Spring narazí, vytvoří beanu (třídu) dle definice. Zkráceně řečeno anotace @Configuration označuje třídu, kde se nachází konfigurace.
  • Anotace @EnableBatchProcessing za nás vytvoří a inicializuje beany (třídy) jako například JobBuilderFactory nebo StepBuilderFactory, které budeme používat pro vytvoření jobu.
  • Anotace @ComponentScan říká Springu, kde má hledat třídy, ze kterých má vytvořit beany. Spring pak prochází zadanou cestu a hledá třídy s anotacemi.

Číst dálSpring Boot a Spring Batch – jednoduchý job

Spring Boot a Spring Batch – vytvoření projektu

V tomto příspěvku si vytvoříme Spring Boot projekt se Spring Batch. V dalších číslech pak budeme s tímto projektem pokračovat. Nejdříve si nechte vygenerovat projekt pomocí Spring Initializr. Přidejte závislost na Batch.

spring_boot_batch_initializr

Stáhněte si zip s vygenerovaným projektem, rozbalte jej a naimportujte do Eclipse IDE (File -> Import… -> Maven -> Existing Maven Projects -> najít rozbalený projekt -> Finish). Projekt spusťte (pravým na projekt -> Run As -> Java Application, nebo pokud používáte STS tak Spring Boot App). Měli byste dostat následující chybu:

Error starting ApplicationContext. To display the auto-configuration report re-run your application with 'debug' enabled.
2017-09-05 13:49:01.364 ERROR 14088 --- [           main] o.s.b.d.LoggingFailureAnalysisReporter   :

***************************
APPLICATION FAILED TO START
***************************

Description:

Cannot determine embedded database driver class for database type NONE

O této chybě již byla řeč v tomto příspěvku. Přidejte tedy do pomu závislost:

<dependency>
	<groupId>org.hsqldb</groupId>
	<artifactId>hsqldb</artifactId>
</dependency>

Pokud projekt spustíte nyní, vše by mělo proběhnout v pořádku.

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.5.6.RELEASE)

2017-09-05 13:53:11.461  INFO 12504 --- [           main] c.v.s.SpringBootBatchApplication         : Starting SpringBootBatchApplication on W2AB00HL with PID 12504 (C:\Users\jd99517\Documents\STS\WorkspaceSpringBatch\spring_boot_batch\spring_boot_batch\target\classes started by JD99517 in C:\Users\jd99517\Documents\STS\WorkspaceSpringBatch\spring_boot_batch\spring_boot_batch)
2017-09-05 13:53:11.464  INFO 12504 --- [           main] c.v.s.SpringBootBatchApplication         : No active profile set, falling back to default profiles: default
2017-09-05 13:53:11.510  INFO 12504 --- [           main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@3bbc39f8: startup date [Tue Sep 05 13:53:11 CEST 2017]; root of context hierarchy
2017-09-05 13:53:12.240  INFO 12504 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2017-09-05 13:53:12.250  INFO 12504 --- [           main] c.v.s.SpringBootBatchApplication         : Started SpringBootBatchApplication in 1.049 seconds (JVM running for 1.64)
2017-09-05 13:53:12.250  INFO 12504 --- [       Thread-2] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@3bbc39f8: startup date [Tue Sep 05 13:53:11 CEST 2017]; root of context hierarchy
2017-09-05 13:53:12.251  INFO 12504 --- [       Thread-2] o.s.j.e.a.AnnotationMBeanExporter        : Unregistering JMX-exposed beans on shutdown

DataSource pro JobRepository

JobRepository je používána pro základní CRUD (Create, Read, Update, Delete) operace nad určitými objekty (domain objekty) frameworku Spring Batch. Jednoduše řečeno, Spring Batch si zde ukládá potřebné informace o jobech (Job) a jednotlivých krocích jobu (Step). V případě, že tyto informace nepotřebujeme, nabízí Spring Batch in-memory implementaci pomocí mapy. Tento způsob jsem zatím používal ve všech předchozí Spring Batch příkladech.

<bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean">
	<property name="transactionManager" ref="transactionManager" />
</bean>

V případě, že chceme batch objekty ukládat, je potřeba nadefinovat data source. Zde je příklad pro databázi PostgreSQL.

<beans:bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
	<beans:property name="driverClassName" value="org.postgresql.Driver" />
	<beans:property name="url" value="jdbc:postgresql://localhost:5432/database" />
	<beans:property name="username" value="username" />
	<beans:property name="password" value="password" />
</beans:bean>

Za database, username, password zadejte validní data.

Pokud máte novou databázi, určitě v ní nebudete mít vytvořeny potřebné tabulky:

  • BATCH_STEP_EXECUTION
  • BATCH_JOB_EXECUTION_CONTEXT
  • BATCH_JOB_EXECUTION_PARAMS
  • BATCH_JOB_INSTANCE
  • BATCH_JOB_EXECUTION
  • BATCH_STEP_EXECUTION_CONTEXT

spring_batch_tables

Číst dálDataSource pro JobRepository

Spring Batch a Java konfigurace

Doposud jsem v příspěvcích k Spring Batch používal xml konfiguraci. Spring ale umožňuje též konfigurovat pomocí java tříd. V tomto příspěvku ukáži jednoduchý Spring Batch projekt, který bude používat java config. Projekt bude stejný jako v tomto příspěvku, pouze bude místo xml používat java konfiguraci.

Struktura projektu

│   pom.xml
│
└───src
    ├───main
    │   └───java
    │       └───batch
    │               AppConfig.java
    │               Employee.java
    │               ExecuteBatchJob.java
    │               ResultWriter.java
    │
    └───resources
            reportCS.csv

AppConfig.java předstaje java třídu obsahující konfiguraci.

Číst dálSpring Batch a Java konfigurace

Načtení csv souboru pomocí FlatFileItemReader ve Spring Batch

Tento příspěvek volně navazuje na článek Spring Batch 3. díl, ve kterém byla ukázána práce s chunk a definovanými objekty reader, processor, writer. V dnešním příspěvku se seznámíme s tím, jak načítat data z .csv souboru pomocí FlatFileItemReaderu. FlatFileItemReader je ItemReader, který čte řádky ze vstupu. Vstup je určen elementem <property name="resource" value="classpath:reportCS.csv"/>. V tomto případě se bude číst ze souboru reportCS.csv, který se nachází na classpath.

Prostý databázový soubor (též plochý databázový soubor, anglicky flat file database) je jednoduchá databáze (většinou tabulka) uložená v textovém souboru ve formě prostého textu. Takový soubor může mít příponu například .txt, .ini, .conf, ale i .dbf apod. Zdroj: cs.wikipedia.org

Zde je ukázka FlatFileItemReaderu.

<bean id="csvFileItemReader" class="org.springframework.batch.item.file.FlatFileItemReader">
	<property name="resource" value="classpath:reportCS.csv"/>
	<property name="encoding" value="UTF-8"/>
	<property name="linesToSkip" value="1"/>
	<property name="lineMapper">
		<bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
			<property name="lineTokenizer">
				<bean class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
					<property name="names" value="Name,Surname,Location,Job,Earning"/>
				</bean>
			</property>
			<property name="fieldSetMapper">
				<bean class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
					<property name="prototypeBeanName" value="employee"></property>
				</bean>
			</property>
		</bean>
	</property>
</bean>

resource: zdroj dat pro čtení
encoding: kódování zdrojového souboru
linesToSkip: nastaví počet řádků souboru, které se mají přeskočit před začátkem čtení
lineMapper: nastaví line mapper, mapování načteného řádku

Číst dálNačtení csv souboru pomocí FlatFileItemReader ve Spring Batch

Spring Batch 3. díl

V minulém díle jsme si vytvořili job, který obsahoval několik kroků (Step). Každému kroku byl přiřazen jeden tasklet, který implementoval celou funkcionalitu. Dost často ale krok v jobu bude načítat data, zpracovávat je a nakonec zapisovat. Pro tento případ je tady chunk. V rámci chunku je možné definovat reader, processor a writer.

<chunk reader="myReader" writer="myWriter" processor="myProcessor">

V následujícím příkladu si ukážeme job, který se bude skládat z jediného kroku, během kterého zavoláme náš reader, poté data vrácená z readeru zpracujeme ve vlastním processoru a nakonec data zapíšem writerem.

context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/batch"
	xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-3.0.xsd">
	
	<beans:bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean">
		<beans:property name="transactionManager" ref="transactionManager" />
	</beans:bean>
	
	<beans:bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
		<beans:property name="jobRepository" ref="jobRepository" />
	</beans:bean>
	
	<beans:bean id="transactionManager" class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />
</beans:beans>

job.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/batch"
	xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
						http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
						http://www.springframework.org/schema/batch
						http://www.springframework.org/schema/batch/spring-batch-3.0.xsd">
	<beans:import resource="context.xml" />
	
	<beans:bean id="myReader" class="cz.vitfo.MyReader"/>
	<beans:bean id="myProcessor" class="cz.vitfo.MyProcessor"/>
	<beans:bean id="myWriter" class="cz.vitfo.MyWriter"/>
	
	<job id="myJob">
		<step id="myStep">
			<tasklet>
				<chunk reader="myReader" writer="myWriter" processor="myProcessor" commit-interval="4"/>
			</tasklet>
		</step>
	</job>
</beans:beans>

Třída MyInput představuje objekty, které budou načítány (vraceny) readerem.

Číst dálSpring Batch 3. díl

Spring Batch 2. díl

V předchozím díle jsme se věnovali představení frameworku String Batch, vysvětlení základních pojmů a ukázali jsme si jednoduchý projekt. V tomto díle vytvoříme další projekt.

Job je posloupnost jednotlivých kroků (Step). Krok pak obsahuje úkol (Tasklet), který popisuje (implementuje) co se má dělat. Task v sobě ještě může obsahovat další kousek (Chunk), ale tomu se budemě věnovat někdy příště.

Vaříme čaj se Spring Batch.
V následujícím příkladu si ukážeme job, který se bude skládat s pěti kroků (Tasklet). Budeme připravovat čaj.

  1. naplníme konvici vodou
  2. zapneme konvici
  3. dáme do konvice čaj
  4. počkáme tři minuty
  5. hotový čaj naservírujeme

Struktura projektu

│   pom.xml
│
└───src
    ├───main
    │   └───java
    │       └───cz
    │           └───vitfo
    │               │   ExecuteJob.java
    │               │
    │               └───tasklets
    │                       FillKettleWithWater.java
    │                       PutTeaToWater.java
    │                       ServeTea.java
    │                       TurnOnKettle.java
    │                       WaitThreeMinutes.java
    │
    └───resources
            context.xml
            job.xml


Všimněte si, že ve složce resources máme dva xml. Spring umožňuje rozdělit konfiguraci do více souborů. Jednou z možností je potom pro vytvoření kontextu předat list konfiguračních souboru (ukázáno v tomto příspěvku) a nebo importovat jeden konfigurační soubor do druhého pomocí elementu <import> jak je ukázáno v tomto příkladu (řádek osm v souboru job.xml).

Číst dálSpring Batch 2. díl

Spring Batch 1. díl

Spring Batch je open source framework pro dávkové zpracování (batch processing). Dávkové zpracování znamená vykonání série programů bez účasti uživatele. Většinou se jedná o akce, které se vykonávají pravidelně v určitý čas jako například vygenerování statistik ke konci měsíce, zjištění změn za poslední týden (např. nové příspěvky za toto období), zpracování výkazů ke konci dne, týdne, měsíce, …

Základní pojmy ve Spring Batch

  • JobLauncher: Vstupní místo, kde se spouští job.
  • Job: Definuje co se má provést (definuje dávku). Každý job může obsahovat několik kroků (Step).
  • Step: Část jobu, jeden z kroků při provádění (jeden z kroků dávky). Step obsahuje atribut next, který udává, jaký další step se má vykonat po tom aktuálním. Step obsahuje Tasklet.
  • Tasklet: Je částí kroku a definuje, co se má provést (reference na třídu implementující rozhraní org.springframework.batch.core.step.tasklet.Tasklet) a nebo může ještě obsahovat Chunk.
  • Chunk: Nejmenší část, ve které se určuje co se má načíst (Reader), jak to zpracovat (Processor) a co má být výstupem (Writer).

Jak vypadá typický job

  • načtení dat (z databáze, ze souboru, …)
  • zpracování dat (výpočty, validace, filtrování, …)
  • zápis dat (do databáze, do různých typů souborů – pdf, doc, txt, …)

Jednoduchý projekt

V následujícím jednoduchém projektu vytvoříme a spustíme job, který bude obsahovat jediný Step (a ten jediný Tasklet), který pouze vypíše text.

Zde je struktura projektu. Jedná se Maven projekt.

│   pom.xml
│
└───src
    ├───main
    │   └───java
    │       └───cz
    │           └───vitfo
    │                   ExecuteJob.java
    │                   MyTasklet.java
    │
    └───resources
            context.xml

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>cz.vito</groupId>
	<artifactId>springbatch</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>springbatch</name>
	<url>http://maven.apache.org</url>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<spring.framework.version>3.2.1.RELEASE</spring.framework.version>
		<spring.batch.version>3.0.2.RELEASE</spring.batch.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.batch</groupId>
			<artifactId>spring-batch-core</artifactId>
			<version>${spring.batch.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.batch</groupId>
			<artifactId>spring-batch-infrastructure</artifactId>
			<version>${spring.batch.version}</version>
		</dependency>
	</dependencies>
</project>

Do pom.xml stačí přidat pouze dvě závislosti a to na org.springframework.batch a spring-batch-infrastructure.

context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/batch"
	xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
						http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
						http://www.springframework.org/schema/batch 
						http://www.springframework.org/schema/batch/spring-batch-3.0.xsd">
		
	<!-- Beans definition -->	
	<beans:bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean">
		<beans:property name="transactionManager" ref="transactionManager" />
	</beans:bean>
	
	<beans:bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
		<beans:property name="jobRepository" ref="jobRepository" />
	</beans:bean>
	
	<beans:bean id="transactionManager" class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />
	
	<beans:bean id="myTasklet" class=" cz.vitfo.MyTasklet" />
	
	
	<!-- Job definition -->
	<job id="myJob">
		<step id="myStep">
			<tasklet ref="myTasklet"></tasklet>
		</step>
	</job>
</beans:beans>

Job launcher potřebuje mít definovánu job repository, do které si ukládá údaje o jobech. Job repository potřebuje mít definován transaction manager. Job má definován Step a ten Tasklet. Tasklet odkazuje na třídu implementující rozhraní Tasklet.

MyTasklet.java

package cz.vitfo;

import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;

public class MyTasklet implements Tasklet {

	public RepeatStatus execute(StepContribution arg0, ChunkContext arg1) throws Exception {
		System.out.println("Code in " + this.getClass().getSimpleName() + " is executed.");
		return null;
	}
}

Třída, která implementuje rozhraní Tasklet. Jediným jejím úkolem je vypsat text.

ExecuteJob.java

package cz.vitfo;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ExecuteJob {

	public static void main(String[] args) {
		ApplicationContext context = new ClassPathXmlApplicationContext("context.xml");
		
		JobLauncher jobLauncher = (JobLauncher) context.getBean("jobLauncher");
		Job job = (Job) context.getBean("myJob");
		
		try {
			JobExecution execution = jobLauncher.run(job, new JobParameters());
			System.out.println("Exit Status: " + execution.getStatus());
			System.out.println("Start: " + execution.getStartTime());
			System.out.println("End: " + execution.getEndTime());
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (context != null) {
				context = null;
			}
		}
		System.out.println("Done");
	}
}

Nejdříve načteme kontext. Poté z něj získáme JobLauncher a Job. Tento Job spustíme pomocí JobLauncher a vypíšeme status jobu po spuštění.

Výsledek

Code in MyTasklet is executed.
Exit Status: COMPLETED
Start: Tue Nov 01 17:26:15 CET 2016
End: Tue Nov 01 17:26:15 CET 2016
Done