Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

Java library that helps with running external processes.

License

NotificationsYou must be signed in to change notification settings

fleipold/jproc

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Build StatusMaven Central

Intro

Running external commands in Java is an error prone task.JProc helps managing input and output of non-interactive,external processes as well as error conditions. It uses sensibledefaults, such as throwing an exception if a process terminateswith a non zero exit status.

Getting Started

To get started either download thejar orif you are using maven add this snippet to your pom:

<dependency>    <groupId>org.buildobjects</groupId>    <artifactId>jproc</artifactId>    <version>2.8.2</version></dependency>

For the basic use case of just capturing program output there is a static method:

Stringoutput =ProcBuilder.run("echo","Hello World!");assertEquals("Hello World!\n",output);

There is another static method that filters a given string througha program:

Stringoutput =ProcBuilder.filter("x y z","sed","s/y/a/");assertEquals("x a z",output.trim());

Output and Input

For more control over the execution we'll use aProcBuilder instance to configurethe process.

The run method builds and spawns the actual process and blocks until the process exits.The process takes care of writing the output to a stream, as opposed to the standardfacilities in the JDK that expect the client to actively consume theoutput from an input stream:

ByteArrayOutputStreamoutput =newByteArrayOutputStream();newProcBuilder("echo")    .withArg("Hello World!")    .withOutputStream(output)    .run();assertEquals("Hello World!\n",output.toString());

The input can be read from an arbitrary input stream, like this:

ByteArrayInputStreaminput =newByteArrayInputStream("Hello cruel World".getBytes());ProcResultresult =newProcBuilder("wc")    .withArgs("-w")    .withInputStream(input).run();assertEquals("3",result.getOutputString().trim());

If all you want to get is the string that gets returned and if thereis not a lot of data, using a streams is quite cumbersome. So for convenienceif no stream is provdied the output is captured by default and can beobtained from the result.

ProcResultresult =newProcBuilder("echo")    .withArg("Hello World!")    .run();assertEquals("Hello World!\n",result.getOutputString());assertEquals(0,result.getExitValue());assertEquals("echo 'Hello World!'",result.getProcString());

For providing input there is a convenience method too:

ProcResultresult =newProcBuilder("cat")    .withInput("This is a string").run();assertEquals("This is a string",result.getOutputString());

The Environment

Some external programs are using environment variables. These can alsobe set using thewithVar method:

ProcResultresult =newProcBuilder("bash")    .withArgs("-c","echo $MYVAR")    .withVar("MYVAR","my value").run();assertEquals("my value\n",result.getOutputString());assertEquals("bash -c 'echo $MYVAR'",result.getCommandLine());

If you want to set multiple environment variables, you can pass a Map:

Map<String,String>envVariables =newHashMap<>();envVariables.put("var1","val 1");envVariables.put("var2","val 2");ProcResultresult =newProcBuilder("bash")        .withArgs("-c","env")        .withVars(envVariables).run();assertTrue(result.getOutputString().contains("var1=val 1\n"));assertTrue(result.getOutputString().contains("var2=val 2\n"));assertEquals("bash -c env",result.getCommandLine());

The environment can be cleared of values inherited from the parent process:

Map<String,String>envVariables =newHashMap<>();envVariables.put("var1","val 1");envVariables.put("var2","val 2");ProcResultresult =newProcBuilder("bash")        .withArgs("-c","env")        .clearEnvironment()        .withVars(envVariables).run();String[]outputLines =result.getOutputString().split("\n");assertEquals("var1=val 1",outputLines[0]);assertEquals("var2=val 2",outputLines[1]);// Note: environment is not going to be completely empty, as there are some variables that every process needs//       thus we only assert on the first two lines.assertEquals("bash -c env",result.getCommandLine());

By default the new program is spawned in the working directory ofthe parent process. This can be overidden:

ProcResultresult =newProcBuilder("pwd")    .withWorkingDirectory(newFile("/"))    .run();assertEquals("/\n",result.getOutputString());

Timeouts

A common usecase for external programs is batch processing of data.These programs might always run into difficulties. Therefore a timeout can bespecified. There is a default timeout of 5000ms. If the program does not terminate within the timeoutinterval it will be terminated and the failure is indicated throughan exception:

ProcBuilderbuilder =newProcBuilder("sleep")    .withArg("2")    .withTimeoutMillis(1000);try {builder.run();fail("Should time out");}catch (TimeoutExceptionex) {assertEquals("Process 'sleep 2' timed out after 1000ms.",ex.getMessage());}

Even if the process does not timeout, we might be interested in theexecution time. It is also available through the result:

ProcResultresult =newProcBuilder("sleep")    .withArg("0.5")    .run();assertTrue(result.getExecutionTime() >500 &&result.getExecutionTime() <1000);

In some cases you might want to disable the timeout.

To make this explicit rather than setting the timeout toa very large number there is a method to disable thetimeout.

Note: Not having a timeout doesn't necessarily make your systemmore stable. Especially if the process hangs (e.g. waiting forinput on stdin).

ProcBuilderbuilder =newProcBuilder("sleep")    .withArg("7")    .withNoTimeout();ProcResultresult =builder.run();assertEquals(result.getExecutionTime(),7000,500);

Exit Status

It is a time honoured tradition that programs signal a failureby returning a non-zero exit value. However in java failure issignalled through exceptions. Non-Zero exit values thereforeget translated into an exception, that also grants access tothe output on standard error.

ProcBuilderbuilder =newProcBuilder("ls")    .withArg("xyz");try {builder.run();fail("Should throw exception");}catch (ExternalProcessFailureExceptionex) {assertEquals("No such file or directory",ex.getStderr().split("\\:")[2].trim());assertTrue(ex.getExitValue() >0);assertEquals("ls xyz",ex.getCommandLine());assertTrue(ex.getTime() >0);}

In some cases a non-zero exit code doesn't indicate an error, but it isused to return a result, e.g. withgrep.

In that case throwing an exception would be inappropriate. To prevent anexception from being thrown we can configure the builder to ignore the exitstatus:

try {ProcResultresult =newProcBuilder("bash")        .withArgs("-c","echo Hello World!;exit 100")        .ignoreExitStatus()        .run();assertEquals("Hello World!\n",result.getOutputString());assertEquals(100,result.getExitValue());}catch (ExternalProcessFailureExceptionex) {fail("A process started with ignoreExitStatus should not throw an exception");}

It is also possible to specify a set of expected status codes that will not leadto an exception:

try {ProcResultresult =newProcBuilder("bash")        .withArgs("-c","echo Hello World!;exit 100")        .withExpectedExitStatuses(0,100)        .run();assertEquals("Hello World!\n",result.getOutputString());assertEquals(100,result.getExitValue());}catch (ExternalProcessFailureExceptionex) {fail("An expected exit status should not lead to an exception");}

Status codes that are not expected will so still lead to an exception:

try {ProcResultresult =newProcBuilder("bash")        .withArgs("-c","echo Hello World!;exit 99")        .withExpectedExitStatuses(0,100)        .run();fail("An exit status that is not part of the expectedExitStatuses should throw");}catch (ExternalProcessFailureExceptionex) {assertEquals(99,ex.getExitValue());}

Good to Know

Input and output can also be provided asbyte[].ProcBuilder also copes with large amounts ofdata.

intMEGA =1024 *1024;byte[]data =newbyte[4 *MEGA];for (inti =0;i <data.length;i++) {data[i] = (byte)Math.round(Math.random() *255 -128);}ProcResultresult =newProcBuilder("gzip")    .withInput(data)    .run();assertTrue(result.getOutputBytes().length >2 *MEGA);

The builder allows to build and spawn several processes fromthe same builder instance:

ProcBuilderbuilder =newProcBuilder("date");Stringdate1 =builder.run().getOutputString();Thread.sleep(2000);Stringdate2 =builder.run().getOutputString();assertNotNull(date1);assertNotNull(date2);assertTrue(!date1.equals(date2));

Pipes

Here is how you can consume stdout in a streaming fashion (for example line by line):

newProcBuilder("echo")    .withArgs("line1\nline2")    .withOutputConsumer(newStreamConsumer() {publicvoidconsume(InputStreamstream)throwsIOException {BufferedReaderreader =newBufferedReader(newInputStreamReader(stream));assertEquals("line1",reader.readLine());assertEquals("line2",reader.readLine());assertNull(reader.readLine());        }    })    .withTimeoutMillis(2000)    .run();

Of course, you can consume stderr in the same way:

newProcBuilder("bash")    .withArgs("-c",">&2 echo error;>&2 echo error2;echo stdout")    .withOutputConsumer(newStreamConsumer() {@Overridepublicvoidconsume(InputStreamstream)throwsIOException {BufferedReaderreader =newBufferedReader(newInputStreamReader(stream));assertEquals("stdout",reader.readLine());assertNull(reader.readLine());        }    })    .withErrorConsumer(newStreamConsumer() {@Overridepublicvoidconsume(InputStreamstream)throwsIOException {BufferedReaderreader =newBufferedReader(newInputStreamReader(stream));assertEquals("error",reader.readLine());assertEquals("error2",reader.readLine());assertNull(reader.readLine());        }    })    .withTimeoutMillis(2000)    .run();

Error output can also be accessed directly:

ProcResultresult =newProcBuilder("bash")    .withArgs("-c",">&2 echo error;>&2 echo error2; echo out;echo out2")    .run();assertEquals("out\nout2\n",result.getOutputString());assertEquals("error\nerror2\n",result.getErrorString());

Alteratively an output stream can be passed in:

ByteArrayOutputStreamout =newByteArrayOutputStream();ByteArrayOutputStreamerr =newByteArrayOutputStream();newProcBuilder("bash")    .withArgs("-c",">&2 echo error;>&2 echo error2; echo out;echo out2")    .withOutputStream(out)    .withErrorStream(err)    .run();assertEquals("out\nout2\n",out.toString());assertEquals("error\nerror2\n",err.toString());

String Representations

The builder can also return a string representation ofthe invocation. Naturally this method doesn't support chaining,that means you'll have to store the builder in a variableto finally run the process.

finalProcBuilderechoBuilder =newProcBuilder("echo")    .withArgs("Hello World!");assertEquals("echo 'Hello World!'",echoBuilder.getProcString());ProcResultresult =echoBuilder.run();assertEquals("Hello World!\n",result.getOutputString());

[8]ページ先頭

©2009-2025 Movatter.jp