배치 실행 Main 클래스

배치를 실행하기 위해서는 main() 메소드가 포함된 Main 클래스가 제공하며, 해당 클래스를 이용해서 일반 배치와 데몬 배치를 실행 할 수 있다.

1. 제공 Main 클래스

프레임워크에서 배치를 실행하기 위한 4개의 Main 클래스를 제공하고 있다.

제공 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) {}
        }
    }
}

Copyright© Bankwareglobal All Rights Reserved.