Zhubin Salehi
Zhubin Salehi

Reputation: 291

How can I abort Spring-Boot startup?

I'm writing a Spring-Boot application to monitor a directory and process files that are being added to it. I register the directory with WatchService in a configuration class:

@Configuration
public class WatchServiceConfig {

    private static final Logger logger = LogManager.getLogger(WatchServiceConfig.class);

    @Value("${dirPath}")
    private String dirPath;

    @Bean
    public WatchService register() {
        WatchService watchService = null;

        try {
            watchService = FileSystems.getDefault().newWatchService();
            Paths.get(dirPath).register(watchService, ENTRY_CREATE);
            logger.info("Started watching \"{}\" directory ", dlsDirPath);
        } catch (IOException e) {
            logger.error("Failed to create WatchService for directory \"" + dirPath + "\"", e);
        }

        return watchService;
    }
}

I would like to abort Spring Boot startup gracefully if registering the directory fails. Does anybody know how I can do this?

Upvotes: 18

Views: 11528

Answers (4)

M. Justin
M. Justin

Reputation: 21114

If you are OK with the messaging in the current IOException, you can have your bean throw it to abort startup:

@Bean
public WatchService watchService() throws IOException {
    WatchService watchService = FileSystems.getDefault().newWatchService();
    Paths.get(dirPath).register(watchService, ENTRY_CREATE);
    logger.info("Started watching \"{}\" directory ", dlsDirPath);
}

If you want a more friendly error message than the default IOException (to better help point users at the error), you can throw your own exception with a customized exception message:

@Bean
public WatchService watchService() {
    try {
        WatchService watchService = FileSystems.getDefault().newWatchService();
        Paths.get(dirPath).register(watchService, ENTRY_CREATE);
        logger.info("Started watching \"{}\" directory ", dlsDirPath);
        return watchService;
    } catch (IOException e) {
        throw new IllegalStateException(
                "Failed to create WatchService for directory \"" + dirPath + "\"", e);
    }
}

Upvotes: 0

Michał Mielec
Michał Mielec

Reputation: 1651

https://docs.spring.io/spring-boot/docs/2.1.1.RELEASE/reference/htmlsingle/#boot-features-application-exit

Each SpringApplication will register a shutdown hook with the JVM to ensure that the ApplicationContext is closed gracefully on exit. All the standard Spring lifecycle callbacks (such as the DisposableBean interface, or the @PreDestroy annotation) can be used.

In addition, beans may implement the org.springframework.boot.ExitCodeGenerator interface if they wish to return a specific exit code when the application ends.

You should implement @PreDestroy methods which releases resources / files. Then during startup, when you detect some error, you could throw some RuntimeException - it start closing application context.

Upvotes: -2

Abhijit Sarkar
Abhijit Sarkar

Reputation: 24518

The accepted answer is correct, but unnecessarily complex. There's no need for a Condition, and then checking for the existence of the bean, and then closing the ApplicationContext. Simply checking for the presence of the directory during WatchService creation, and throwing an exception will abort the application startup due to failure to create the bean.

Upvotes: 7

Kyle Anderson
Kyle Anderson

Reputation: 7031

Get the Application Context, e.g.:

@Autowired
private ConfigurableApplicationContext ctx;

Then call the close method if you cannot find the directory:

ctx.close();

That gracefully shutdowns the Application Context and thus the Spring Boot Application itself.

Update:

A more detailed example based on the code provided in the Question.

Main Class

@SpringBootApplication
public class GracefulShutdownApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext ctx = SpringApplication.run(GracefulShutdownApplication.class, args);
        try{
            ctx.getBean("watchService");
        }catch(NoSuchBeanDefinitionException e){
            System.out.println("No folder to watch...Shutting Down");
            ctx.close();
        }
    }

}

WatchService Configuration

@Configuration
public class WatchServiceConfig {

    @Value("${dirPath}")
    private String dirPath;

    @Conditional(FolderCondition.class)
    @Bean
    public WatchService watchService() throws IOException {
        WatchService watchService = null;
        watchService = FileSystems.getDefault().newWatchService();
        Paths.get(dirPath).register(watchService, ENTRY_CREATE);
        System.out.println("Started watching directory");
        return watchService;
    }

Folder Condition

public class FolderCondition implements Condition{

    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        String folderPath = conditionContext.getEnvironment().getProperty("dirPath");
        File folder = new File(folderPath);
        return folder.exists();
    }
}

Make the WatchService Bean @Conditional based on whether the directory is present or not. Then in your Main Class, check whether the WatchService Bean exists, and if not shutdown the Application Context by calling close().

Upvotes: 13

Related Questions