Spring Bootのwebアプリとバッチ処理をひとつにまとめる

Dockerで運用しているSpring Bootでできたwebアプリに、あらたにバッチ処理が必要になった。
webアプリと同じサーバで実行するので、同一のjarでwebアプリもバッチ処理も行えるようにしてみた。
仕組みとしては、javaコマンドでjarを実行するときに目的のAppクラスを指定出来るようになっている。

# ビルドの設定

```pom.xml
<build>
	<plugins>
		<plugin>
			<groupId>org.apache.maven.plugins</groupId>
			<artifactId>maven-jar-plugin</artifactId>
			<configuration>
				<archive>
					<manifest>
						<mainClass>sandbox.Entrypoint</mainClass>
					</manifest>
				</archive>
			</configuration>
		</plugin>
		<plugin>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-maven-plugin</artifactId>
			<configuration>
				<classifier>exec</classifier>
			</configuration>
		</plugin>
	</plugins>
</build>
```

mavenでjarをビルドする時に、javaコマンドで実行されるクラスが指定する。
このクラスで、引数に応じて目的のAppクラスを切り替えられるようにする。

# アプリの基底クラス

```java:AppBase.java
@Configuration
public abstract class AppBase {
	
	@Autowired
	DataSource dataSource;

	@Autowired
	Dialect dialect;

	@Autowired
	Naming naming;

}
```

webアプリ/バッチ処理で共通して利用するデータソースの設定などを行う。

# webアプリ

```java:WebApp.java
@EnableAutoConfiguration
@ComponentScan
public class WebApp extends AppBase {
	
	public static void main(String[] args) {
		SpringApplication.run(WebApp.class, args);
	}

}
```

元のAppクラスから、わかりやすいように名称を変更した。
特に変わったことは行わない。

# バッチ処理

```java:Batch.java
@EnableBatchProcessing
@EnableAutoConfiguration
@ComponentScan
public class Batch extends AppBase {

	@Autowired
	JobBuilderFactory jobBuilderFactory;

	@Autowired
	StepBuilderFactory stepBuilderFactory;

	@Autowired
	Crawler crawler;
	
	public static void main(String[] args) {
		SpringApplication application = new SpringApplication(Batch.class);
		application.setWebEnvironment(false);
		System.exit(SpringApplication.exit(application.run(args)));
	}

	@Bean
	public Step crawl() {
		return stepBuilderFactory.get("crawl").tasklet(new Tasklet() {
			public RepeatStatus execute(StepContribution stepContribution,
					ChunkContext chunkContext) throws Exception {
				crawler.crawl();
				return RepeatStatus.FINISHED;
			}
		}).build();
	}

	@Bean
	public Job job(Step crawl) throws Exception {
		return jobBuilderFactory.get("site-crawl")
				.incrementer(new RunIdIncrementer()).start(crawl).build();
	}

}
```

新たに実装したAppクラス。
実行時に不要なTomcatが起動しないようにしている。

# エンドポイント

```java:Entrypoint.java
public class Entrypoint {

	public static void main(String[] args) throws Exception {
		if (args != null && args.length > 0 && "web".equals(args[0])) {
			WebApp.main(args);
			return;
		}

		Batch.main(args);
	}

}
```

引数に応じて目的のAppクラスを切り替えるクラス。
第１引数が「web」の場合にwebアプリを実行するように定義。
新たなバッチ処理が増えた場合は、この引数のパターンを増やして対処が出来ると思う。

# Docker

```Dockerfile
FROM frolvlad/alpine-oraclejdk8:slim
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/var/app.jar"]
```

イメージを作成するDockerfileではjarの実行を指定するのみ。

# 実行

Dockerを実行するサーバの/var/xxx.jarへ、avenでビルドしたjarを配置する。

```run_web.sh
docker run --name site-crawler-web \
  -d \
  -v /etc/localtime:/etc/localtime:ro \
  -v /var/xxx.jar:/var/app.jar \
  -p 80:8080 \
  container \
  web
```

webアプリを実行するときは、引数に「web」を指定する。


```run_batch.sh
docker run --name site-crawler-batch \
  -d \
  -v /etc/localtime:/etc/localtime:ro \
  -v /var/xxx.jar:/var/app.jar \
  container \
  batch
```

バッチを実行するときは、引数に「batch」（「web」以外）を指定する。
バッチ処理が終わるとコンテナが終了する。