JobLauncher, SimpleJobLauncher

spring framework/spring-batch 2012.08.28 15:43 posted by dev.bistro

휴가중(진짜 심심해서 쓰는 ) 포스팅. 작성중


JobLauncher 의 유일한 구현체 SimpleJobLauncher이다.

1. 유일한 public method 'run'을 보면 Job , JobParameters 를 아귀먼트로 받는것을 확인할 수 이고,  return 은  JobExecution이다.

2. JobExecution은 Entity를 상속받는 상태를 나타내는 클래스라 생각해도 된다.



/*
 * Copyright 2006-2008 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.batch.core.launch.support;

/**
 * Simple implementation of the {@link JobLauncher} interface. The Spring Core
 * {@link TaskExecutor} interface is used to launch a {@link Job}. This means
 * that the type of executor set is very important. If a
 * {@link SyncTaskExecutor} is used, then the job will be processed
 * within the same thread that called the launcher. Care should
 * be taken to ensure any users of this class understand fully whether or not
 * the implementation of TaskExecutor used will start tasks synchronously or
 * asynchronously. The default setting uses a synchronous task executor.
 * 
 * There is only one required dependency of this Launcher, a
 * {@link JobRepository}. The JobRepository is used to obtain a valid
 * JobExecution. The Repository must be used because the provided {@link Job}
 * could be a restart of an existing {@link JobInstance}, and only the
 * Repository can reliably recreate it.
 * 
 * @author Lucas Ward
 * @Author Dave Syer
 * 
 * @since 1.0
 * 
 * @see JobRepository
 * @see TaskExecutor
 */

public class SimpleJobLauncher implements JobLauncher, InitializingBean {
	protected static final Log logger = LogFactory.getLog(SimpleJobLauncher.class);
	private JobRepository jobRepository;
	private TaskExecutor taskExecutor;

	/**
	 * Run the provided job with the given {@link JobParameters}. The
	 * {@link JobParameters} will be used to determine if this is an execution
	 * of an existing job instance, or if a new one should be created.
	 * 
	 * @param job the job to be run.
	 * @param jobParameters the {@link JobParameters} for this particular
	 * execution.
	 * @return JobExecutionAlreadyRunningException if the JobInstance already
	 * exists and has an execution already running.
	 * @throws JobRestartException if the execution would be a re-start, but a
	 * re-start is either not allowed or not needed.
	 * @throws JobInstanceAlreadyCompleteException if this instance has already
	 * completed successfully
	 * @throws JobParametersInvalidException
	 */

	public JobExecution run(final Job job, final JobParameters jobParameters)
			throws JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException,
			JobParametersInvalidException {

		Assert.notNull(job, "The Job must not be null.");
		Assert.notNull(jobParameters, "The JobParameters must not be null.");

		final JobExecution jobExecution;
		JobExecution lastExecution = jobRepository.getLastJobExecution(job.getName(), jobParameters);
		if (lastExecution != null) {
			if (!job.isRestartable()) {
				throw new JobRestartException("JobInstance already exists and is not restartable");
			}
		}

		// Check the validity of the parameters before doing creating anything
		// in the repository...
		job.getJobParametersValidator().validate(jobParameters);

		/*
		 * There is a very small probability that a non-restartable job can be
		 * restarted, but only if another process or thread manages to launch
		 * and fail a job execution for this instance between the last
		 * assertion and the next method returning successfully.
		 */

		jobExecution = jobRepository.createJobExecution(job.getName(), jobParameters);

		try {
			taskExecutor.execute(new Runnable() {

				public void run() {
					try {
						logger.info("Job: [" + job + "] launched with the following parameters: [" + jobParameters
								+ "]");
						job.execute(jobExecution);
						logger.info("Job: [" + job + "] completed with the following parameters: [" + jobParameters
								+ "] and the following status: [" + jobExecution.getStatus() + "]");
					}
					catch (Throwable t) {
						logger.info("Job: [" + job
								+ "] failed unexpectedly and fatally with the following parameters: [" + jobParameters
								+ "]", t);
						rethrow(t);
					}
				}

				private void rethrow(Throwable t) {
					if (t instanceof RuntimeException) {
						throw (RuntimeException) t;
					}
					else if (t instanceof Error) {
						throw (Error) t;
					}
					throw new IllegalStateException(t);
				}
			});
		}
		catch (TaskRejectedException e) {
			jobExecution.upgradeStatus(BatchStatus.FAILED);
			if (jobExecution.getExitStatus().equals(ExitStatus.UNKNOWN)) {
				jobExecution.setExitStatus(ExitStatus.FAILED.addExitDescription(e));
			}
			jobRepository.update(jobExecution);
		}

		return jobExecution;
	}

	public void setJobRepository(JobRepository jobRepository) {
		this.jobRepository = jobRepository;
	}

	public void setTaskExecutor(TaskExecutor taskExecutor) {
		this.taskExecutor = taskExecutor;
	}

	public void afterPropertiesSet() throws Exception {
		Assert.state(jobRepository != null, "A JobRepository has not been set.");
		if (taskExecutor == null) {
			logger.info("No TaskExecutor has been set, defaulting to synchronous executor.");
			taskExecutor = new SyncTaskExecutor();
		}
	}

}

---
public class JobExecution extends Entity {
	private JobInstance jobInstance;
	private volatile Collection stepExecutions = new CopyOnWriteArraySet();
	private volatile BatchStatus status = BatchStatus.STARTING;
	private volatile Date startTime = null;
	private volatile Date createTime = new Date(System.currentTimeMillis());
	private volatile Date endTime = null;
	private volatile Date lastUpdated = null;
	private volatile ExitStatus exitStatus = ExitStatus.UNKNOWN;
	private volatile ExecutionContext executionContext = new ExecutionContext();
	private transient volatile List failureExceptions = new CopyOnWriteArrayList();

       하략 ... 대부분 생성자/Get/Set



참고#1: http://chanwook.tistory.com/797

요약 : 4년전 교육 받았을때도 느꼈지만... 박찬욱님 짱 :)

'spring framework > spring-batch' 카테고리의 다른 글

JAXP DOM4J, JDOM2의 성능 비교  (0) 2013.11.13
Spring Batch Admin  (0) 2013.01.11
JobLauncher, SimpleJobLauncher  (0) 2012.08.28
JobParametersIncrementer의 사용.  (0) 2012.08.25


Job Parameter를 자동으로 Increment 하기 위해서 사용.

(Job의 구현체를 구분하는 방법은 Job + Job Parameter이기 때문에, Parameter없이는 같은 job을 계속 실행하면 안된다)





@Component
public class SimpleJobParametersIncrementer implements JobParametersIncrementer {

	static final SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd-hhmmss");
	public JobParameters getNext(JobParameters parameters) {
		String id = format.format(new Date());
		return new JobParametersBuilder().addString("run.id", id).toJobParameters();
	}

}
	
        
	    
	

	
		


요약
1. JobParametersIncrementer 구현
2. JobExplorerFactoryBean bean 설정
3. job에 increment로 1번 설정
4. 실행은 xmlfile jobanme [-next] 를 추가

참고

http://numberformat.wordpress.com/2010/02/07/multiple-batch-runs-with-spring-batch/


참고2 [BATCH_JOB_PARAMS] 테이블

JOB_INSTANCE_ID TYPE_CD KEY_NAME STRING_VAL DATE_VAL LONG_VAL DOUBLE_VAL

3 STRING run.id 20120825-124631 1970-01-01 09:00:00 0 0

4 STRING run.id 20120825-125909 1970-01-01 09:00:00 0 0


참고3. 기본적으로 제공하는 crementer는

org.springframework.batch.core.launch.support.RunIdIncrementer 가 있고 key를 프로퍼티로 받는다. 

https://fisheye.springsource.org/browse/spring-batch/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/RunIdIncrementer.java?r=488ba7a37b70c62a2ac40d6c945ea940408add80


'spring framework > spring-batch' 카테고리의 다른 글

JAXP DOM4J, JDOM2의 성능 비교  (0) 2013.11.13
Spring Batch Admin  (0) 2013.01.11
JobLauncher, SimpleJobLauncher  (0) 2012.08.28
JobParametersIncrementer의 사용.  (0) 2012.08.25

Play framework의 Database는 Evolutions scripts 에 의해서 startup/shutdown에 액션을 취할수 있다. plain SQL로 작성되고 conf/evolutions/{database name} 위치에 있다. 1.sql이 실행되고 2.sql이 있으면 수행되는 형식이다.

막 이전에 yaml 파일과 GlobalSetting를 상속받아서 테스트 데이터를 입력하였는데 거기에 scripts로 추가적으로 테스트 데이터를 입력해보도록 하자.


현재 포스팅을 하고 있는 대상 tutorial 웹어플리케이션의 default database name은 [default] 이다.

application.conf

# Database configuration

# ~~~~~ 

# You can declare as many datasources as you want.

# By convention, the default datasource is named `default`

#

db.default.driver=org.h2.Driver

db.default.url="jdbc:h2:mem:play"




현재 우리 프로젝트의 폴더 모습은 위와 같다. 자동으로 생성된 폴더와 1.sql도 보일것이다. 이런거 만든적이 없다고 거짓말 하지 마라. 바로 이전 포스팅에서 Apply this script now! 를 눌렀잖아?

저기에 2.sql을 만들어 보자. (2개의 테스트 데이터를 넣는 sql문이다)

# Sample data 대충 요롷게

# --- !Ups

insert into message (id, content)  values  (5, 'test message'), (6, 'last message');

# --- !Downs





위의 모습처럼 [허용해줘!버튼]을 볼 수 있다. 이러한 스크립트는 데이터를 넣을때도 유용할 수 있지만, 협업하는 개발자가 같은 테이블임에도 DB스키마다 다르게 작업하는등의 이슈가 있을때 사용할 수 있겠지?


여기서 궁금한게 있지 않나? 1.sql 다음에 10.sql을 쓰면 과연 수행이 될것인가? 2.sql을 10.sql로 변경하고 Play Command Line에서 reboot를 입력한후 run하였다. 하지만 10.sql은 수행되지 않는다.

첫DB는 default 이르을 가져야 하고, 1.sql 뒤에 2.sql 의 순서대로 script는 실행된다. (라고 난 확인했다~)

Evolutions scripts를 수행하는 native code를 분석하면 좀 더 명확하게 알수 있겠지만, 난 지금 play framework의 사용법을 배우는 중이라서 그건 니네들이 알아서 보자.-_-z





티스토리 툴바