'2008/11'에 해당되는 글 12건
- 2008/11/28 [Jump-up!하이버네이트] 1부 Conversation with Hibernate 2화
- 2008/11/27 [Jump-up!하이버네이트] 1부 Conversation with Hibernate 1화
- 2008/11/25 제 9회 스프링 세미나 스크린캐스팅: Spring AOP 4
- 2008/11/25 "Spring Python" 파이썬 개발자에게도 봄(?)을
- 2008/11/21 [Spring batch] 스프링배치 연재(10) JobLauncher와 Job, Step
- 2008/11/20 스프링 2.5.x 버전을 쓰시는 분들은 2.5.5 이상으로 빨리 업데이트 하세요
- 2008/11/18 제 9회 스프링 세미나 스크린캐스팅: Spring AOP 3 (2)
- 2008/11/12 Spring, Grails를 품에 안다 (4)
- 2008/11/11 제 9회 스프링 세미나 스크린캐스팅: Spring AOP 2
- 2008/11/07 http://www.ksug.org/rss 로 RSS 주소 변경
Conversation with Hibernate 2화입니다.
1화에서 이어서 2화에서는 Session과 Conversation 관리에 대한 패턴을 살펴보고, 예제를 통해서 검증을 해보게 됩니다.
그럼 좋은 시간 되세요~. 감사합니다.
스크린캐스팅 보러 가기
Jump-up!hibernate 연재를 시작하려고 합니다.
하이버네이트에 대해서 많은 분들이 공유할 수 있는 좋은 기회가 됐으면 합니다.
첫 번째 시간은 Conversation과 conversation 구현에 방법에 따른 sql 실행에 대한 내용입니다.
감사합니다.
스크린 캐스팅 보러 가기
스크린캐스팅 보러가기
Spring 도입 초기에 Struts와 Spring을 비슷하게 보는 국내 개발자들을 흔히 만날 수 있었다. Spring이 Struts를 대체하고자 등장한 것이 아니듯, Spring Python 역시 Django를 대체하려고 나온 것은 아니다. 스프링은 이론적 배경에서 출발한 것이 아니라 엔터프라이즈 시스템 구현 과정에서 바람직한 방법을 총망라한 것이다. Python is Not Java임에도 불구하고... 그야말로 전도(Evangelism)라는 말이 딱 어울린다. 전도를 위해서는 그 나라 언어와 문화를 배워야 하듯, Spring의 철학을 Python에서 실현하기 위해서는 보다 Python 스러움을 수용해야 한다.
0.8.0 ... 현재 Spring Python의 버전이다. 레퍼런스를 보면서.. 벌써 하고 놀라지 않을 수 없었다.
JobLauncher
JobLauncher는 이름 그대로 배치 Job을 실행시키는 역할을 한다. 해당 인터페이스를 살펴 보면 Job과 그 Job을 실행시키는데 쓰이는 변수인 JobParameters 클래스만을 파라미터로 받는 run메소드만이 정의되어 있다.
public interface JobLauncher {
JobExecution run(Job job, JobParameters jobParameters) throws JobExecutionAlreadyRunningException, JobRestartException;
}
리스트 12 : JobLauncher 인터페이스
기본 구현 클래스로는 SimpleJobLauncher가 제공된다. 이를 이용한 설정은 다음과 같다.
<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
<property name="taskExecutor">
<bean class="org.springframework.core.task. SyncTaskExecutor " />
</property>
</bean>
리스트 13 : SimpleJobLauncher 설정
jobRepository 속성은 위에서 계속 언급한 그 JobRepository 인터페이스를 구현한 클래스가 삽입된다.
taskExecutor는 Job실행을 어떤 방식으로 할 것인지를 지정할 수 있는 속성으로 org.springframework.core.task.TaskExecutor를 구현한 클래스가 들어가야 한다. TaskExecutor는 “void execute(Runnable task)” 메소드 하나만을 가지는 단순한 인터페이스로 JDK 1.5의 Executor 인터페이스와 같은 메소드 시그니처를 가진다. 스프링의 Core 패키지에서는 JDK 1.4와의 호환성을 위해서 이 인터페이스가 따로 분리되었다고 한다.
taskExecutor를 이용해서 Job을 동기적, 혹은 비동기적으로 실행할 수 있다. 별도로 설정하지 않으면 SyncTaskExecutor가 디폴트 클래스로 설정되어서 동기적으로 실행된다. 비동기적 방식으로 실행을 원할 때는 SimpleAsyncTaskExecutor 클래스를 지정한다. 대부분의 배치Job이 스케쥴러나 command line을 통해 실행되기 때문에 동기적 실행방식으로 해도 무난하겠지만, 만약 Job의 실행 이벤트가 웹application을 통해 전달되는 경우가 있다면 비동기적으로 실행되어야 할 것이다. 배치Job의 특성상 처리 시간이 오래 걸리는 작업이 많을 것이고, 그 시간동안 사용자가 웹브라우저에서 모래 시계를 보고 기다리게 하는 것은 바람직하지 못한 일이다. 사용자가 기다림을 못 참아 창을 닫거나 백스페이스를 누를 경우 어떻게 처리를 해야 할지 고민하는 것도 부담이 되는 작업이다. 피치 못하게 배치성 작성의 실행이 웹어플리케이션을 통하게 될 때는 비동기적으로 처리하고 ‘작업 처리 요청이 되었습니다. 몇 분 뒤에 확인을 해 보시기 바랍니다. ‘정도의 메시지로 사용자에게 이를 알리는 것이 안전한 방식으로 생각된다.
Job과 Step
실행시킬 작업을 의미하는 Job 인터페이스는 아래 같이 정의된다.
public interface Job {
String getName();
boolean isRestartable();
void execute(JobExecution jobexecution) throws JobExecutionException;
}
리스트 14 : JOB 인터페이스
해당 작업의 이름과 재시작 가능성여부의 속성을 반환하는 메소드와 작업을 실행시키는 excute메소드가 있다.
역시 기본 구현 클래스로 SimpleJob 클래스가 제공이 되고, jobRepository가 그 속성으로 들어간다. 아래와 같이 abstract한 bean 설정으로 해두고, 실제적인 Job들을 설정할 때 parent로 이를 활용하면, 반복적인 코드가 없는 설정을 할 수 있다.
<bean id="simpleJob" class="org.springframework.batch.core.job.SimpleJob" abstract="true">
<property name="jobRepository" ref="jobRepository" />
</bean>
리스트 15 : parent로 쓰기 위한 simpleJob의 설정
스프링배치는 Job을 실행하는 것이 핵심적인 역할인데도, 실제적으로 Job설정에서는 크게 할 일이 없다. 대신 실제적인 수행 작업들은 SimpleJob이 가지고 있는 Step 들의 List를 통해 이루어 진다.
public interface Step {
String getName();
boolean isAllowStartIfComplete();
int getStartLimit();
void execute(StepExecution stepexecution) throws JobInterruptedException;
}
리스트 16 : STEP 인터페이스
이 Step 인터페이스를 구현하고 있는 클래스 중에 ItemOrientedStep라는 클래스가 있다. 지난달 연재에 많은 지면을 할애해서 설명했던 Item이라는 존재가 이 클래스를 통해 연결이 되는 것이다. 즉, 이 클래스가 Spring-batch-core 모듈과 Spring-batch-infrastructure 모듈을 있는 다리 역할을 한다고 볼 수 있다. ItemOrientedStep은 SimpleStepFactoryBean이라는 FactoryBean을 통해서 생성을 할 수 있다. Job과 마찬가지로 공통적인 설정인 abstarct로 선언해 두도록 하자.
<bean id="simpleStep" class="org.springframework.batch.core.step.item.SimpleStepFactoryBean" abstract="true">
<property name="jobRepository" ref="jobRepository" />
<property name="commitInterval" value="10" />
</bean>
리스트 17 : parent로 쓰기 위한 SimpleStep의 설정
commitInterval 속성은 첫 연재 때 설명한 것처럼, 적절한 transaction 단위를 가지고 가기 위해서 주기적인 commit 건수를 지정할 수 있는 속성이다.
위에서 잡은 abstract bean의 설정을 이용해 Job과 Step을 설정한 예는 다음과 같다.
<bean id="sampleJob" parent="simpleJob">
<property name="steps">
<bean parent="simpleStep">
<property name="transactionManager" ref=" transactionManager " /><property name="itemReader" ref="sampleItemReader " />
<property name="itemWriter" ref=" sampleItemWriter "/>
</bean>
</property>
</bean>
리스트 18 : Job과 Step의 설정 예
simpleJob의 steps속성은 java.util.List형이므로 여러 개의 Step설정이 당연히 가능하다. 그리고 Step설정에서의 itemReader와 itemWriter속성은 지난 시간에 나왔던 FlatFileItemReader등의 클래스가 들어가면 된다.
위의 설정만을 본다면 ItemOrientedStep이 직접 itemReader와 itemWriter를 멤버변수로 가지고 있는 것처럼 보일 수도 있겠지만, 내부의 실제적인 구현은 중간에 itemHandler라는 멤버변수가 itemReader와 itemWriter를 소유하고 있는 형태로 되어있다. SimpleStepFactoryBean을 사용한 설정이기에 보다 단순화된 구조로 설정이 가능한 것이다.
배치처리의 성격에 따라서는 이렇게 ItemReader와 ItemWriter를 활용한 구조가 맞지 않는 경우도 있을 것이다. 예를 들어 단순히 DB의 procedure호출만으로 끝나는 배치처리가 있다면 단순히 메소드 하나로 기능을 구현하고 싶어질 것이다. 그런 배치 프로그램을 위해서 TaskletStep이라는 클래스가 있고, TaskletStep이 내부에서 호출을 하는 Tasklet이라는 인터페이스가 정의되어 있다.
public interface Tasklet {
ExitStatus execute() throws Exception;
}
리스트 19 : tasklet 인터페이스
마찬가지로 taskletStep은 abstract한 bean으로 설정해 주면 편리하다.
<bean id="taskletStep" class="org.springframework.batch.core.step.tasklet.TaskletStep" abstract="true">
<property name="jobRepository" ref="jobRepository" />
</bean>
아래 예는 스프링배치의 샘플프로젝트에서 제공되는 SystemCommandTasklet클래스를 이용해서 “echo hello”라는 명령어를 5초동안의 timeout시간을 두고 실행시키는 설정의 예이다. ItemOrientedStep과 마찬가지로 steps 속성 아래 직접 넣거나 <ref bean=" helloStep "/>의 형식으로 참조를 시켜서 Job에 포함시킬 수 있다.
<bean id="helloStep" parent="taskletStep">
<property name="tasklet">
<bean class="org.springframework.batch.sample.tasklet.SystemCommandTasklet">
<property name="command" value="echo hello" />
<property name="timeout" value="5000" />
</bean>
</property>
</bean>
샘플프로젝트에는FileDeletingTasklet, SystemCommandTasklet, DummyMessageSendingTasklet 등의 예제가 있어서 이를 참조할 수 있다. Tasklet을 구현한 클래스에서 업무DB에 접근하는 DAO를 삽입해서 쓸 수도 있을 것이다.
이렇게 Job을 구성하기 위해서는 많은 구성요소가 필요한 것 같지만, 실제로 코딩해야 할 클래스들은 ItemReader나 ItemWriter, Tasklet에 불과한 것을 알 수 있다. 그리고 ItemReader, ItemWriter도 전형적인 읽기와 쓰기 작업이라면 기본 제공 클래스를 이용한 설정으로 구성이 가능하기 때문에, 최종 개발자가 작성한 코드는 더욱 줄어들 수 있다.
Command Line에서의 Job 실행
드디어 지금까지의 구성요소들을 조합해 배치잡을 실행해 볼 수 있는 방법이 나온다.
CommandLineJobRunner라는 클래스를 이용하면 main 메소드가 포함된 일반적인 java application처럼 스프링배치의 Job들을 실행할 수 있다.
java CommandLineJobRunner [설정파일명] [job이름]
리스트 20 : CommandLineJobRunner 실행 형식
물론 지정한 설정파일에는 JobRepository, JobLauncher, Job 등의 구성요소들의 다 정의되어 있거나 정의된 다른 xml파일들의 import되어 있어야 할 것이다. 배치 Job 실행에 필요한 매개변수를 넘기고 싶을 때는 job이름 뒤에 [변수명=값]의 형태로 적어주면 된다. 여기에 지정된 값들을 JobParameters 클래스들로 접근이 가능하고, JobRepository에 의하여 기록된다. 여러 개의 변수가 지정이 가능하므로 Job이름 뒤의 매개변수가 들어갈 공간들은 전부 JobParameters 지정을 위해서 쓰인다고 볼 수 있다. JobParameters 객체는 JobInstance. getJobParameters() 메소드나 StepExcution. getJobParameters() 메소드를 통해서 얻을 수 있다.
개념적으로 본다면 한번의 논리적 Job실행 단위인 JobInstance는 job configuration과 대응되는 단위인 Job과 JobParameters의 결합체이다. 즉 하나의 JobIntance가 다른 JobIntance와 다른 성격을 가질 수 있는 것은 JobParameters에 의해서 결정된다고 볼 수 있다.
스케쥴러를 이용한 실행
스프링 배치에서는 별도의 스케쥴러가 제공되지는 않고 Cron이나 Quarz를 이용해서 실행스케쥴을 설정할 수 있다. Cron을 활용한다면 위에서 설명한 command line에서의 명령어를 crontab에 등록하면 될 것이다.
샘플프로젝트에서 예시로 나와있는 Quartz를 이용한 설정은 아래와 같다. 스프링에서 제공되는 Quartz 지원 클래스들을 활용하는 것이다.
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="jobDetail" />
<property name="cronExpression" value="0/10 * * * * ?" />
</bean>
</property>
</bean><bean id="jobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="org.springframework.batch.sample.quartz.JobLauncherDetails" />
<property name="group" value="quartz-batch" />
<property name="jobDataAsMap">
<map>
<entry key="jobName" value="footballJob"/>
<entry key="jobLocator" value-ref="jobRegistry"/>
<entry key="jobLauncher" value-ref="jobLauncher"/>
</map>
</property>
</bean><bean id="jobRegistry" class="org.springframework.batch.core.configuration.support.MapJobRegistry" />
리스트 21 : QUARZ를 이용한 스케쥴링
JobLauncherDetails 클래스는 org.springframework.scheduling.quartz.QuartzJobBean을 상속한 클래스로 jobLocator에서 jobName을 키로 해당 Job을 얻어와서 jobLauncher를 통해 실행을 시키는 절차를 수행한다. cronTrigger의 cronExpression속성은 cron과 똑 같은 형식으로 스케쥴을 지정할 수 있게 되어 있다.
JobLocator 인터페이스는 Job객체를 얻어올 수 있는 저장소를 확장성 있게 제공하기 위한 인터페이스이다.
public interface JobLocator {
Job getJob(String name) throws NoSuchJobException;
}
리스트 22 : JobLocator 인터페이스
위의 예제에서는 단순히 메모리에 등록하고 불러오는 MapJobRegistry를 활용하도록 되어 있으나, 필요에 따라 이 인터페이스를 구현해서 DB, Naming Server 등을 활용한 클래스를 만들 수 있을 것이다.
- 정상혁 ( http://benelog.egloos.com )
long 혹은 Long의 경우에도 INTEGER 로 초기화 하는 코드를 확인할 수 있습니다. 스프링처럼 심하게 잘 만들어진 코드에 이런 오류가 있는지 잘 이해가 가지 않습니다. 위 코드를 처음 확인한 제 동료의 경우는 설마 스프링에 이런 버그가 있지는 않을 듯 해서 본인이 모르는 뭔가가 있는지 저에게 확인했습니다. 아래는 JDBC 스펙에서 오린 것인데요. 스펙에서도 long to BIGINT 는 명기하고 있습니다.
제 블로그에 글에 알려드린 바대로 2.5.5 버전에서는 갱신해서 배포했습니다.
데이터 타입에 대한 오류는 매우 민감합니다. 만일, 2.5.4 이하 버전으로 운용하는 시스템이라면 데이터베이스에 의도하지 않는 값이 있을 수 있습니다. 테스트 해본 팀원에 따르면 Types.INTEGER 를 벗어나는 값이 오면 예외를 발생하는 것이 아니라 오버플로우가 일어나 이상한 수치가 들어간다고 하네요. 특정 클래스 Map에 담으면 알 수 없는 타입으로 인지해 정상적으로 long 수치를 저장하더군요. Transaction Script의 남용으로 객체 대신 Map을 쓰는 경우가 많은 국내 현실 탓에 오히려 문제가 없었을 수도 있겠다 싶어 씁쓸하기도 했습니다.
이런 버그 하나로 스프링의 신뢰도를 의심할 분들이 있지는 않을까 하는 노파심도 들었습니다. 허나 스프링 이슈 트래커를 보면 그렇지 않음을 확인할 수 있습니다. 문제는 물론 해결책까지 제시하는 스마트한 고객(스프링 커뮤니티의 일원이죠), 3시간만에 접수하고 그날 밤 빌드에 반영하는 기민한 기술지원, 이러한 과정을 투명하게 공유하는 시스템 등을 상용 제품과 비교해보면 단박에 알 수 있습니다. 과거에 기술지원이 없어 오픈소스를 못쓴다는 말을 수십, 수백번 들은 바 있는데요. 상용 제품은 버그를 찾아내면 바로 해결해주던가요? 얼마전 국내 대표적인 솔루션 회사의 시장 선도 제품에 버그를 발견했던 일이 있습니다. 우리 팀원이 여섯 차례 문제를 보고했습니다. 빠른 조치를 위해 현상 뿐 아니라 예외로그를 분석해서 문제가 되는 클래스까지 알려주었습니다. 업체의 사이트에는 게시판이 있기는 했지만, 기술적인 문의가 오가는 것은 거의 없었습니다. 몇 일 후에 기술지원 인력이 와서 반나절을 앉아 있다가 '해결할 수 없어서 연구소에 보고하겠다'는 말만 남긴 채 떠나갔습니다. 안타까운 것은 경험을 통해 이미 그러한 결과를 예상하고 있었지만, 혹시나 하고 있었다는 점입니다.
어떤 문법을 사용할 것인지, 어떤 AOP 구현체를 사용할 것인지, 언제 위빙할 것인지에 관한 이야기 입니다.
스크린 캐스팅 보러가기
이로써 SpringSource는 모토로 삼고 있는 자바의 복잡함과 싸우는 전쟁(Weapons for the War on Java Complexity)에서 새로운 무기를 추가한 격이다.
로그 존슨은 Groovy의 강점을 다음과 같이 설명하고 있다. 이는 Ruby와 비교될 듯 해서 옮겨본다.
- 자바 클래스 파일로 바로 컴파일할 수 있는 유일한 동적 언어(only dynamic language that can compile directly to Java .class files)
- 자바와 자연스럽게 섞어 쓸 수 있는 유일한 언어(the only language that can be used mixed seamlessly with
Java)
- 자바 애노테이션을 처리할 수 있는 유일한 언어(the only language that can process Java annotations)
- 자바 개발자가 다른 동적 언어에 비해 습득이 쉬움(it has a natural migration path from Java, rather than calling for a big, risky leap of faith)
- DSL 구현에 적합
Spring AOP 두 번째 주제 보러가기
더불어 기존 feedburner 주소도 유효하게 갱신했습니다만, 한RSS의 경우 갱신이 즉각 반영되지는 않는 듯 합니다.

Prev




Rss Feed