1. http://www.techavalanche.com/2011/08/21/spring-batch-hello-world-in-memory/
2. http://www.techavalanche.com/2011/10/23/spring-batch-itemreader-and-itemwriter/
2013년 8월 6일 화요일
Custom Tasklet - with Java
import org.apache.hadoop.util.ToolRunner;
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{
private String 변수1;
private String 변수2;
@Override
public RepeatStatus execute(StepContribution contribution,
ChunkContext chunkContext) throws Exception {
// TODO Auto-generated method stub
int exitCode=0;
String paths[] = 변수지정;
exitCode = ToolRunner.run(new 하둡의드라이버(), paths);
System.exit(exitCode);
return null;
}
public MyTasklet(String 변수1, String 변수2) {
this.변수1 = 변수1;
this.변수2 = 변수2;
}
}
----------------------------------------------------------------------------------------------------------------
이렇게 custom tasklet을 만들고 해당 클래스를 bean으로 만들어서
배치작업에서 사용하면 된다.
빈 설정
<bean id="myCustomTasklet"
class="com.custom.test.MyTasklet">
<constructor-arg type="String" value="변수1" />
<constructor-arg type="String" value="변수2" />
</bean>
배치 작업 설정
<batch:job id="job1">
<batch:step id="step1">
<batch:tasklet ref="myCustomTasklet" />
</batch:step>
</batch:job>
----------------------------------------------------------------------------------------------------------------------
테스트를 위해 자바로 배치 작업을 실행한다면
import java.util.Date;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobInstance;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class RunJob {
public static void main(String[] args) throws Throwable {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("배치작업있는.xml");
ctx.start();
JobLauncher jobLauncher = (JobLauncher) ctx.getBean("jobLauncher");
Job dbJob = (Job) ctx.getBean("job1");
JobExecution jobExecution = jobLauncher.run(dbJob, new JobParametersBuilder().addDate("date", new Date()).toJobParameters());
//JobParameter를 지정해야 작업이 실행된다. 여기서는 날짜를 변수로 넣었고 해당 변수는 사용하지 않았다
JobInstance jobInstance = jobExecution.getJobInstance();
System.out.println("job instance Id: " + jobInstance.getId());
//테스트를 위해 작업 번호를 출력
BatchStatus batchStatus = jobExecution.getStatus();
while(batchStatus.isRunning()) {
System.out.println("Strill running...");
Thread.sleep(10*1000);//10sec
//작업이 진행중임을 확인하기 위한 예제
}
}
}
참고: http://static.springsource.org/spring-hadoop/docs/current/reference/html/fs.html
http://static.springsource.org/spring-hadoop/docs/current/reference/html/hadoop.html#hadoop:tool-runner
http://static.springsource.org/spring-batch/reference/html/configureJob.html#runningJobsFromCommandLine
http://www.mkyong.com/spring-batch/run-spring-batch-job-with-commandlinejobrunner/
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{
private String 변수1;
private String 변수2;
@Override
public RepeatStatus execute(StepContribution contribution,
ChunkContext chunkContext) throws Exception {
// TODO Auto-generated method stub
int exitCode=0;
String paths[] = 변수지정;
exitCode = ToolRunner.run(new 하둡의드라이버(), paths);
System.exit(exitCode);
return null;
}
public MyTasklet(String 변수1, String 변수2) {
this.변수1 = 변수1;
this.변수2 = 변수2;
}
}
----------------------------------------------------------------------------------------------------------------
이렇게 custom tasklet을 만들고 해당 클래스를 bean으로 만들어서
배치작업에서 사용하면 된다.
빈 설정
<bean id="myCustomTasklet"
class="com.custom.test.MyTasklet">
<constructor-arg type="String" value="변수1" />
<constructor-arg type="String" value="변수2" />
</bean>
배치 작업 설정
<batch:job id="job1">
<batch:step id="step1">
<batch:tasklet ref="myCustomTasklet" />
</batch:step>
</batch:job>
----------------------------------------------------------------------------------------------------------------------
테스트를 위해 자바로 배치 작업을 실행한다면
import java.util.Date;
import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobInstance;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class RunJob {
public static void main(String[] args) throws Throwable {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("배치작업있는.xml");
ctx.start();
JobLauncher jobLauncher = (JobLauncher) ctx.getBean("jobLauncher");
Job dbJob = (Job) ctx.getBean("job1");
JobExecution jobExecution = jobLauncher.run(dbJob, new JobParametersBuilder().addDate("date", new Date()).toJobParameters());
//JobParameter를 지정해야 작업이 실행된다. 여기서는 날짜를 변수로 넣었고 해당 변수는 사용하지 않았다
JobInstance jobInstance = jobExecution.getJobInstance();
System.out.println("job instance Id: " + jobInstance.getId());
//테스트를 위해 작업 번호를 출력
BatchStatus batchStatus = jobExecution.getStatus();
while(batchStatus.isRunning()) {
System.out.println("Strill running...");
Thread.sleep(10*1000);//10sec
//작업이 진행중임을 확인하기 위한 예제
}
}
}
참고: http://static.springsource.org/spring-hadoop/docs/current/reference/html/fs.html
http://static.springsource.org/spring-hadoop/docs/current/reference/html/hadoop.html#hadoop:tool-runner
http://static.springsource.org/spring-batch/reference/html/configureJob.html#runningJobsFromCommandLine
http://www.mkyong.com/spring-batch/run-spring-batch-job-with-commandlinejobrunner/
파일 필터링 및 하둡으로 로컬파일 복사
로컬 파일 필터링 출처: http://mudchobo.tistory.com/355
File file=new File("폴더의 경로");
String[] list = file.list(new FilenameFilter()
{
@Override
public boolean accept(File dir, String name)
{
return name.startsWith("My.Log");
}
});
My.Log로 시작하는 파일을 주어진 폴더(경로)에서 찾아 이름만 반환한다.
for (int i = 0; i < list.length; i++)
{
localSrc="폴더의경로".concat(list[i]);
dst = "복사할경로".concat(list[i]);
in = new BufferedInputStream(new FileInputStream(localSrc));
fs = FileSystem.get(URI.create(dst), conf);
if(fs.exists(new Path(dst))) {
fs.delete(new Path(dst), true);
}
out = fs.create(new Path(dst), new Progressable() {
//64kb 의 데이터 패킷이 데이터 노드 파이프라인에 쓰일 때 마다 하둡에 의해 progress() 메소드가 호출, 마침표를 프린트
public void progress(){
System.out.print(".");
}
});
IOUtils.copyBytes(in,out,4096,true);
}
폴더의 경로에 파일 이름을 붙여서 파일의 경로를 만들고 복사할 경로에 파일 이름을 붙여서 복사할 위치의 경로를 만들었다.
그리고 IOUtils를 이용해서 출력
참고: http://byulbada.egloos.com/2232184 (BufferedInput/Output)
http://k.daum.net/qna/openknowledge/view.html?qid=40E1d (파일 필터링)
File file=new File("폴더의 경로");
String[] list = file.list(new FilenameFilter()
{
@Override
public boolean accept(File dir, String name)
{
return name.startsWith("My.Log");
}
});
My.Log로 시작하는 파일을 주어진 폴더(경로)에서 찾아 이름만 반환한다.
for (int i = 0; i < list.length; i++)
{
localSrc="폴더의경로".concat(list[i]);
dst = "복사할경로".concat(list[i]);
in = new BufferedInputStream(new FileInputStream(localSrc));
fs = FileSystem.get(URI.create(dst), conf);
if(fs.exists(new Path(dst))) {
fs.delete(new Path(dst), true);
}
out = fs.create(new Path(dst), new Progressable() {
//64kb 의 데이터 패킷이 데이터 노드 파이프라인에 쓰일 때 마다 하둡에 의해 progress() 메소드가 호출, 마침표를 프린트
public void progress(){
System.out.print(".");
}
});
IOUtils.copyBytes(in,out,4096,true);
}
폴더의 경로에 파일 이름을 붙여서 파일의 경로를 만들고 복사할 경로에 파일 이름을 붙여서 복사할 위치의 경로를 만들었다.
그리고 IOUtils를 이용해서 출력
참고: http://byulbada.egloos.com/2232184 (BufferedInput/Output)
http://k.daum.net/qna/openknowledge/view.html?qid=40E1d (파일 필터링)
2013년 8월 5일 월요일
crontab 사용 및 배치 스크립트
배치 스크립트 예제: http://www.lucasward.net/2010/07/spring-batch-deployment-example.html
#!/bin/bash CP=실행할xml이있는폴더/ LIB=프로젝트의라이브러리들이있는폴더/* for f in $LIB do CP=$CP:$f done java -cp $CP org.springframework.batch.core.launch.support.CommandLineJobRunner \ 예제잡파일.xml 예제잡이름
크론탭 사용 예제: http://www.cyberciti.biz/faq/how-do-i-add-jobs-to-cron-under-linux-or-unix-oses/
* * * * * command to be executed - - - - - | | | | | | | | | ----- Day of week (0 - 7) (Sunday=0 or 7) | | | ------- Month (1 - 12) | | --------- Day of month (1 - 31) | ----------- Hour (0 - 23) ------------- Minute (0 - 59)
XML 파일에 스크립트를 삽입시켜 사용할 수 있다.
<hdp:script id="adSetUp" language="javascript" run-at-startup="false">
//importPackage(java.util);
inputPath = "/로컬파일주소"
outputPath="/hdfs로 복사할 주소"
if(fsh.test(outputPath)) {
fsh.rmr("file://".concat(outputPath))
}
if(fsh.test(inputPath)) {
fsh.get(inputPath,outputPath)
}
</hdp:script>
이 스크립트는 스크립트 id를 job id처럼 호출 하면 된다. 혹은 run-at-startup 항목을 true로 하면 자동 실행된다.
Unable to locate Spring NamespaceHandler for XML schema namespace
org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Unable to locate Spring NamespaceHandler for XML schema namespace [http://www.springframework.org/schema/context]
Offending resource: class path resource [launch-context.xml]
이런 에러가 뜨면서 내가 만든 배치 잡이 실행되지 않는다.
maven을 통해서 라이브러리를 함께 넣어서 jar 파일을 만들었다.
내가 찾은 해결책은 맨 아래를 참조 바람.
http://rgordon.co.uk/blog/2012/01/20/getting-started-with-spring-batch/
위의 블로그에서는 이클립스 플러그인 m2e에서 dependency copy가 되지 않아서 명령어로 직접 lib를 넣은 것이다.
http://stackoverflow.com/questions/17099438/unable-to-locate-namespacehandler-for-namespace-http-www-springframework-org
여기에서는 spring-context라이브러리와 xsd 경로를 통일시키라고 하는데... 통일시켜도 동작하지 않는다.
=> 주요 해결책은 이것인것 같다. 거의 모든 구글 결과가 이 내용이다. 클래스패스에 라이브러리가 있는지 확인하고 해당 버전과 호환되는 버전을 써야 한다는 내용들이었다.
http://mojo.codehaus.org/exec-maven-plugin/exec-mojo.html
스프링 배치 샘플 프로젝트를 만들면 pom파일에 exec-maven-plugin이 있다. 이 플러그인을 통해서 mvn 명령어로 jar 파일을 실행 시킬 수 있다. 샘플 프로젝트의 폼 파일을 보면 아래와 같이 되어 있다.
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.1</version>
<configuration>
<mainClass>org.springframework.batch.core.launch.support.CommandLineJobRunner</mainClass>
<arguments>
<!-- job configuration file -->
<argument>classpath:/launch-context.xml</argument>
<!-- job name -->
<argument>job1</argument>
</arguments>
</configuration>
</plugin>
launch-context.xml 파일에서 job1을 실행시키는 설정이 되어 있다.
이클립스에서 gaol에 exec:exec를 입력하고 실행시키면 된다.
참조:http://www.dashaun.com/2010/03/10/springframework-applicationcontext-woes-unable-to-locate-spring-namespacehandler/
http://stackoverflow.com/questions/15013651/using-maven-execexec-with-arguments
http://www.lucasward.net/2010/07/spring-batch-deployment-example.html
나에게 맞았던 해결책
출처: http://stackoverflow.com/questions/3335203/yet-another-unable-to-locate-spring-namespacehandler-error
Shade 플러그인을 쓰니까 해결이 되었다. - 아직 transformers 항목은 이해하지 못했다.
Offending resource: class path resource [launch-context.xml]
이런 에러가 뜨면서 내가 만든 배치 잡이 실행되지 않는다.
maven을 통해서 라이브러리를 함께 넣어서 jar 파일을 만들었다.
내가 찾은 해결책은 맨 아래를 참조 바람.
http://rgordon.co.uk/blog/2012/01/20/getting-started-with-spring-batch/
위의 블로그에서는 이클립스 플러그인 m2e에서 dependency copy가 되지 않아서 명령어로 직접 lib를 넣은 것이다.
http://stackoverflow.com/questions/17099438/unable-to-locate-namespacehandler-for-namespace-http-www-springframework-org
여기에서는 spring-context라이브러리와 xsd 경로를 통일시키라고 하는데... 통일시켜도 동작하지 않는다.
=> 주요 해결책은 이것인것 같다. 거의 모든 구글 결과가 이 내용이다. 클래스패스에 라이브러리가 있는지 확인하고 해당 버전과 호환되는 버전을 써야 한다는 내용들이었다.
http://mojo.codehaus.org/exec-maven-plugin/exec-mojo.html
스프링 배치 샘플 프로젝트를 만들면 pom파일에 exec-maven-plugin이 있다. 이 플러그인을 통해서 mvn 명령어로 jar 파일을 실행 시킬 수 있다. 샘플 프로젝트의 폼 파일을 보면 아래와 같이 되어 있다.
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.1</version>
<configuration>
<mainClass>org.springframework.batch.core.launch.support.CommandLineJobRunner</mainClass>
<arguments>
<!-- job configuration file -->
<argument>classpath:/launch-context.xml</argument>
<!-- job name -->
<argument>job1</argument>
</arguments>
</configuration>
</plugin>
launch-context.xml 파일에서 job1을 실행시키는 설정이 되어 있다.
이클립스에서 gaol에 exec:exec를 입력하고 실행시키면 된다.
참조:http://www.dashaun.com/2010/03/10/springframework-applicationcontext-woes-unable-to-locate-spring-namespacehandler/
http://stackoverflow.com/questions/15013651/using-maven-execexec-with-arguments
http://www.lucasward.net/2010/07/spring-batch-deployment-example.html
나에게 맞았던 해결책
출처: http://stackoverflow.com/questions/3335203/yet-another-unable-to-locate-spring-namespacehandler-error
Shade 플러그인을 쓰니까 해결이 되었다. - 아직 transformers 항목은 이해하지 못했다.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.handlers</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.schemas</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>org.XYZ</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
Maven - Packaging Jar With Dependency
http://stackoverflow.com/questions/1729054/including-dependencies-in-a-jar-with-maven
위의 링크에서 알려준 방법으로 만들 수 있었다.
추가로 나는 pom.xml 파일에 아래의 내용을 추가했다.
그리고 maven에서 mvn package 명령을 실행했다.
(이클립스에서 goal을 package로 설정 후 실행)
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<id>make-assembly</id> <!-- this is used for inheritance merges -->
<phase>package</phase> <!-- bind to the packaging phase -->
<goals>
<goal>attached</goal>
</goals>
</execution>
</executions>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
</manifest>
</archive>
</configuration>
</plugin>
참고:http://mkkbest.tistory.com/entry/11-1
http://valley.egloos.com/viewer/?url=http://entireboy.egloos.com/4615383
위의 링크에서 알려준 방법으로 만들 수 있었다.
추가로 나는 pom.xml 파일에 아래의 내용을 추가했다.
그리고 maven에서 mvn package 명령을 실행했다.
(이클립스에서 goal을 package로 설정 후 실행)
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<id>make-assembly</id> <!-- this is used for inheritance merges -->
<phase>package</phase> <!-- bind to the packaging phase -->
<goals>
<goal>attached</goal>
</goals>
</execution>
</executions>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
</manifest>
</archive>
</configuration>
</plugin>
참고:http://mkkbest.tistory.com/entry/11-1
http://valley.egloos.com/viewer/?url=http://entireboy.egloos.com/4615383
2013년 8월 2일 금요일
스프링 프레임워크 - 하둡
스프링에 하둡 배치 잡을 실행 시킬 수 있다.
참조: http://bcho.tistory.com/726
http://static.springsource.org/spring-hadoop/docs/current/reference/html/fs.html
환경 설정은 위의 참조 홈페이지를 따라 하면 되고 메이븐을 쓴다면
스프링 프레임워크의 하둡 프로젝트를 디펜던시에 추가해주면 된다.
디펜던지 참조: http://www.springsource.org/spring-data/hadoop
나는 설정을 이렇게 했다.
<hdp:configuration>
fs.default.name=${hd.fs}
dfs.name.dir=file://${name.dir}
dfs.data.dir=file://${data.dir}
hadoop.temp.dir=file://${temp.dir}
</hdp:configuration>
그리고 나는 맵과 리듀스 그리고 드라이버 이렇게 하나의 하둡잡에 세개의 클래스를 만들었다.
그래서 스프링에서 아래와 같이 드라이버를 tool-runner로 실행했다.
<hdp:tool-runner id="내Driver" tool-class="com.내Driver"
run-at-startup="true">
<hdp:arg value="/user/내hdfs/input"/>
<hdp:arg value="/user/내hdfs/test/output"/>
<hdp:arg value="세번째 인자"/>
<hdp:arg value="네번째 인자"/>
property=value
</hdp:tool-runner>
그리고 실행하니 NullPointerException이 난다.
Error creating bean with name 'adMainDriver': Invocation of init method failed; nested exception is java.lang.NullPointerExcepttion
원인은 잘 모르겠지만 결론적으론 내 하둡 설정이 잘못되어 있던 것인듯
스프링 소스에서 설정 부분을 아래로 바꿨더니 돌아간다.
파일 이름은 시스템에서 사용하는 하둡 설정 파일로 바꾸고 내 시스템의 하둡 설정 파일을 소스폴더에 붙여넣었다.
참조: http://bcho.tistory.com/726
http://static.springsource.org/spring-hadoop/docs/current/reference/html/fs.html
환경 설정은 위의 참조 홈페이지를 따라 하면 되고 메이븐을 쓴다면
스프링 프레임워크의 하둡 프로젝트를 디펜던시에 추가해주면 된다.
디펜던지 참조: http://www.springsource.org/spring-data/hadoop
나는 설정을 이렇게 했다.
<hdp:configuration>
fs.default.name=${hd.fs}
dfs.name.dir=file://${name.dir}
dfs.data.dir=file://${data.dir}
hadoop.temp.dir=file://${temp.dir}
</hdp:configuration>
그리고 나는 맵과 리듀스 그리고 드라이버 이렇게 하나의 하둡잡에 세개의 클래스를 만들었다.
그래서 스프링에서 아래와 같이 드라이버를 tool-runner로 실행했다.
<hdp:tool-runner id="내Driver" tool-class="com.내Driver"
run-at-startup="true">
<hdp:arg value="/user/내hdfs/input"/>
<hdp:arg value="/user/내hdfs/test/output"/>
<hdp:arg value="세번째 인자"/>
<hdp:arg value="네번째 인자"/>
property=value
</hdp:tool-runner>
그리고 실행하니 NullPointerException이 난다.
Error creating bean with name 'adMainDriver': Invocation of init method failed; nested exception is java.lang.NullPointerExcepttion
원인은 잘 모르겠지만 결론적으론 내 하둡 설정이 잘못되어 있던 것인듯
스프링 소스에서 설정 부분을 아래로 바꿨더니 돌아간다.
파일 이름은 시스템에서 사용하는 하둡 설정 파일로 바꾸고 내 시스템의 하둡 설정 파일을 소스폴더에 붙여넣었다.
<hdp:configuration resources="classpath:/custom-site.xml, classpath:/hq-site.xml"/>
피드 구독하기:
글 (Atom)