Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CTRL+C w/ Spring Boot & Gradle Kills Gradle Daemon

I use the Spring Boot Gradle plugin to start a Tomcat server & my app. I start the Tomcat server via gradle bootRun. I've also enabled the Gradle daemon, in hopes to make Gradle builds go faster.

However, enabling the daemon is for naught. Every time I stop the server via Ctrl + C, then start the server again with gradle bootRun, I encounter the message:

Starting a new Gradle Daemon for this build (subsequent builds will be faster).

Ctrl + C not only stops the Tomcat server under the covers of Spring Boot, but also kills the Gradle daemon. Which defeats the purpose of Gradle's daemon mode.

Is there a better way I should be stopping the server, hopefully via command line interface in the same terminal for which I started tomcat with gradle bootRun, that keeps the Gradle daemon alive?

like image 972
McNinja Avatar asked Aug 24 '16 12:08

McNinja


People also ask

How do I turn on actuators in spring boot?

To enable Spring Boot actuator endpoints to your Spring Boot application, we need to add the Spring Boot Starter actuator dependency in our build configuration file. Maven users can add the below dependency in your pom. xml file. Gradle users can add the below dependency in your build.

What is the difference b/w spring and spring boot?

Spring Boot is basically an extension of the Spring framework, which eliminates the boilerplate configurations required for setting up a Spring application. It takes an opinionated view of the Spring platform, which paves the way for a faster and more efficient development ecosystem.

How do I gracefully shutdown spring services?

In the Spring Boot Document, they said that 'Each SpringApplication will register a shutdown hook with the JVM to ensure that the ApplicationContext is closed gracefully on exit. ' When I click ctrl+c on the shell command, the application can be shutdown gracefully.


2 Answers

This is still an issue in Gradle 4. My best compromise/solution (building off charlie_pl's answer):

  1. Press ctrl+z to send the running process to the background.
  2. Kill the process like: kill $(ps aux | grep "MyApp" | grep -v grep | awk '{print $2}')
  3. Restart: ./gradlew run ...
like image 70
Jacob Brown Avatar answered Oct 05 '22 20:10

Jacob Brown


I'm not familiar with the Spring Boot plugin, so presumably there is no 'bootStop' command (as there is in the Jetty plugin). Also, after an extensive search, I don't think there is a command-line option for the desired result.

One option, though admittedly a kludge, is to use the Tooling API. (Full code example is here.)

The idea is to start the long-running task in a Groovy script. On command, the script will stop the task and call gradle tasks to tickle the daemon.

From the GitHub code linked above, a long-running task could be:

task runService() << {
    ant.delete(file: "runService.log")
    def count = 0
    while(true) {
        new File("runService.log").withWriterAppend {
            it.writeLine("[runService] count: ${count}")
        }
        println "sleeping ...."
        try { Thread.sleep(5 * 1000) } catch (Exception ex) {}
        count++
    }
}

The idea behind the Groovy script is to start the task in a background thread, and then send a cancel token upon receipt of the command.

For clarity, I'll illustrate two sections. The first section is the background thread:

class BuildRunner implements Runnable {
    def connector
    def tokenSource
    def taskName

    BuildRunner(connector, tokenSource, taskName) {
        this.connector = connector
        this.tokenSource = tokenSource
        this.taskName = taskName
    }

    public void run() {
        def connection = connector.connect()        
        try {            
            def build = connection.newBuild()
            build.withCancellationToken(tokenSource.token())
            build.setStandardOutput(System.out)
            build.setStandardError(System.err)
            build.forTasks(taskName)
            build.run()
            println "${taskName} is finishing ..."
        } catch(BuildCancelledException bcex) {
            println "received cancel signal"
            println "tickling daemon ..."
            tickleDaemon(connector)
            println "Done."
            System.exit(0)
        } catch(Exception ex) {
            println "caught exception : " + ex
        } finally {            
          connection.close()        
        }        
    }

    def tickleDaemon = { connector ->
        final String TASKS = "tasks"
        def connection = connector.connect()        
        def build = connection.newBuild()
        build.forTasks(TASKS)
        build.run()
    }
}

and the other section is the main console:

// main -----------

// edit as appropriate
final String TASK_NAME = "runService"
final String GRADLE_INSTALL_DIR = "/Users/measter/tools/gradle-2.14.1"
final String PROJECT_DIR = "../service"

def connector = GradleConnector.newConnector()
connector.useInstallation(new File(GRADLE_INSTALL_DIR))
connector.forProjectDirectory(new File(PROJECT_DIR))

def tokenSource = connector.newCancellationTokenSource()

println "starting ${TASK_NAME}"
def buildRunner = new BuildRunner(connector, tokenSource, TASK_NAME)
new Thread(buildRunner).start()

def console = new Scanner(System.in)
println "Enter a command (S: stop task, Q: quit): "

while (console.hasNextLine()) {
    def lineTokenizer = new Scanner(console.nextLine())
    String token = lineTokenizer.next()
    if (token.equalsIgnoreCase("S")) {
        tokenSource.cancel()
    } else if (token.equalsIgnoreCase("Q")) {
        println "Done."
        System.exit(0)
    }
}

This code can easily be customized to execute other tasks, to restart a task etc. It hints at a glorified wrapper around personal command-line usage.

like image 38
Michael Easter Avatar answered Oct 05 '22 19:10

Michael Easter