Question

I am running into various problems when trying to run android start-server from within Java as external process. The Java is called by Gradle. Let me describe to you what is exactly happening in various scenarios:

Environment

  • Windows 7 X64
  • Java 7
  • commons-exec-1.1
  • Gradle 1.6
  • Android API 17
  • IntelliJ IDEA 12.1.4 Community Edition

Assumption
adb daemon is killed and will start up when calling adb start-server.

Case 1

This code:

DefaultExecutor executor = new org.apache.commons.exec.DefaultExecutor();
executor.execute(org.apache.commons.exec.CommandLine.parse("adb start-server"));
log.info("Checkpoint!");

When run from Gradle run task of the application plugin, will display the start-server output, i.e.:

* daemon not running. starting it now on port 5037 *
* daemon started successfully *

and then it will hang, i.e. "Checkpoint!" will never be logged. Killing the adb.exe process manually will cause the code to continue execution.

Question 1

Why this call blocks? When adb start-server command is run from terminal, after a couple of seconds the control is returned to the terminal, so why it doesn't happen in the code?

Case 2

If instead I use directly the Java runtime like so:

Runtime.getRuntime().exec(new String[]{"adb", "start-server"});
log.info("Checkpoint!");
System.exit(0);

If calling from Gradle as previously, the "Checkpoint!" will be logged. However, the execution will hang on System.exit(0). Killing adb.exe manually will again make Gradle call finish.

In this case no output of adb start-server is displayed.

Interesting thing is that when, instead of Gradle, I run the application from IntelliJ IDEA with build setup mimicking that of Gradle's, everything works fine and the application finishes properly.

Question 2

Why Gradle hangs on System.exit(0) and IntelliJ doesn't? Is this somehow related to the fact that Gradle itself is a process that internally calls Java and in case of IntelliJ, Java is called immediately without any indirection? Why does it matter?

Question 3

Ultimately, I want to be able to run this from Gradle without hangs of any kind. Logging output of adb start-server would be a bonus. I would greatly appreciate any hints how to do this.

Was it helpful?

Solution 2

Ultimately I solved the problem by doing the following:

  1. Inside my program, for calling adb start-server, I no longer use org.apache.commons.exec.DefaultExecutor.execute("adb start-server") nor Java's Runtime.getRuntime().exec("adb start-server").
    Instead, I use java.lang.ProcessBuilder("adb start-server").inheritIO().start().waitFor(). Note the inheritIO() is added in Java 7 and allows for online reading of the started process stdout, among others.
    EDIT The inheritIO() in Gradle has no effect when Gradle is called from CLI directly as opposed to being called by IntelliJ IDEA. See this question for details. Probably implementing StreamGobbler as described here would fix the problem.

  2. In Gradle, instead of Gradle's application plugin's run task, I again use ProcessBuilder reusing run tasks variables. It looks like so:

apply plugin: 'java'
apply plugin: 'application'

sourceCompatibility = '1.7'
targetCompatibility = '1.7'

// configuration-time of the original run task
run { 
    main = com.example.MainClass; // without this, our custom run will try to run "null"
}

task myRun(dependsOn: build) {

        // execution-time of our custom run task.
        doFirst {

            ProcessBuilder pb = new ProcessBuilder(tasks['run'].commandLine);
            pb.directory(tasks['run'].workingDir);

            // works when the gradle command is executed from IntelliJ IDEA, has no effect when executed from standalone CLI interface.
            pb.inheritIO(); 

            Process proc = pb.start();
            proc.waitFor();
        }
    }

As for the reason of the strange behaviors, I don't have any definite answer, so I refrain from further comments.

EDIT 9 July 2013
This question seems to point out the answer to Question 2 is: on windows, the parent process waits for child process before it terminates. Unfortunately, the solutions proposed there do not work because of problem stated in Question 1.

Hope this helps,
Konrad

OTHER TIPS

Forgive me if I'm answering the wrong question, but if I wanted to start a server from gradle (we're doing this for tomcat, sonicMQ, ApacheDS, and EnterpriseDB) I would start with the gradle Exec task and only try something else if it was inadequate.

task startADB(type: Exec) {
    commandLine 'adb', 'start-server'
}

Unfortunately I can't answer any of your questions about various bits of code hanging, because I haven't a clue! I just know that if I wanted to launch a server from gradle that's how I'd do it. In fact it's how I am doing it. Another - slightly more complicated - option is to use the tanuki wrapper or something similar to it. You can still start and stop it from gradle, but you get some advanced features.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top