30

My Spring Boot application is not a web server, but it's a server using custom protocol (using Camel in this case).

But Spring Boot immediately stops (gracefully) after started. How do I prevent this?

I'd like the app to stop if Ctrl+C or programmatically.

@CompileStatic
@Configuration
class CamelConfig {

    @Bean
    CamelContextFactoryBean camelContext() {
        final camelContextFactory = new CamelContextFactoryBean()
        camelContextFactory.id = 'camelContext'
        camelContextFactory
    }

}
rbento
  • 9,919
  • 3
  • 61
  • 61
Hendy Irawan
  • 20,498
  • 11
  • 103
  • 114

9 Answers9

37

I found the solution, using org.springframework.boot.CommandLineRunner + Thread.currentThread().join(), e.g.: (note: code below is in Groovy, not Java)

package id.ac.itb.lumen.social

import org.slf4j.LoggerFactory
import org.springframework.boot.CommandLineRunner
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication

@SpringBootApplication
class LumenSocialApplication implements CommandLineRunner {

    private static final log = LoggerFactory.getLogger(LumenSocialApplication.class)

    static void main(String[] args) {
        SpringApplication.run LumenSocialApplication, args
    }

    @Override
    void run(String... args) throws Exception {
        log.info('Joining thread, you can press Ctrl+C to shutdown application')
        Thread.currentThread().join()
    }
}
Hendy Irawan
  • 20,498
  • 11
  • 103
  • 114
23

As of Apache Camel 2.17 there is a cleaner answer. To quote http://camel.apache.org/spring-boot.html:

To keep the main thread blocked so that Camel stays up, either include the spring-boot-starter-web dependency, or add camel.springboot.main-run-controller=true to your application.properties or application.yml file.

You will want the following dependency too:

<dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-spring-boot-starter</artifactId> <version>2.17.0</version> </dependency>

Clearly replace <version>2.17.0</version> or use the camel BOM to import dependency-management information for consistency.

jmkgreen
  • 1,633
  • 14
  • 22
15

An example implementation using a CountDownLatch:

@Bean
public CountDownLatch closeLatch() {
    return new CountDownLatch(1);
}

public static void main(String... args) throws InterruptedException {
    ApplicationContext ctx = SpringApplication.run(MyApp.class, args);  

    final CountDownLatch closeLatch = ctx.getBean(CountDownLatch.class);
    Runtime.getRuntime().addShutdownHook(new Thread() {
        @Override
        public void run() {
            closeLatch.countDown();
        }
    });
    closeLatch.await();
}

Now to stop your application, you can look up the process ID and issue a kill command from the console:

kill <PID>
Willy du Preez
  • 1,063
  • 10
  • 12
5

Spring Boot leaves the task of running the application to the protocol around which the application is implemented. See, for example, this guide:

Also required are some housekeeping objects like a CountDownLatch to keep the main thread alive...

So the way of running a Camel service, for example, would to be to run Camel as a standalone application from your main Spring Boot application class.

Anatoly
  • 456
  • 5
  • 13
4

All threads are completed, the program will close automatically. So, register an empty task with @Scheduled will create a loop thread to prevent shutdown.

file application.yml

spring:
  main:
    web-application-type: none

file DemoApplication.java

@SpringBootApplication
@EnableScheduling
public class DemoApplication {

   public static void main(String[] args) {
       SpringApplication.run(DemoApplication.class, args);
   }

}

file KeepAlive.java

@Component
public class KeepAlive {

   private static final Logger log = LoggerFactory.getLogger(ScheduledTasks.class);
   private static final SimpleDateFormat dateFormat =  new SimpleDateFormat("HH:mm:ss");

   @Scheduled(fixedRate = 1 * 1000 * 60) // 1 minute
   public void reportCurrentTime() {
       log.info("Keepalive at time {}", dateFormat.format(new Date()));
   }
}
frhack
  • 4,862
  • 2
  • 28
  • 25
izee
  • 41
  • 2
4

This is now made even simpler.

Just add camel.springboot.main-run-controller=true to your application.properties

slfan
  • 8,950
  • 115
  • 65
  • 78
Venkat
  • 314
  • 3
  • 10
0

My project is NON WEB Spirng Boot. My elegant solution is create a daemon thread by CommandLineRunner. Then, Application do not shutdown immediately.

 @Bean
    public CommandLineRunner deQueue() {
        return args -> {
            Thread daemonThread;
            consumer.connect(3);
            daemonThread = new Thread(() -> {
                try {
                    consumer.work();
                } catch (InterruptedException e) {
                    logger.info("daemon thread is interrupted", e);
                }
            });
            daemonThread.setDaemon(true);
            daemonThread.start();
        };
    }
galaxyAbstractor
  • 537
  • 1
  • 4
  • 13
Cheng
  • 316
  • 3
  • 9
-3

To keep the java process alive when not deploying a web application set the webEnvironment property to false like so:

 SpringApplication sa = new SpringApplication();
 sa.setWebEnvironment(false); //important
 ApplicationContext ctx = sa.run(ApplicationMain.class, args);
JARC
  • 5,288
  • 8
  • 38
  • 43
-5

for springboot app to run continously it has to be run in a container, otherwise it is just like any java app all threads are done it finishes, you can add

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

and it will turn it into webapp, if not you are responsible keeping it alive in your implementation

mariubog
  • 1,498
  • 15
  • 16
  • Like I said above, it's not a web server. So my question is... how do I simulate Tomcat's behavior? Do I stall the main thread or something like that? What's the "official" way to do this? – Hendy Irawan Jan 19 '15 at 08:31