배치 실행 Main 클래스
배치를 실행하기 위해서는 main() 메소드가 포함된 Main 클래스가 제공하며, 해당 클래스를 이용해서 일반 배치와 데몬 배치를 실행 할 수 있다.
1. 제공 Main 클래스
프레임워크에서 배치를 실행하기 위한 4개의 Main 클래스를 제공하고 있다.
클래스 명 | 설명 |
---|---|
DefaultCommandLineJobRunner |
일반 배치를 실행하기 위한 Main 클래스 |
DefaultDaemonBatchJobRunner |
데몬 배치를 실행하기 위한 Main 클래스 |
DefaultLocalCommandRunner |
Local에서 배치를 실행하기 위한 Main 클래스 |
DefaultModuleTestCommandRunner |
배치 모듈 테스트를 위한 Main 클래스 |
2. DefaultCommandLineJobRunner 클래스
일반 배치를 실행하는 Main 클래스로서 초기화 및 Application Container 생성, Job XML 로드, Batch Context 초기화 등을 수행한다.
해당 클래스는 배치 실행과 관련된 초기화 전/후에 추가적인 커스터마이징이 필요한 경우, 해당 클래스에서 커스터마이징을 진행할 수 있다.
Sample Source
public class DefaultCommandLineJobRunner extends AbstractJobRunner
{
public static void main( String[] args) throws Exception
{
DefaultCommandLineJobRunner command= new DefaultCommandLineJobRunner();
List<String> newargs= new ArrayList<String>( Arrays.asList( args));
List<String> params= new ArrayList<String>();
int count= 0;
String applicationLocation= null;
String applicationName= null;
String jobIdentifier= null;
boolean setProp= false;
for( String arg: newargs)
{
...
switch( count)
{
case 0:
applicationLocation= arg;
break;
case 1:
applicationName= arg;
break;
case 2:
jobIdentifier= arg;
break;
default:
params.add( arg);
break;
}
count++;
...
}
if( applicationName== null || jobIdentifier== null)
{
String message= "At least 3 argements are required : appHome, applicationName,jobIdentifier.";
DefaultCommandLineJobRunnerLogger.logger.error( message);
CommandLineJobRunner.message= message;
command.exit( 1);
}
String[] parameters= params.toArray( new String[params.size()]);
int result= command.start( applicationLocation, applicationName, jobIdentifier, parameters, setProp);
if(!BatchApplicationContext.isModuleTest())
{
command.exit( result);
}
else
{
ModuleTestInputParameter.getInputParameterObject().setExitCode(result);
}
}
int start( String applicationLocation, String applicationName, String jobIdentifier
, String[] parameters, boolean setProps)
{
final LightweightApplicationContext context;
String jobName= jobIdentifier;
ApplicationContainer container = null;
try
{
this.jobId = jobIdentifier;
// 초기화
DefaultBatchInitializer.initGeneralBatch();
// ApplicationContainer 생성
container = BatchInitializer.createContainer(applicationLocation, jobIdentifier);
// 기본 Header 생성
ContextHeader cHeader = ( ContextHeader)Thread.currentThread().getContextClassLoader().loadClass(
container.getRequestHeaderClassName()).getDeclaredConstructor().newInstance();
...
// 배치 파라미터 선 로딩 처리
BasicBatchControlParameters basicBatchControlParameters = null;
if(Boolean.parseBoolean(System.getProperty(BxmBatchConstants.SYSPROP_PRELOAD_BATCH_PARAMETER_OPT, "true")))
{
basicBatchControlParameters = BatchApplicationContainer.getBatchControlParameters();
basicBatchControlParameters.loadBatchControlParameters(jobIdentifier);
// DataSource 정보 Load
List<String> dataSoruceList = basicBatchControlParameters.getDataSourceAllowList();
// DataSource 정보 설정
DeployedResourceScannerConfigurer.setCheckDataSourceList(dataSoruceList);
}
// Batch Context 초기화
context= BatchInitializer.initializeContext(
applicationLocation, applicationName, jobIdentifier, null, setProps, this, cHeader, parameters);
JobParameters jobParameters = getJobParameters(parameters);
Assert.isTrue( parameters== null || parameters.length== 0 || !jobParameters.isEmpty(),
"Invalid JobParameters "+ Arrays.asList( parameters)+
". If parameters are provided they should be in the form name = value (no whitespace).");
// Job 설정
Job job = BatchInitializer.getJob(context, jobIdentifier);
if(BatchApplicationContext.isModuleTest())
{
ManagedFlowJob managedFlowJob = (ManagedFlowJob) job;
managedFlowJob.setName(jobName);
}
// 파라미터 설정
cHeader.getDataContainer().put( BatchContextConstans.CMDC_JOBPARAMETER_NAME, jobParameters);
// ServiceTraceContextImpl 생성
BatchInitializer.createServiceTraceContextImpl(jobName, cHeader);
// Loglevel 설정
DefaultLogLevel defaultLogLevel = new DefaultLogLevel();
defaultLogLevel.setSysLogLevel(ContextTrace.getLocalContext().getSysLoggingLevel());
defaultLogLevel.setBizLogLevel(ContextTrace.getLocalContext().getBizLoggingLevel());
defaultLogLevel.setDBLogLevel(ContextTrace.getLocalContext().getDBLoggingLevel());
cHeader.getDataContainer().put(BatchContextConstans.CMDC_DEFAULT_LOGLEVEL, defaultLogLevel);
// Default ERROR Code 설정
BxmBatchConstants.BATCH_DEFAULT_ERROR_CODE = DefaultConst.DEFAULT_ERROR_CODE;
// ShutdownHook 정의
ApplicationContainerLoader.registerBeforeShutdownHook( new Runnable(){
public void run()
{
destory( context);
}
});
// Job Executor Get
JobExecutor jobExecutor = BatchApplicationContainer.getJobExecutor();
final JobExecution jobExecution = jobExecutor.jobExecute(
launcher, container, ApplicationContextTrace.getCurrentApplication(),
job, jobParameters, BatchType.GENERAL, false, null, scheduleId, cHeader, basicBatchControlParameters);
// 종료처리
try
{
String jobExitCode = System.getProperty(DefaultCommandLineJobRunner.JOB_EXITCODE);
if(StringUtils.hasText(jobExitCode) && exitCodeMapper instanceof SimpleJvmExitCodeMapper)
{
Map<String, Integer> mapper = new HashMap<String, Integer>();
String[] exitCodeStrs = jobExitCode.split(";");
for(String exitCodeStr : exitCodeStrs)
{
String[] exitCode = exitCodeStr.split(",");
mapper.put(exitCode[0], Integer.parseInt(exitCode[1]));
}
((SimpleJvmExitCodeMapper) exitCodeMapper).setMapping(mapper);
}
}
catch (Exception e)
{
DefaultCommandLineJobRunnerLogger.logger.error(e.getMessage(), e);
}
return exitCodeMapper.intValue( jobExecution.getExitStatus().getExitCode());
}
catch( Throwable th)
{
String message= "Job Terminated in error: "+ th.getMessage();
// Job 초기화 실패시 등록 처리
JobExecution jobExecution = BatchLoadFailDao.addLoadFailInfo(jobIdentifier, parameters, null, th);
if(container != null && jobExecution != null)
{
BatchErrorInfo batchErrorInfo = BatchSystemMessageUtils.getErrorInfo(th);
if(batchErrorInfo == null)
{
String errorMessage = BatchSystemMessageUtils.getMessageByCode(BxmBatchErrorCode.BATCH_START_FAILED
, new Object[]{jobIdentifier, th.getMessage()});
batchErrorInfo = new BatchErrorInfo();
batchErrorInfo.setErrorCode(BxmBatchErrorCode.BATCH_START_FAILED);
batchErrorInfo.setErrorMessage(errorMessage);
batchErrorInfo.setThrowable(th);
}
BatchSystemMessageUtils.upateJobErrorInfo(jobExecution.getId(), batchErrorInfo.getErrorCode(), batchErrorInfo.getErrorMessage(), th);
}
DefaultCommandLineJobRunnerLogger.logger.error( message, th);
return exitCodeMapper.intValue( ExitStatus.FAILED.getExitCode());
}
}
}
3. DefaultDaemonBatchJobRunner 클래스
데몬 배치를 실행하는 Main 클래스로서 초기화 및 Application Container 생성, Job XML 로드, Batch Context 초기화, 데몬 배치 실행 등을 수행한다.
해당 클래스는 데몬 배치 실행과 관련된 초기화 전/후에 추가적인 커스터마이징이 필요한 경우, 해당 클래스에서 커스터마이징을 진행할 수 있다.
Sample Source
public class DefaultDaemonBatchJobRunner extends AbstractJobRunner {
public static void main( String[] args) throws Exception
{
List<String> newargs = new ArrayList<String>(Arrays.asList(args));
Set<String> opts = new HashSet<String>();
List<String> params = new ArrayList<String>();
System.setProperty(DAEMON_BATCH, "true");
System.setProperty(SmartTaskletStep.OTHER_JOB_REPOS, "true");
DefaultDaemonBatchJobRunner daemonBatchJobRunner = new DefaultDaemonBatchJobRunner();
int count = 0;
String applicationLocation = null;
String applicationName = null;
String jobIdentifier = null;
boolean setProp = false;
// 배치 입력 아규먼트 값 처리
for(String arg : newargs)
{
...
switch(count)
{
case 0:
applicationLocation = arg;
break;
case 1:
applicationName = arg;
break;
case 2:
jobIdentifier = arg;
break;
default:
params.add(arg);
break;
}
count++;
...
}
// Application Name과 jobIdentifier(데몬배치 Job Id)가 없으면 error 처리
if(applicationName == null || jobIdentifier == null)
{
String message = "At least 3 argements are required : Application Home, Application Name, Job Identifier.";
logger.error(message);
daemonBatchJobRunner.exit(1);
}
// 파라미터를 배열로 변경
String[] parameters = params.toArray(new String[params.size()]);
int result= daemonBatchJobRunner.start(applicationLocation, applicationName, jobIdentifier, parameters, opts, setProp);
daemonBatchJobRunner.exit(result);
}
int start(String applicationLocation, String applicationName, String jobIdentifier, String[] parameters, Set<String> opts, boolean setProps)
{
@SuppressWarnings("unused")
final LightweightApplicationContext context;
ApplicationContainer container = null;
// bxmenv.properites 화일에 정의된 시스템프러퍼티 로딩.
SystemPropertyLoader.loadSystemProperty();
try
{
// 초기화
DefaultBatchInitializer.initDaemonBatch();
// ApplicationContainer 생성
container = DaemonBatchInitializer.createContainer(applicationLocation, jobIdentifier);
// 데몬 배치 인스턴스 정보 조회 Dao
bxmBatchInstanceInfoDao = DaemonBatchDaoContainer.getBatchInstanceInfoDao();
// 기본 Header 생성
ContextHeader cHeader = ( ContextHeader)Thread.currentThread().getContextClassLoader().loadClass(
container.getRequestHeaderClassName()).getDeclaredConstructor().newInstance();
/** 2019-07-19 OracleSetModule 호출하는데 context 가 필요해서 세팅 */
BatchInitializer.createServiceTraceContextImpl(jobIdentifier, applicationName, cHeader);
// Batch Context 초기화
context = DaemonBatchInitializer.initializeContext(applicationLocation, applicationName, jobIdentifier, opts, setProps, this);
// Node 정보 확인
if(StringUtils.isEmpty(BxmBatchConstants.PROP_EXEC_NODE_NO))
{
throw new IllegalStateException("Node No is null or empty.");
}
// 실행 Option Command 확인
if(opts.contains("-stop") || opts.contains("-stat"))
{
return batchOptionCommand(opts, jobIdentifier);
}
// Option 이 없으면 데몬배치 중복 실행여부 확인
else
{
List<String> bxmInstanceIds = new ArrayList<String>();
// BXM INSTANCE ID 정보 가져오기..
List<String> values = bxmBatchInstanceInfoDao.getDaemonBatchStat(jobIdentifier, BxmBatchConstants.PROP_EXEC_NODE_NO, true);
// JMX 체크하여...확인
if(values != null && !values.isEmpty())
{
bxmInstanceIds.addAll(values);
for(String frmInstanceId : bxmInstanceIds)
{
boolean isCheck = false;
try
{
commandMBean("check", jobIdentifier, frmInstanceId);
}
catch (Throwable th)
{
if(th instanceof TimeoutException)
{
logger.warn("JMX Connection Check.. Timeout..! Skip..!");
logger.warn(th.getMessage());
isCheck = true;
}
else if(th.getCause() instanceof IOException
&& th.getCause().getCause() instanceof ServiceUnavailableException)
{
logger.warn("JMX Connection Check.. ServiceUnavailableException..! Skip..!");
logger.warn(th.getMessage());
isCheck = true;
}
else
{
logger.error(th.getMessage(), th);
// Timeout Exception이 아닌 경우에 종료.
return exitCodeMapper.intValue(ExitStatus.FAILED.getExitCode());
}
}
if(!isCheck)
{
logger.error("Batch is Already running.");
return exitCodeMapper.intValue(ExitStatus.FAILED.getExitCode());
}
}
}
}
/////////////////////////////////////////////////////////////////
// 데몬배치 실행....
/////////////////////////////////////////////////////////////////
standAloneExecutor = new DaemonStandAloneExecutor();
standAloneExecutor.setDaemon(true);
standAloneExecutor.afterPropertiesSet();
standAloneExecutor.start(container, jobIdentifier);
// 시작 될때 까지 대기
while(true)
{
if(standAloneExecutor.isStarted())
{
break;
}
Thread.sleep(10);
}
while(true)
{
if((isStopFlag && standAloneExecutor.getScheduledTaskCount() == 0)
|| (standAloneExecutor.isStopFlag() && standAloneExecutor.getScheduledTaskCount() == 0))
{
logger.info("================================================================================");
logger.info("Daemon Batch Task is 0. Daemon Batch Down !!");
logger.info("================================================================================");
clear();
break;
}
Thread.sleep(1000);
}
}
catch( Throwable th)
{
String message= "Job Terminated in error: "+ th.getMessage();
logger.error( message, th);
return exitCodeMapper.intValue(ExitStatus.FAILED.getExitCode());
}
finally
{
ContextTrace.removeLocalContext();
}
return exitCodeMapper.intValue(ExitStatus.COMPLETED.getExitCode());
}
...
}
4. DefaultLocalCommandRunner 클래스
Local 개발도구에서 배치를 실행하기 위한 Main 클래스로서 배치를 실행하기 위한 아규먼트를 DefaultCommandLineJobRunner로 전달한다.
해당 클래스에서 Local 배치 실행 시 필요한 커스터마이징을 진행할 수 있다.
Sample Source
public class DefaultLocalCommandRunner {
public static String JOB_LOG_NAME_KEY = "_@#job_log_file_";
/**
* 로컬 배치 실행 <br><br>
*
* 1 parameter : Application Location <br>
* 2 parameter : Application Name <br>
* 3 parameter : Job Name <br>
* 4 parameter.... : 입력 파라미터 (key1=value1 key2=value2 ....) <br>
*
* @param args 배치 실행 아규먼트..
*/
public static void main(String[] args)
{
BatchCommonUtils.disableWarning();
if(args.length < 3)
{
throw new IllegalArgumentException("입력 Argument 개수 오류 입니다. args : " + Arrays.asList(args));
}
// Tool에서 해당 Class 호출 하기 전에 설정
//System.setProperty("config-location", "file:///E:\\batch_test\\config\\bxm-batch.xml");
String logName = String.format("%s\\%s\\%s_%s"
, args[1], DateUtils.getDate(new Date()), args[2], DateUtils.getDateTimeNow());
System.setProperty(JOB_LOG_NAME_KEY, logName);
try
{
DefaultCommandLineJobRunner.main(args);
}
catch (Exception e)
{
throw new RuntimeException(e.getMessage(), e);
}
}
}
5. DefaultModuleTestCommandRunner 클래스
배포 된 배치 모듈(Bean)에 대해서 리모트로 테스트를 수행 할 수 있도록 제공하는 Main 클래스이다.
해당 클래스에서 모듈 테스트 실행 시 필요한 커스터마이징을 진행할 수 있다.
참고: 배치 모듈 테스트의 실행과 결과 전달은 Java Serialization 및 DeSerialization을 사용하여 파일을 통해 처리된다.
Sample Source
public class DefaultModuleTestCommandRunner {
protected static SystemExiter systemExiter= new JvmSystemExiter();
protected static final Logger logger= LoggerFactory.getLogger( DefaultModuleTestCommandRunner.class);
public static void main(String[] args) throws Exception {
BatchCommonUtils.disableWarning();
BatchModuleTestResult result = null;
try
{
// Serialiezed Object 를 Read한다.
String readObjectFile = args[0];
String resultObjectFile = null;
if(args.length > 1)
{
resultObjectFile = args[1];
}
BatchRemoteModuleTestInfo batchModuleTestInfo = (BatchRemoteModuleTestInfo) BatchFileObjectUtils.readObject(readObjectFile);
result = BatchModuleTestUtils.runServerBatchModuleTest(batchModuleTestInfo);
BatchFileObjectUtils.writeObject(resultObjectFile, result);
logger.info("#######################################");
logger.info("# Result : {}", result);
logger.info("#######################################");
}
catch (Throwable th)
{
logger.error(th.getMessage(), th);
systemExiter.exit(1);
}
finally
{
Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
for(Thread thread: threadSet)
{
try {thread.interrupt();} catch (Exception e) {}
}
}
systemExiter.exit(result.getExitCode());
}
@SuppressWarnings("unused")
private static BatchModuleTestInfo readBatchModuleTestInfo(String readObjectFile) throws IOException, ClassNotFoundException
{
FileInputStream fileInputStream = null;
ObjectInputStream objectinputstream = null;
try
{
fileInputStream = new FileInputStream(new File(readObjectFile));
objectinputstream = new ObjectInputStream(fileInputStream);
return (BatchModuleTestInfo) objectinputstream.readObject();
}
finally
{
try { if(objectinputstream != null) objectinputstream.close(); } catch (Exception e) {}
try { if(fileInputStream != null) fileInputStream.close(); } catch (Exception e) {}
}
}
}