Giter Site home page Giter Site logo

nuprocess's Introduction

NuProcess

A low-overhead, non-blocking I/O, external Process execution implementation for Java. It is a replacement for java.lang.ProcessBuilder and java.lang.Process.

Have you ever been annoyed by the fact that whenever you spawn a process in Java you have to create two or three "pumper" threads (for every process) to pull data out of the stdout and stderr pipes and pump data into stdin? If your code starts a lot of processes you can have dozens or hundreds of threads doing nothing but pumping data.

NuProcess uses the JNA library to use platform-specific native APIs to achieve non-blocking I/O on the pipes between your Java process and the spawned processes:

  • Linux: uses epoll
  • MacOS X: uses kqueue/kevent
  • Windows: uses IO Completion Ports

Maven

<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>nuprocess</artifactId>
    <version>2.0.6</version>
    <scope>compile</scope>
</dependency>

👉 Note: Versions 1.0.0 and above contain breaking API changes from 0.9.7.

It's mostly about the memory

Speed-wise, there is not a significant difference between NuProcess and the standard Java Process class, even when running 500 concurrent processes. On some platforms such as MacOS X or Linux, NuProcess is 20% faster than java.lang.Process for large numbers of processes.

However, when it comes to memory there is a significant difference. The overhead of 500 threads, for example, is quite large compared to the one or few threads employed by NuProcess.

Additionally, on unix-based platforms such as Linux, when creating a new process java.lang.Process uses a fork()/exec() operation. This requires a temporary copy of the Java process (the fork), before the exec is performed. When running tests on Linux, in order to spawn 500 processes required setting the JVM max. memory to 3Gb (-Xmx3g). NuProcess uses a variant of fork() called vfork(), which does not impose this overhead. NuProcess can comfortably spawn 500 processes even when running the JVM with only 128Mb.

Example

Like the Java ProcessBuilder, NuProcess offers NuProcessBuilder, so building a process is fairly simple. Let's make a simple example where we use the Unix "cat" command. When launched with no parameters, cat reads from STDIN and echos the output to STDOUT. We're going to start the cat process, write "Hello world!" to its STDIN, and read the echoed reply from STDOUT and print it. Let's build and start the process.

NuProcessBuilder pb = new NuProcessBuilder(Arrays.asList("/bin/cat"));
ProcessHandler handler = new ProcessHandler();
pb.setProcessListener(handler);
NuProcess process = pb.start();
process.wantWrite();
process.waitFor(0, TimeUnit.SECONDS); // when 0 is used for waitFor() the wait is infinite

You'll notice the ProcessHandler in code above. This is a class you provide which receives callbacks from the process to handle input, output, termination, etc. And notice the wantWrite() call, this expresses that we have something we want to write to the process, so our ProcessHandler will be called back to perform the write. Here's what ProcessHandler looks like for our example:

class ProcessHandler extends NuAbstractProcessHandler {
   private NuProcess nuProcess;

   @Override
   public void onStart(NuProcess nuProcess) {
      this.nuProcess = nuProcess;
   }
   
   @Override
   public boolean onStdinReady(ByteBuffer buffer) {
      buffer.put("Hello world!".getBytes());
      buffer.flip();
      return false; // false means we have nothing else to write at this time
   }

   @Override
   public void onStdout(ByteBuffer buffer, boolean closed) {
      if (!closed) {
         byte[] bytes = new byte[buffer.remaining()];
         // You must update buffer.position() before returning (either implicitly,
         // like this, or explicitly) to indicate how many bytes your handler has consumed.
         buffer.get(bytes);
         System.out.println(new String(bytes));

         // For this example, we're done, so closing STDIN will cause the "cat" process to exit
         nuProcess.closeStdin(true);
      }
   }
}

Synchronous Operation

NuProcess does allow you to perform synchronous writes to the stdin of the spawned process. Even though the writes are synchronous they are non-blocking; meaning the write returns immediately. In this model, you do not use NuProcess.wantWrite() and your onStdinReady() method will not be called. If you extend the NuAbstractProcessHandler you do not need to provide an implementation of onStdinReady(). Use the NuProcess.writeStdin() method to write data to the process. This method will return immediately and writes are queued and occur in order. Read the JavaDoc for the NuProcess.writeStdin() method for cautions and caveats.

In the synchronous model, the above example would look like this:

NuProcessBuilder pb = new NuProcessBuilder(Arrays.asList("/bin/cat"));
ProcessHandler handler = new ProcessHandler();
pb.setProcessListener(handler);
NuProcess process = pb.start();

ByteBuffer buffer = ByteBuffer.wrap("Hello, World!".getBytes());
process.writeStdin(buffer);

process.waitFor(0, TimeUnit.SECONDS); // when 0 is used for waitFor() the wait is infinite

And the handler:

class ProcessHandler extends NuAbstractProcessHandler {
   private NuProcess nuProcess;

   @Override
   public void onStart(NuProcess nuProcess) {
      this.nuProcess = nuProcess;
   }
   
   public void onStdout(ByteBuffer buffer, boolean closed) {
      if (!closed) {
         byte[] bytes = new byte[buffer.remaining()];
         // You must update buffer.position() before returning (either implicitly,
         // like this, or explicitly) to indicate how many bytes your handler has consumed.
         buffer.get(bytes);
         System.out.println(new String(bytes));

         // For this example, we're done, so closing STDIN will cause the "cat" process to exit
         nuProcess.closeStdin(true);
      }
   }
}

JavaDocs

You can read the JavaDoc here. Make sure you read and fully understand the JavaDoc for the NuProcessHandler interface as it is your primary contract with NuProcess.

Settings

These are settings that can be defined as System properties that control various behaviors of the NuProcess library. You typically do not need to modify these.

com.zaxxer.nuprocess.threads

This setting controls how many threads are used to handle the STDIN, STDOUT, STDERR streams of spawned processes. No matter how many processes are spawned, this setting will be the maximum number of threads used. Possible values are:

  • auto (default) - this sets the maximum number of threads to the number of CPU cores divided by 2.
  • cores - this sets the maximum number of threads to the number of CPU cores.
  • <number> - the sets the maximum number of threads to a specific number. Often 1 will provide good performance even for dozens of processes.

The default is auto, but in reality if your child processes are "bursty" in their output, rather than producing a constant stream of data, a single thread may provide equivalent performance even with hundreds of processes.

com.zaxxer.nuprocess.softExitDetection

On Linux and Windows there is no method by which you can be notified in an asynchronous manner that a child process has exited. Rather than polling all child processes constantly NuProcess uses what we call "Soft Exit Detection". When a child process exits, the OS automatically closes all of it's open file handles; which is something about which we can be notified. So, on Linux and Windows when NuProcess determines that both the STDOUT and STDERR streams have been closed in the child process, that child process is put into a "dead pool". The processes in the dead pool are polled to determine when they have truly exited and what their exit status was. See com.zaxxer.nuprocess.deadPoolPollMs

The default value for this property is true. Setting this value to false will completely disable process exit detection, and the ``NuProcess.waitFor()" API MUST be used. Failure to invoke this API on Linux will result in an ever-growing accumulation of "zombie" processes and eventually an inability to create new processes. There is very little reason to disable soft exit detection unless you have child process that itself closes the STDOUT and STDERR streams.

com.zaxxer.nuprocess.deadPoolPollMs

On Linux and Windows, when Soft Exit Detection is enabled (the default), this property controls how often the processes in the dead pool are polled for their exit status. The default value is 250ms, and the minimum value is 100ms.

com.zaxxer.nuprocess.lingerTimeMs

This property controls how long the processing thread(s) remains after the last executing child process has exited. In order to avoid the overhead of starting up another processing thread, if processes are frequently run it may be desirable for the processing thread to remain (linger) for some amount of time (default 2500ms).

Related Projects

Charles Duffy has developed a Clojure wrapper library here. Julien Viet has developed a Vert.x 3 library here.

Limitations

The following limitations exist in NuProcess:

  • Currently only supports Linux, Windows, and MacOS X.
  • Java 7 and above
  • Linux support requires at least kernel version 2.6.17 or higher (kernels after June 2006)

nuprocess's People

Contributors

anantharaman93 avatar avrecko avatar balonus avatar benhumphreys-atlassian avatar bhamiltoncx avatar brettwooldridge avatar brucemcrooster avatar bturner avatar changvvb avatar fr0l avatar ilya-klyuchnikov avatar lfbayer avatar lis0x90 avatar mtyson avatar nataliejameson avatar neoware avatar pfxuan avatar vietj avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

nuprocess's Issues

Option to set namedPipeCounter in WindowsProcess

Windows:
Bug: If we launch a subprocess using NuProcess in jvm number one and wait for subprocess to finish (waitfor(0,sec)) assuming the subprocess takes a long time to finish, then we cannot launch another subprocess using NuProcess in jvm number two because namedPieCounter (WindowsProcess.java line 95) is set to 100 by default in both jvms so when checkPipeConnected(WindowsProcess.java line 501) is called we will have name collision on pipenames and the pipe will not connect.
Fix:
Give access to namePipeCounter so we can change it in onPreStart method.

ProcessKqueue.closeQueue filling up

I'm on Sierra 10.12.4, though I don't believe the exact version should matter.

On Mac OS X, when creating processes and never writing anything to their stdin, the closeQueue of every ProcessKqueue will only accumulate processes, and never be emptied.
This causes processes after 512 * numThreads to stop reporting their exits properly (BasePosixProcess.onExit leaves the happy path way too early).

It seems that processes are added to the closeQueue whenever they finish, but closeQueue is only drained when receiving a SIGUSR2, which only happens when queueing a write (or queueing a STDIN_CLOSED_PENDING_WRITE_TOMBSTONE).

To make it work, I could close each process' stdin right after starting them, but it doesn't look like I should have to.

Obtain PID

Using a library (or similar hackery) like this Flapdoodle, it is possible to obtain the PID of a standard Java Process object.

Is that possible to do with a NuProcess object?

Sequential execution of Nuprocess is throwing IllegalStateException exception

My use case is as follows:

  1. Scrape a webpage and extract all the images.
  2. One by one optimize those images by using linux binaries (optipng, turbojpeg etc).
  3. Above binaries are called from Nuprocess.

After first execution of external process I am getting following exception (subsequent execution failed):

java.lang.IllegalStateException: closeStdin() method has already been called.
    at com.zaxxer.nuprocess.internal.BasePosixProcess.wantWrite(BasePosixProcess.java:178)

Here is how the invocation of NuProcess looks like:

        NuProcessBuilder pb = new NuProcessBuilder(commandList);
        final ProcessHandler handler = new ProcessHandler(original.getAbsolutePath(), destination.getAbsolutePath(), jobId);
        pb.setProcessListener(handler);
        NuProcess process = pb.start();
        process.wantWrite();
        process.waitFor(0, TimeUnit.SECONDS);

Also my implementation of ProcessHandler

public class ProcessHandler  extends NuAbstractProcessHandler {
    private NuProcess nuProcess;
    private String originalFile;
    private String compressedFile;
    private String requestId;
    final Logger.ALogger LOGGER = Logger.of(this.getClass());

    public ProcessHandler(String originalFile, String compressedFile, String requestId){
        this.compressedFile = compressedFile;
        this.originalFile = originalFile;
        this.requestId = requestId;
    }

    @Override
    public void onStart(NuProcess nuProcess) {
        LOGGER.info("Request Id::" + requestId + "::Received encoding request for file::" + originalFile);
        this.nuProcess = nuProcess;
    }

    public void onExit(int exitCode){
        LOGGER.info("Request Id::" + requestId + "::Completed encoding of file::" + originalFile);
    }

    @Override
    public boolean onStdinReady(ByteBuffer buffer) {
        LOGGER.info("Request Id::" + requestId + "::Queueing encoding of file::" + originalFile);
        buffer.flip();
        return false; // false means we have nothing else to write at this time
    }

    @Override
    public void onStdout(ByteBuffer buffer) {
        if (buffer == null) {
            return;
        }
        byte[] bytes = new byte[buffer.remaining()];
        buffer.get(bytes);
        // We're done, so closing STDIN will cause the process to exit
        nuProcess.closeStdin();
    }
}

0.9.2+ not available on Maven Central

Howdy --

I notice that 0.9.2 and 0.9.3 releases have been tagged; however, Central is showing 0.9.1 at this time.

Given that there are a substantial number of enhancements that went into 0.9.2, it would be nice to have newer binaries available.

Killed processes returns with exit code 0

Hi,
When launching a process which is later killed i always see a onExit(int statusCode) call with statusCode=0 - which make it look like the process finished successfully, while in reality it was killed.

e.g. a shell script called killtest.sh with the contents:

#!/bin/bash
kill -6 $$

will give me the following output in bash:

$ ./killtest.sh ; echo $?
Abort trap: 6
134

but when executing with NuProcess my callback receives a statusCode of 0 - how can I detect that the process was killed and not finished successfully? is this the intended behaviour? I'm not sure how a kill is actually propagated to the parent process.

the output of the following java code: https://gist.github.com/hpoul/b7ab5b58c32c7dfd463d

Started process
Command exited with exit code 0

i have tested this on mac and linux with nuprocess 0.9.4

thanks,
herbert

Problem with nonexistant executables on Linux

Hello,

On my system, NuProcess acts as if the execution of a nonexistant executable is always successful. I run Linux 4.6.2-1 and glibc 2.24-2. Attached below is the log of mvn install which shows tests breaking.

Cheers

[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building NuProcess 1.1.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-enforcer-plugin:1.0:enforce (enforce-maven) @ nuprocess ---
[INFO] 
[INFO] --- maven-resources-plugin:3.0.1:resources (default-resources) @ nuprocess ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /home/haveo/coding/NuProcess/src/main/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ nuprocess ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] --- maven-resources-plugin:3.0.1:testResources (default-testResources) @ nuprocess ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /home/haveo/coding/NuProcess/src/test/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ nuprocess ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] --- maven-surefire-plugin:2.19.1:test (default-test) @ nuprocess ---

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.zaxxer.nuprocess.FastExitingProcessTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.172 sec - in com.zaxxer.nuprocess.FastExitingProcessTest
Running com.zaxxer.nuprocess.InterruptTest
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.389 sec - in com.zaxxer.nuprocess.InterruptTest
Running com.zaxxer.nuprocess.EnvironmentTest
Started Java Process
Waited for Java Process
env: [PWD=/home/haveo/coding/NuProcess, _=/usr/lib/jvm/java-8-openjdk/jre/bin/java, PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/heroku/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/lib/jvm/default/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl, JAVA_HOME=/usr/lib/jvm/java-8-openjdk, SHLVL=1, MAVEN_CMD_LINE_ARGS= install, MAVEN_PROJECTBASEDIR=/home/haveo/coding/NuProcess, OLDPWD=/home/haveo/coding/NuProcess, NLSPATH=/usr/dt/lib/nls/msg/%L/%N.cat, XFILESEARCHPATH=/usr/dt/app-defaults/%L/Dt]
Started Nu Process
Waited for Nu Process
env: [PWD=/home/haveo/coding/NuProcess, _=/usr/lib/jvm/java-8-openjdk/jre/bin/java, JAVA_HOME=/usr/lib/jvm/java-8-openjdk, PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/heroku/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/lib/jvm/default/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl, SHLVL=1, MAVEN_CMD_LINE_ARGS= install, MAVEN_PROJECTBASEDIR=/home/haveo/coding/NuProcess, OLDPWD=/home/haveo/coding/NuProcess, NLSPATH=/usr/dt/lib/nls/msg/%L/%N.cat, XFILESEARCHPATH=/usr/dt/app-defaults/%L/Dt]
Started Java Process
Waited for Java Process
env: []
Started Nu Process
Waited for Nu Process
env: []
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.113 sec - in com.zaxxer.nuprocess.EnvironmentTest
Running com.zaxxer.nuprocess.CatTest
Tests run: 9, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 4.703 sec <<< FAILURE! - in com.zaxxer.nuprocess.CatTest
noExecutableFound(com.zaxxer.nuprocess.CatTest)  Time elapsed: 0.005 sec  <<< FAILURE!
java.lang.AssertionError: Output did not matched expected result expected:<-2147483648> but was:<0>
    at com.zaxxer.nuprocess.CatTest.noExecutableFound(CatTest.java:200)

Running com.zaxxer.nuprocess.ThreadedTest
2 Maximum memory used: 60964264
2 Total execution time (ms): 5766
3 Maximum memory used: 60800512
3 Total execution time (ms): 5766
0 Maximum memory used: 60964264
0 Total execution time (ms): 5769
1 Maximum memory used: 60964328
1 Total execution time (ms): 5769
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 5.76 sec - in com.zaxxer.nuprocess.ThreadedTest
Running com.zaxxer.nuprocess.DirectWriteTest
Writing: This is a test
Read: This is a test
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.018 sec - in com.zaxxer.nuprocess.DirectWriteTest

Results :

Failed tests: 
  CatTest.noExecutableFound:200 Output did not matched expected result expected:<-2147483648> but was:<0>

Tests run: 19, Failures: 1, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 15.796 s
[INFO] Finished at: 2016-10-18T17:38:02+02:00
[INFO] Final Memory: 15M/212M
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.19.1:test (default-test) on project nuprocess: There are test failures.
[ERROR] 
[ERROR] Please refer to /home/haveo/coding/NuProcess/target/surefire-reports for the individual test results.
[ERROR] -> [Help 1]
[ERROR] 
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR] 
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException

Starting daemonized processes

We are trying to switch from using the regular Runtime.exec() from java to the NuProcess lib. We are using this solely on Linux. We don't realy use async processes, e.g. when we execute a process we always need to wait untill its "done". Most of the time this is to read and parse output, this works great.

However, we also have a couple of daemon processes we start (more specific: kvm/qemu). The regular behavior in Java was to return when the process was executed correctly without writing anything to stdout/stdin or return when the process could not be started (it would write output to stderror then).

After our switch to NuProcess we can't "execute and return when started correctly" anymore. We use waitFor(0, ...) directly after executing to wait untill the process is don e but this call hangs untill we kill the executed process. I understand why this is (the process is still running) but im wondering if there is a solution so we get the same behavior as with Runtime.exec?

Unsupported operating system: freebsd

Caused by: java.lang.RuntimeException: Unsupported operating system: freebsd
at com.zaxxer.nuprocess.NuProcessBuilder.(NuProcessBuilder.java:80) ~[nuprocess-0.9.4.jar:na]
... 17 common frames omitted

Can it support sending key event into the child process?

Say that try to invoke a child process to run Plink(pure cli of Putty) to remote to a linux server and run the "more" command, the process is stuck on waiting for key event to scroll to next page, seems that nothing can do to let it continue. Any way to resolve this issue? Thanks.

Linux epoll event pool can be created more than once

This is a minor issue I noticed while debugging an unrelated problem.

ProcessEpoll's eventPool is a static field, so it is shared across all instances. But it is populated inside the ProcessEpoll constructor, which can be called more than once, depending on the number of processing threads. The constructor doesn't check if the pool was already created, so it will create a new pool and overwrite the existing one each time it is invoked.

This should be fixed so the event pool will only be created once. The constructor could check if eventPool != null, or the event pool creation could be moved to a static initializer block.

Weird lockup using NuProcess

Not sure if this is NuProcess's problem or a system related issue. I've modified your example program to see how it works with multiple threads. The behavior I'm seeing is very odd.

Here's the code.

http://pastebin.com/0ta5byrk

It runs fine on my mac, using Oracle Java 1.7 build 1.7.0_17-b02, with multiple threads and processes.
It locks up on my linux systems (Ubuntu and centos), using either OpenJDK or Oracle Java (both 1.7 and 1.8) if I try to use multiple threads.

Worse, I can't even compile that code on my linux box without getting this error.

java.lang.UnsatisfiedLinkError: Can't obtain updateLastError method for class com.sun.jna.Native

After digging, I found: java-native-access/jna#281 and saw the note about passing: -Djna.nosys=true ... This works, but only if I only use a single thread.

I'm not asking you to fix anything here, I'm just curious if you could run that program on your system, and let me know what you see. I'd like to know if this is something my operating system, java build, or a mistake in my code is causing from your perspective.

Thanks!

Pipes leak on OSX

This sample program leaks pipes until it crashes: https://gist.github.com/bertmaher/b3a54a6c4ed0eb5c52da91065af9d029. I think the problem is that closeStdin(true) tries to enqueue the process in the closeQueue, but that queue eventually fills up (and starts throwing, but the exception is swallowed by the method that calls onStart). Using closeStdin(false) solves the problem, but this behavior feels like a bug.

Process termination not detected

I'm running Linux (specifically, Mint 17.1) in VirtualBox. I'm trying to use NuProcess to execute a ping, then check the exit code to determine whether a destination is reachable.

The ping process terminates and goes into a zombie state, but NuProcess never detects that this has happened. I'm not sure why, but while attempting to debug the problem, I have determined that something seems to be wrong in ProcessEpoll. I set breakpoints in the process() method and stepped through it, and noticed that ident is always 0. So linuxProcess ends up always being null and the method returns without doing anything else.

I haven't touched any settings, so they should all have the default values.

My test code is below. The handler's onStart() gets invoked, but onExit() is never called. The System.in.read() is just there to block the main thread from terminating, to make it easier to see what's happening.

import java.util.ArrayList;

import com.zaxxer.nuprocess.NuAbstractProcessHandler;
import com.zaxxer.nuprocess.NuProcess;
import com.zaxxer.nuprocess.NuProcessBuilder;

public class NuProcessTest {

    public static void main(String[] args) throws Exception {
        ArrayList<String> pingCommand = new ArrayList<>(8);
        pingCommand.add("ping");
        pingCommand.add("-c");
        pingCommand.add("1");
        pingCommand.add("-W");
        pingCommand.add("2");
        pingCommand.add("www.example.com");

        NuAbstractProcessHandler handler = new NuAbstractProcessHandler() {
            @Override
            public void onStart(NuProcess nuProcess) {
                System.out.println("process started");
            }

            @Override
            public void onExit(int statusCode) {
                System.out.println("process terminated, code " + statusCode);
            }
        };

        new NuProcessBuilder(handler, pingCommand).start();

        System.in.read();
    }

}

EpollEvent memory alignment is incorrect

Foremost: This misalignment doesn't matter with the current code, and, further, it may not actually be misaligned on all platforms. But on Linux Mint 18 (Ubuntu 16.04) x86_64, it is misaligned.

Environment:

bturner@elysoun ~$ uname -a
Linux elysoun 4.10.0-38-generic #42~16.04.1-Ubuntu SMP Tue Oct 10 16:32:20 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
bturner@elysoun ~$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 5.4.0-6ubuntu1~16.04.5' --with-bugurl=file:///usr/share/doc/gcc-5/README.Bugs --enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-5 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-5-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-5-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-5-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.5)

It's quite simple to verify the misalignment. Here's a simple C program:

#include <sys/epoll.h>
#include <stdio.h>

int main(int argc, char *argv[]) {
    printf("sizeof(epoll_event): %lu\n", sizeof(struct epoll_event));

    return 0;
}

The result: 12. Calling new EpollEvent().size(), meanwhile, in the NuProcess code, returns 16. Using ALIGN_GNUC is adding 4 bytes of padding to the end of the structure.

Since nothing in the NuProcess code tries to marshal multiple EpollEvent structs in a single call the misalignment is benign, just a tiny overallocation in memory. But if you ever wanted to, say, update ProcessEpoll to pass a higher max than 1, with the current memory alignment setting it fails--EpollEvents beyond the first one appear uninitialized after the call returns >1.

Setting the alignment to ALIGN_NONE fixes this issue, at least for x86_64, and results in the correct size of 12.

On my system, the bits/epoll.h (/usr/include/x86_64-linux-gnu/bits/epoll.h) header has the following define:

#define __EPOLL_PACKED __attribute__ ((__packed__))

That's included by sys/epoll.h, which then has the following structure definition:

struct epoll_event
{
  uint32_t events;      /* Epoll events */
  epoll_data_t data;    /* User data variable */
} __EPOLL_PACKED;

__attribute__ ((__packed__)) disables inserting padding memory alignment, per GCC's documentation:

packed
This attribute, attached to struct or union type definition, specifies that each member (other than zero-width bit-fields) of the structure or union is placed to minimize the memory required. When attached to an enum definition, it indicates that the smallest integral type should be used. (GCC 7.2, but similar for earlier versions.)

Reactive Streams Support

The streams branch looks like it stalled, there were a couple of issues picked up under #53. Was there a serious issue, or did you just get fed up with it? 😉
I could really do with stream support and can offer my time, would you be open to a PR to get it into master and released if I can get it going?

current working directory (cwd) support

The README states that "NuProcess does not currently permit setting an alternate current working directory (cwd) for the child process."

Why does this limitation exist, and will be solved in near future?

Support more features for WindowsProcess

Hello,
Some functions are useful to control the sub-process, including:

  • WaitForInputIdle to determine if a GUI-based console is idle on user input
  • GenerateConsoleCtrlEvent + CREATE_NEW_PROCESS_GROUP to send the Ctrl events so that the sub-process is able to abort the running command

Hope those features can be supported, thanks.

Expose exitCode on NuProcess

Can we expose exitCode on the NuProcess interface? If there is no reason why we can't I can submit a PR for this change.

Package support for Java 1.6

The source code is JDK 6 compliance.
I was able to compile it with source 1.6 and it passed the tests.

Is there a reason the maven task (and packaging to maven central) is JDK7 ?

stdout/err buffer overflow behaviour

I've noticed that for Posix, if the stdout buffer becomes full then a RuntimeException("stdout buffer has no bytes remaining") is thrown (https://github.com/brettwooldridge/NuProcess/blob/master/src/main/java/com/zaxxer/nuprocess/internal/BasePosixProcess.java#L483). I've observed that this exception is caught at https://github.com/brettwooldridge/NuProcess/blob/master/src/main/java/com/zaxxer/nuprocess/internal/BaseEventProcessor.java#L83 whereupon the process is then declared as stopped.

My understanding from the API doc is that it is fine for a stdout handler to return the buffer that it was passed in the event that it is not in a position to consume it and that the stdout handler would then be called at a later time. My expectation then was that the upstream process would block until it could write more to stdout.

I was hoping that you could confirm the actual design. It seems that stdout handlers must always consume in order to avoid seeing that the process is represented as one that has stopped.

The same behaviour applies to stderr. Thanks.

Note that #53 is related, although that focuses more on a solution. This issue pertains to unexpected behaviour in accordance with the existing API doc.

Exec failure can be silently ignored

On Linux, attempting to run a process which does not exist (such that execve() returns ENOENT) can result in a zombie being left behind, and a LinuxProcess object for which isRunning returns True -- even when softExitDetection is enabled and the soft-exit polling interval has passed.

Below uses the Clojure core.async bindings at https://gist.github.com/charles-dyfis-net/b16926920d9ae05535db

Observed against NuProcess 0.9.1.

(def test-fail
  (run-process ["i-dont-exist"]))

config-server.process> (:proc test-fail)
#<LinuxProcess com.zaxxer.nuprocess.linux.LinuxProcess@402f54a3>
config-server.process> (.isRunning (:proc test-fail))
true
config-server.process> (bean (:proc test-fail))
{:stdout 83, :stdin 81, :stderr 85, :softExit false, :running true, :pid 9316, :class com.zaxxer.nuprocess.linux.LinuxProcess}

...results from ps:

cduffy    9189  0.0  0.0      0     0 pts/9    Z+   11:53   0:00 [java] <defunct>

...output from sysdig (for a different invocation, with the same result):

18113 12:34:59.911023556 0 <NA> (9253) < execve res=-2(ENOENT) exe= args= tid=9253(<NA>) pid=9253(<NA>) ptid=9250(java) cwd=/home/cduffy/VC/config-server fdlimit=4096

The empty exe value has been verified to be a sysdig bug; strace shows that the path is being correctly passed to the execve call.

Is there any way to set max memory used by the native library

Hi I would like to know is there any way to set the max native memory used by the library as the memory this library uses is not managed by GC.

We have our jvm process running in a small docker container which have a max memory limit of 4 GB. That should include both heap and non heap usage. If the overall container memory reaches near 4GB we have a kernel killing the processes.

Pipe and redirection support

Hi
I am trying to execute following command from nuprocess
cat file1 file2 file3 > outputfile
The command is valid in a linux shell but can't be executed through nuprocess. Is there any idea in which such types of commands can be run through nuprocess?

Feature: Allow close only on async write thread flush

Provide a mechanism by which users leveraging the writeStdin() mechanism can run .closeStdin() only after work pending on this thread has been flushed. A few potential approaches which come to mind:

  • Allow .closeStdin events to be queued for execution by the thread performing asynchronous writes (placed in the same queue as pending content, and thus running only after previously-queued content) rather than run immediately.
  • Trigger a callback when the input queue for a given process is completely flushed, allowing the user to call .closeStdin themselves if they so desire. (If the actual queue implementation is shared, this would require maintaining a per-NuProcess counter).

Closing stdin after all buffered writes are completed?

Is it possible to tell a NuProcess to close stdin after it finishes writing all of the supplied data asynchronously? In the example, you close stdin after you successfully read from the stdout of the child process. In my case, I am calling a tool which expect the parent to close stdin after writing all of the data it wants to send, then it processes, then it write back.

I tried directly writing to process.writeStdin() and by using the onStdinReady() callback. Calling nuProcess.closeStdin() does not appear to be the way to go, as the docs say it will close immediately. I do not see a method to check the process of the buffered writes to stdin.

CreateProcessW() failed, error: 2

Hello,

I'm experiencing following error when I try to test the example in your page except replacing /bin/cat as cmd.exe:

java.lang.RuntimeException: CreateProcessW() failed, error: 2
        at com.zaxxer.nuprocess.windows.WindowsProcess.start(WindowsProcess.java:262)
        at com.zaxxer.nuprocess.windows.WinProcessFactory.createProcess(WinProcessFactory.java:42)
        at com.zaxxer.nuprocess.NuProcessBuilder.start(NuProcessBuilder.java:266)

The problem is: 1) No idea of what error 2 means; 2) The start() function only prints the error message, instead of throwing the error.

I have included jna-4.2.1.jar and jna-platform-4.2.1.jar into the classpath, and also try to extract the dll of JNA into the library path, but the error is still there.

Child process inherits all open file descriptors on linux

Because CLOEXEC isn't set on all the file descriptors, the child process inherits them all. Unfortunately it looks like there is no easy way to avoid this when using posix_spawnp. I haven't been able to find what the correct solution would be.

Here is an interesting thread about this issue. It sounds a bit messy, but one proposed solution seems to be to dup all file descriptors (even those that aren't open) to stdout and close them (except for stdin/stdout/stderr).

http://comp.unix.programmer.narkive.com/ZlyvRUIY/handling-the-posix-spawn-file-descriptor-hell#post57

Large overhead compared to ProcessBuilder

Hi,

NuProcess has been designed to reduce process startup overhead.
However, using NuProcess I observe significant increase of startup overhead (about 3x times).

I have used Zt-Exec before which is based on ProcessBuilder

String output =
        new ProcessExecutor()
                .command(params)
                .readOutput(true)
                .timeout(1, TimeUnit.MINUTES)
                .exitValue(0)
                .directory(new File(inputFilePath))
                .execute()
                .outputUTF8();

Now I switched to NuProcess and observe increased overhead:

NuProcessBuilder pb = new NuProcessBuilder(params);
pb.setCwd(Paths.get(inputFilePath));

ProcessHandler handler = new ProcessHandler();
pb.setProcessListener(handler);

NuProcess process = pb.start();
process.waitFor(1, TimeUnit.MINUTES); 

I tested on Windows 10 and Linux Ubuntu

Observations:

  • large overhead occurs during the first startup of an external tool
  • zt-exec is faster than NuProcess for about 0.3 sec for consequtive startups

Any suggestions?

maven-bundle-plugin should not be a dependency

In your pom.xml's dependencies section, you have:

<dependency>
    <groupId>org.apache.felix</groupId>
    <artifactId>maven-bundle-plugin</artifactId>
    <version>2.3.7</version>
    <type>maven-plugin</type>
</dependency>

I've removed this locally and built the project without error. Having this dependency causes other projects, especially OSGi bundles, that refer to NuProcess to bundle all of the maven-bundle-plugin jars into their META-INF/lib. That doesn't seem like it could possibly be intentional or desirable.

(Question) Detecting process creation failure

The various NuProcessFactory implementations all seem to follow a similar approach:

XyzProcess process = new XyzProcess(processListener);
process.start(...);
return process;

process.start(...), for all types, returns a NuProcess (specifically, all the implementations return this;, but it can also return null; if the process isn't started), but that value is never considered. So the factory implementations never return null.

Since the factory is returning the instantiated object, though, how is one to detect that the process couldn't be started? I can check isRunning(), but that doesn't help me differentiate between a fast-exiting process and a process that failed to start, since all of the start() implementations print stack traces rather than propagating exceptions. It looks like I could differentiate between failed start and fast exit by checking whether onStart was ever invoked on my NuProcessHandler. Is that how you'd recommend checking? isRunning() set to false and onStart never invoked?

Is there any possibility that NuProcess could be changed to provide a better mechanism for detecting processes that can't be started? The most obvious approach would be to have NuProcessBuilder.start() throw an exception, but that seems to be against the design ethos for the project.

WindowsProcess printStackTrace

Hey, the WindowsProcess uses printStackTrace (lines 173,220,249,303,332,392) and not using logger, can you please use a logger so i can turn in off when they aren't needed?
My log shows those errors on tests and without a way to turn them off they fill my log

Thank you

stdio FD >= 3

One current shortcoming of the Java process API is that it doesn't support communication on FDs >= 3. This is easy to do on *nix OSes, but requires a bit of trickery using the lpReserved2 field in STARTUPINFO to work in Windows:

http://www.catch22.net/tuts/undocumented-createprocess

This can only ever work for Windows CRT-based programs, but I think it is "better than nothing".

Exit status 2147483647 for fast-exiting processes on linux

Hi,
I think there is a race condition somewhere on linux (doesn't happen on mac os) - when a process exits immediately (successfully with exit code 0) the callback gets called with an exit code 2147483647.

I simply started a "echo test" with NuProcess which gave me the error exit code about 5-10% of the time, while "echo 0.1 ; echo test" works perfectly.
if it helps, here is my small test program: https://gist.github.com/hpoul/83ff1a63c13cc49a4839

i'm not sure if this is ever a problem in real applications as probably most launched programs would actually do work and not just exit, but i'm not sure how "fast" a program must exit before this error occurs?

We basically stumbled across this, because we wanted to create unit tests to make sure that we handle exit codes correctly and stumbled across occasional test failures because we got exit code 2147483647 while expecting others.. we've now added sleep 0.1 everywhere for now, but that's probably not the best solution :)

thanks,
Herbert

Non-blocking way to delay onStdinReady() until data is available

I'm testing this library for use in a workflow that involves piping multiple processes together (something I wish this library supported internally, where it could probably be done more efficiently - without having to copy ByteBuffers - though presumably this is zero-copy with direct ones - in order to avoid holding references to NuProcess's output buffers), including complex output handling, and occasionally piping one process to more than one output process, or observing the output from within the Java process.

One issue with creating workflows like this is this: Say I'm piping process A's stdout to process B's stdin.

I launch B, then A. A caches buffers into a ConcurrentLinkedDeque, which, B will read from when onStdInReady() is called.

However, onStdinReady() is called before any data is available, and even if it returns true, which should result in it's being called again, if no data is written to the passed buffer, it is not called again. The only solution I've found is to block in onStdinReady() until the first buffer is available. Which kind of defeats the purpose of using a non-blocking library for this stuff. But more seriously - and I haven't dug into the code deeply enough to determine this - if the thread pool used is the one created in the static block at the top of LinuxProcess, then there are only numProcessors threads available - once there are as many of these blocked as there are cores, there will be no threads to collect output.

See the example code below (it uses wav2png and sox to read an audio file into 16-bit integer wav format and generate a thumbnail from it). If you remove the call to waitForFirstWrite.await(), no output will ever be written to wav2png.

    public static void main(String[] args) throws FileNotFoundException, InterruptedException {
        String file = "/tmp/test-32bitFloat.wav";
        NuProcessBuilder soxBuilder = new NuProcessBuilder( "/usr/bin/sox", file, "-t", "wav", "-b", "16", "-" );
        NuProcessBuilder wav2pngBuilder = new NuProcessBuilder( "/usr/bin/wav2png", "-", "-o", "/tmp/test.png" );
        File w2err = new File( "/tmp/test-wav2png.err" );
        File soxerr = new File( "/tmp/test-sox.err" );
        FileChannel w2errCh = new FileOutputStream( w2err ).getChannel();
        FileChannel soxErrCh = new FileOutputStream( soxerr ).getChannel();

        ConcurrentLinkedDeque<ByteBuffer> writes = new ConcurrentLinkedDeque<>();
        AtomicBoolean done = new AtomicBoolean();
        CountDownLatch allDoneLatch = new CountDownLatch( 2 );

        CountDownLatch waitForFirstWrite = new CountDownLatch( 1 );

        wav2pngBuilder.setProcessListener( new NuProcessHandler() {

            @Override
            public void onPreStart(NuProcess nuProcess) {
            }

            @Override
            public void onStart(NuProcess nuProcess) {
                nuProcess.wantWrite();
            }

            @Override
            public void onExit(int exitCode) {
                try {
                    System.out.println( "wav2png exited. " + exitCode );
                } finally {
                    latch.countDown();
                }
            }

            @Override
            public void onStdout(ByteBuffer buffer, boolean closed) {

            }

            @Override
            public void onStderr(ByteBuffer buffer, boolean closed) {
                try {
                    w2errCh.write( buffer );
                    if ( closed ) {
                        w2errCh.close();
                    }
                } catch ( IOException ex ) {
                    ex.printStackTrace();
                }
            }

            @Override
            public boolean onStdinReady(ByteBuffer buffer) {
                try {
                    waitForFirstWrite.await();
                } catch ( InterruptedException ex ) {
                    ex.printStackTrace();
                }
                ByteBuffer out;
                int cap = buffer.capacity() - buffer.position();
                int bytesWritten = 0;
                while ( ( out = writes.pollFirst() ) != null ) {
                    out.flip();
                    int rem = out.remaining();
                    if ( rem <= cap ) {
                        bytesWritten += rem;
                        cap -= rem;
                        buffer.put( out );
                        if ( cap == 0 ) {
                            break;
                        }
                    } else {
                        if ( buffer.remaining() == 0 ) {
                            out.flip();
                            writes.addFirst( out );
                            break;
                        } else {
                            byte[] bytes = new byte[buffer.remaining()];
                            bytesWritten += bytes.length;
                            out.get( bytes );
                            buffer.put( bytes );
                            out.compact();
                            writes.addFirst( out );
                            break;
                        }
                    }
                }
                if ( done.get() && writes.isEmpty() ) {
                    return false;
                }
                if ( bytesWritten > 0 ) {
                    buffer.flip();
                }
                return true;
            }
        } );
        soxBuilder.setProcessListener( new NuProcessHandler() {
            private NuProcess proc;

            @Override
            public void onPreStart(NuProcess nuProcess) {
                this.proc = nuProcess;
            }

            @Override
            public void onStart(NuProcess nuProcess) {
            }

            @Override
            public void onExit(int exitCode) {
                latch.countDown();
            }

            @Override
            public void onStdout(ByteBuffer buffer, boolean closed) {
                done.set( closed );
                ByteBuffer copy = ByteBuffer.allocateDirect( buffer.remaining() );
                copy.put( buffer );
                writes.addLast( copy );
                waitForFirstWrite.countDown();
            }

            @Override
            public void onStderr(ByteBuffer buffer, boolean closed) {
                try {
                    soxErrCh.write( buffer );
                    if ( closed ) {
                        soxErrCh.close();
                    }
                } catch ( IOException ioe ) {
                    ioe.printStackTrace();
                }
            }

            @Override
            public boolean onStdinReady(ByteBuffer buffer) {
                return false;
            }
        } );
        NuProcess wav2png = wav2pngBuilder.start();
        NuProcess p = soxBuilder.start();

        allDoneLatch.await();
    }

Question: JNA vs JNR

I'm curious if you've considered converting from JNA to JNR (specifically jnr-ffi).

In evaluating NuProcess, we've been comparing it with (defunct?, but functional) jnr-process. jnr-process is not a very good option for us, given the project appears to be dead now, but in benchmarking the two solutions we've found that JNR appears to be significantly more memory-efficient than JNA. I've opened #80 with some optimizations that cut down on JNA-induced allocation churn, but switching to JNR might offer an even bigger improvement.

I'm not proposing anything, to be clear, or asking for any changes. I'm just interested in whether you've ever considered JNR.

Underlying processes complete but waitFor times out (OSX)

I have an app that runs lots and lots of processes via a thread pool. Fairly consistently after running for ~30 minutes my calls to waitFor start hanging, stuck on the CountDownLatch. There are no nuprocess threads running and the underlying processes are all done.

I'm wondering if exitPending.countDown(); in BasePosixProcess.onExit should be moved into a finally block to ensure it is called if onExit is called.

"pool-1-thread-7" #18 prio=5 os_prio=31 tid=0x00007fca9307a000 nid=0x6303 waiting on condition [0x0000000126611000]
   java.lang.Thread.State: TIMED_WAITING (parking)
    at sun.misc.Unsafe.park(Native Method)
    - parking to wait for  <0x000000076ab28ed8> (a java.util.concurrent.CountDownLatch$Sync)
    at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedNanos(AbstractQueuedSynchronizer.java:1037)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.tryAcquireSharedNanos(AbstractQueuedSynchronizer.java:1328)
    at java.util.concurrent.CountDownLatch.await(CountDownLatch.java:277)
    at com.zaxxer.nuprocess.internal.BasePosixProcess.waitFor(BasePosixProcess.java:151)
    at org.dalquist.photos.survey.PhotoHashingRunner$MetaDataExtractor.call(PhotoHashingRunner.java:277)
    at org.dalquist.photos.survey.PhotoHashingRunner$MetaDataExtractor.call(PhotoHashingRunner.java:1)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

   Locked ownable synchronizers:
    - <0x00000006c7d88990> (a java.util.concurrent.ThreadPoolExecutor$Worker)

NuProcess start 1.0.3 fails on Windows

I'm unable to get 1.0.3 to work on Windows 7 (64-bit). It seems like NuProcess fails to create a process on Windows. The same test code works on Windows 7 with NuProcess 0.9.7.

Here's the stacktrace:
io.spikex.core.util.unit.ProcessTest > testCommandOutput STANDARD_ERROR
java.lang.RuntimeException: CreateProcessW() failed, error: 2
at com.zaxxer.nuprocess.windows.WindowsProcess.start(WindowsProcess.java:251)
at com.zaxxer.nuprocess.windows.WinProcessFactory.createProcess(WinProcessFactory.java:42)
at com.zaxxer.nuprocess.NuProcessBuilder.start(NuProcessBuilder.java:240)
at io.spikex.core.util.process.ProcessExecutor.start(ProcessExecutor.java:115)

Unit test fails on Mac OS X Yosemite (10.10.5)

It looks like current version can not pass noExecutableFound() test on Mac OS:

/Library/Java/JavaVirtualMachines/jdk1.7.0_79.jdk/Contents/Home/bin/java "-Dmaven.home=/Applications/IntelliJ IDEA 14.app/Contents/plugins/maven/lib/maven3" "-Dclassworlds.conf=/Applications/IntelliJ IDEA 14.app/Contents/plugins/maven/lib/maven3/bin/m2.conf" -Didea.launcher.port=7534 "-Didea.launcher.bin.path=/Applications/IntelliJ IDEA 14.app/Contents/bin" -Dfile.encoding=UTF-8 -classpath "/Applications/IntelliJ IDEA 14.app/Contents/plugins/maven/lib/maven3/boot/plexus-classworlds-2.4.jar:/Applications/IntelliJ IDEA 14.app/Contents/lib/idea_rt.jar" com.intellij.rt.execution.application.AppMain org.codehaus.classworlds.Launcher -Didea.version=14.1.4 test
[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building NuProcess 1.0.4-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-enforcer-plugin:1.0:enforce (enforce-maven) @ nuprocess ---
[INFO] 
[INFO] --- maven-resources-plugin:2.7:resources (default-resources) @ nuprocess ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /Users/user/project/Java/NuProcess/upstream/NuProcess/src/main/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ nuprocess ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 33 source files to /Users/user/project/Java/NuProcess/upstream/NuProcess/target/classes
[WARNING] /Users/user/project/Java/NuProcess/upstream/NuProcess/src/main/java/com/zaxxer/nuprocess/internal/UnsafeHelper.java:[23,16] sun.misc.Unsafe is internal proprietary API and may be removed in a future release
[WARNING] /Users/user/project/Java/NuProcess/upstream/NuProcess/src/main/java/com/zaxxer/nuprocess/internal/UnsafeHelper.java:[28,25] sun.misc.Unsafe is internal proprietary API and may be removed in a future release
[WARNING] /Users/user/project/Java/NuProcess/upstream/NuProcess/src/main/java/com/zaxxer/nuprocess/internal/UnsafeHelper.java:[34,20] sun.misc.Unsafe is internal proprietary API and may be removed in a future release
[WARNING] /Users/user/project/Java/NuProcess/upstream/NuProcess/src/main/java/com/zaxxer/nuprocess/internal/UnsafeHelper.java:[36,20] sun.misc.Unsafe is internal proprietary API and may be removed in a future release
[WARNING] /Users/user/project/Java/NuProcess/upstream/NuProcess/src/main/java/com/zaxxer/nuprocess/internal/UnsafeHelper.java:[56,18] sun.misc.Unsafe is internal proprietary API and may be removed in a future release
[INFO] 
[INFO] --- maven-resources-plugin:2.7:testResources (default-testResources) @ nuprocess ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /Users/user/project/Java/NuProcess/upstream/NuProcess/src/test/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ nuprocess ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 7 source files to /Users/user/project/Java/NuProcess/upstream/NuProcess/target/test-classes
[INFO] 
[INFO] --- maven-surefire-plugin:2.18.1:test (default-test) @ nuprocess ---
[INFO] Surefire report directory: /Users/user/project/Java/NuProcess/upstream/NuProcess/target/surefire-reports

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.zaxxer.nuprocess.CatTest
Starting test lotOfProcesses()
 Iteration 1
 Iteration 2
 Iteration 3
 Iteration 4
 Iteration 5
 Iteration 6
 Iteration 7
 Iteration 8
 Iteration 9
 Iteration 10
 Iteration 11
 Iteration 12
 Iteration 13
 Iteration 14
 Iteration 15
 Iteration 16
 Iteration 17
 Iteration 18
 Iteration 19
 Iteration 20
Completed test lotOfProcesses()
Starting test decodingShortUtf8Data()
Completed test decodingShortUtf8Data()
Starting test badExit()
Completed test badExit()
Starting test lotOfData()
Completed test lotOfData()
Starting test decodingLongUtf8Data()
Completed test decodingLongUtf8Data()
Starting test noExecutableFound()
java.lang.RuntimeException: Invocation of posix_spawn() failed, return code: 2, last error: 2
    at com.zaxxer.nuprocess.internal.BasePosixProcess.checkReturnCode(BasePosixProcess.java:867)
    at com.zaxxer.nuprocess.internal.BasePosixProcess.start(BasePosixProcess.java:359)
    at com.zaxxer.nuprocess.osx.OsxProcessFactory.createProcess(OsxProcessFactory.java:34)
    at com.zaxxer.nuprocess.NuProcessBuilder.start(NuProcessBuilder.java:266)
    at com.zaxxer.nuprocess.CatTest.noExecutableFound(CatTest.java:197)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.rules.ExternalResource$1.evaluate(ExternalResource.java:48)
    at org.junit.rules.RunRules.evaluate(RunRules.java:20)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:283)
    at org.apache.maven.surefire.junit4.JUnit4Provider.executeWithRerun(JUnit4Provider.java:173)
    at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:153)
    at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:128)
    at org.apache.maven.surefire.booter.ForkedBooter.invokeProviderInSameClassLoader(ForkedBooter.java:203)
    at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:155)
    at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:103)
Completed test noExecutableFound()
Starting test changeCwd() (java cwd=/Users/user/project/Java/NuProcess/upstream/NuProcess, tmp=/var/folders/2m/qc6sw15x5bx0d71kqs9vm2xh0000gp/T/junit6815590441566501432)
Completed test changeCwd()
Tests run: 8, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 3.748 sec - in com.zaxxer.nuprocess.CatTest
Running com.zaxxer.nuprocess.FastExitingProcessTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0 sec - in com.zaxxer.nuprocess.FastExitingProcessTest
Running com.zaxxer.nuprocess.InterruptTest

No notifications when issuing kill -9 to the child process

Hello,

Is it possible to know in the parent process if the state of the child process changes?

I'm trying to spawn "sh -c 'sleep 60'", then kill this child process with kill -9 from the shell and this child process starts to be in the defunct state until parent process exits. At the same time no notifications received within NuProcessHandler#onExit.

Kind Regards,
Sergey

Cannot capture some console output in Windows

Hello,
I'm experiencing a problem of capturing output via NuProcess, it works well for cmd.exe, but when launching some native CLI consoles such as mysql.exe or psql.exe, the program keeps hanging and cannot capture any output. Below test code for your reference:

import com.zaxxer.nuprocess.NuAbstractProcessHandler;
import com.zaxxer.nuprocess.NuProcess;
import com.zaxxer.nuprocess.NuProcessBuilder;

import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.concurrent.Semaphore;

/**
 * Created by Will on 2017/1/15.
 */
public class MySQLTest {
    public static void main(String... args) {
        new MySQLTest().execute();
    }

    private void execute() {
        NuProcessBuilder pb = new NuProcessBuilder(Arrays.asList("D:\\mysql\\bin\\mysql.exe", "--host=127.0.0.1", "--user=root", "--port=3311"));
        ProcessHandler processHandler = new ProcessHandler();
        pb.setProcessListener(processHandler);
        NuProcess np = pb.start();

        processHandler.awaitDisconnection();
    }

    class ProcessHandler extends NuAbstractProcessHandler {
        private NuProcess nuProcess;
        private LinkedList<String> cmdList = new LinkedList<String>();
        private Semaphore disconnected = new Semaphore(0);

        @Override
        public void onStart(NuProcess nuProcess) {
            this.nuProcess = nuProcess;
        }

        public void awaitDisconnection() {
            disconnected.acquireUninterruptibly(1);
        }

        public void clear() {
            cmdList.clear();
        }

        public void close() {
            nuProcess.destroy(false);
        }

        //Send key event
        public void sendKey(String ch) {
            clear();
            nuProcess.writeStdin(ByteBuffer.wrap(ch.getBytes()));
        }

        //Send command
        public void write(String stack) {
            cmdList.add(stack + "\n");
            //nuProcess.hasPendingWrites();
            nuProcess.wantWrite();
        }

        public synchronized Boolean isPending() {
            return nuProcess.hasPendingWrites();
        }

        @Override
        public boolean onStdinReady(ByteBuffer buffer) {
            if (!cmdList.isEmpty()) {
                String cmd = cmdList.poll();
                buffer.put(cmd.getBytes());
                buffer.flip();
            }
            return !cmdList.isEmpty();
        }

        @Override
        public void onStdout(ByteBuffer buffer, boolean closed) {
            int remaining = buffer.remaining();
            byte[] bytes = new byte[remaining];
            buffer.get(bytes);
            System.out.println("Receiving output ...");
            System.out.println(new String(bytes));
            System.out.flush();
            if (closed) {
                disconnected.release();
            }

            // nuProcess.wantWrite();
            // We're done, so closing STDIN will cause the "cat" process to exit
            //nuProcess.closeStdin(true);
        }

        @Override
        public void onStderr(ByteBuffer buffer, boolean closed) {
            this.onStdout(buffer, false);
        }
    }
}

1.0.5 release

Hi,

Is release of 1.0.5 version planned?
It would be great to have released version that includes fixes from #49

Best Regards,
Michał

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.