简体   繁体   中英

Spring batch return custom process exit code

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?

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.

Despite of exit-status of Sprint-Batch's Job (ie 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 (ie 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>

Spring Boot and Sring Batch already have an internal solution for this, all you need is an extra line of code:

System.exit(SpringApplication.exit(applicationContext));

Here is another example:

public class BatchApplication {

    public static void main(String[] args) {
        ApplicationContext applicationContext = SpringApplication.run(BatchApplication.class, args);
        System.exit(SpringApplication.exit(applicationContext));
    }

}

EDIT: If you would like to know how it works check this class: org.springframework.boot.autoconfigure.batch.JobExecutionExitCodeGenerator

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM