Spring batch return custom process exit code

11,784

Solution 1

Thanks to @Mahendra I've got an idea :)

I've created a JobCompletionNotificationListener class as @Mahendra suggested:

@Component
public class JobCompletionNotificationListener extends JobExecutionListenerSupport {

    private static final Logger logger = LoggerFactory.getLogger(JobCompletionNotificationListener.class);

    @Override
    public void afterJob(JobExecution jobExecution) {

        SingletonExitCode exitCode = SingletonExitCode.getInstance();

       if(jobExecution.getStatus() == BatchStatus.COMPLETED)
       {
           logger.info("Exit with code " + ExitCode.NORMAL_END_OF_EXECUTION);
           exitCode.setExitCode(ExitCode.NORMAL_END_OF_EXECUTION);
       }
       else {
           logger.info("Exit with code " + ExitCode.ABNORMAL_END_OF_EXECUTION_WARNING);
           exitCode.setExitCode(ExitCode.ABNORMAL_END_OF_EXECUTION_WARNING);
       }
    }
}

But I don't force the application to exit with System.exit() from this class. I've implemented a simple singleton like this:

public class SingletonExitCode {

    public ExitCode exitCode = ExitCode.ABNORMAL_END_OF_EXECUTION_WARNING; // Default code 3
    private static SingletonExitCode instance = new SingletonExitCode();

    private SingletonExitCode() {}

    public static SingletonExitCode getInstance() {
        return instance;
    }

    public void setExitCode(ExitCode exitCode) {
        this.exitCode = exitCode;
    }
}

and I ask the ExitCode from my singleton after closing Spring context:

@SpringBootApplication
@EnableBatchProcessing
@Import(CoreCommonsAppComponent.class)
public class Application {
    // a lot of nice things

    public static void main(String... args) throws Exception{
        ApplicationContext context = SpringApplication.run(Application.class, args);
        logger.info("================================================");
        SpringApplication.exit(context);
        System.exit(SingletonExitCode.getInstance().exitCode.getCode());
    }
}

I did this because if we exit directly from JobCompletionNotificationListener class we miss an important line in the logs:

Job: [FlowJob: [name=writeErrorFromFile]] completed with the following parameters: [{-input.xml.file=c:/temp/unit-test-error.xml, -spring.batch.job.names=writeErrorFromFile, run.id=15, input.xml.file=c:/temp/unit-test-error.xml}] and the following status: [FAILED]

And seems that Spring context is not properly closed.

Solution 2

Despite of exit-status of Sprint-Batch's Job (i.e. COMPLETED or FAILED), java process will be completed successfully (and you will get process exit-code as 0).

If you want a custom exit-code for java process so that you can use it any script or somewhere else, you can use JobExecutionListener.

You can check the job's exitStatus in afterJob() and accordingly exit the java process with your desired exit-code (i.e. 4 for FAILURE)

Example of JobExecutionListener

public class InterceptingExitStatus implements JobExecutionListener{

    @Override
    public void beforeJob(JobExecution jobExecution) {
    }

    @Override
    public void afterJob(JobExecution jobExecution) {
        ExitStatus exitStatus = jobExecution.getExitStatus() ;

        if(exitStatus == ExitStatus.COMPLETED ){
            System.exit(0);
        }

        if(exitStatus == ExitStatus.FAILED ){
            System.exit(4);
        }
    }

}

and this is how you can configure job-listener in the xml file -

<job id="job">
....
....

    <listeners>
        <listener ref="interceptingExitStatus "/>
    </listeners>
</job>
Share:
11,784

Related videos on Youtube

Grechka Vassili
Author by

Grechka Vassili

Apparently, this user prefers to keep an air of mystery about them.

Updated on June 04, 2022

Comments

  • Grechka Vassili
    Grechka Vassili almost 2 years

    I have one jar with several jobs, I want to execute only one job each time and retrieve a custom exit code.

    For example, I have basic job (retrieveErrorsJob) configuration with one step that will read an input XML file and write the data in specific database table.

    Application class

    @SpringBootApplication
    @EnableBatchProcessing
    @Import(CoreCommonsAppComponent.class)
    public class Application {
    
        private static final Logger logger = LoggerFactory.getLogger(Application.class);
        private ConfigurationConstants constants;
    
        @Autowired
        public Application(ConfigurationConstants constants) {
            this.constants = constants;
        }
    
        @EventListener(ApplicationStartedEvent.class)
        public void idApplication()
        {
            logger.info("================================================");
            logger.info(constants.APPLICATION_NAME() + "-v." + constants.APPLICATION_VERSION() + " started on " + constants.REMOTE_HOST());
            logger.info("------------------------------------------------");
        }
    
        public static void main(String... args) throws Exception{
            ApplicationContext context = SpringApplication.run(Application.class, args);
            logger.info("================================================");
            SpringApplication.exit(context);
        }
    }
    

    I can choose one job from command line:

    java -jar my-jar.jar --spring.batch.job.names=retrieveErrorsJob --input.xml.file=myfile.xml

    Spring Batch starts the correct job.

    The problem is that I need the jar to return a custom process exit integer like ExitCode.FAILED == 4 etc. But I always have a ZERO (if ExitCode = SUCCESS or FAILED).

    As per the docs, I need to implement ExitCodeMapper interface.

    Code (not finished)

    public class CustomExitCodeMapper implements ExitCodeMapper {
    
    private static final int NORMAL_END_EXECUTION = 1;
    private static final int NORMAL_END_WARNING = 2;
    private static final int ABNORMAL_END_WARNING = 3;
    private static final int ABNORMAL_END_ERROR = 4;
    
    @Override
    public int intValue(String exitCode) {
    
        System.out.println("EXIT CODE = " + exitCode);
    
        switch (exitCode)
        {
            case "FAILED":
                return ABNORMAL_END_WARNING;
            default:
                return NORMAL_END_EXECUTION;
        }
    }
    

    }

    I can't find a way to use this custom implementation. I could set the custom implementation to CommandLineJobRunner but how to use this class?

  • lauksas
    lauksas almost 4 years
    That was my first try, happens that sometimes spring does not have time to update the job status in the database because it shuts down the connection after System.exit so look for that
  • Joy
    Joy about 2 years
    Would it not kill the application immediately ? How to exit the application after the job has finished ?