├── .github └── workflows │ └── maven.yml ├── .gitignore ├── .mvn └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── .travis.yml ├── Changelog.txt ├── LICENSE ├── NOTICE.txt ├── README.md ├── get_release_commands.sh ├── mvnw ├── mvnw.cmd ├── pom.xml └── src ├── main └── java │ └── org │ └── zeroturnaround │ └── exec │ ├── InvalidExitUtil.java │ ├── InvalidExitValueException.java │ ├── InvalidOutputException.java │ ├── InvalidResultException.java │ ├── MDCCallableAdapter.java │ ├── MDCRunnableAdapter.java │ ├── MessageLogger.java │ ├── MessageLoggers.java │ ├── ProcessAttributes.java │ ├── ProcessExecutor.java │ ├── ProcessInitException.java │ ├── ProcessOutput.java │ ├── ProcessResult.java │ ├── StartedProcess.java │ ├── WaitForProcess.java │ ├── close │ ├── ExceptionUtil.java │ ├── ProcessCloser.java │ ├── StandardProcessCloser.java │ └── TimeoutProcessCloser.java │ ├── listener │ ├── CompositeProcessListener.java │ ├── DestroyerListenerAdapter.java │ ├── ProcessDestroyer.java │ ├── ProcessListener.java │ └── ShutdownHookProcessDestroyer.java │ ├── stop │ ├── DestroyProcessStopper.java │ ├── NopProcessStopper.java │ └── ProcessStopper.java │ └── stream │ ├── CallerLoggerUtil.java │ ├── ExecuteStreamHandler.java │ ├── InputStreamPumper.java │ ├── LineConsumer.java │ ├── LogOutputStream.java │ ├── NullOutputStream.java │ ├── PumpStreamHandler.java │ ├── StreamPumper.java │ ├── TeeOutputStream.java │ └── slf4j │ ├── Level.java │ ├── Slf4jDebugOutputStream.java │ ├── Slf4jErrorOutputStream.java │ ├── Slf4jInfoOutputStream.java │ ├── Slf4jOutputStream.java │ ├── Slf4jStream.java │ ├── Slf4jTraceOutputStream.java │ └── Slf4jWarnOutputStream.java └── test ├── java └── org │ └── zeroturnaround │ └── exec │ ├── ProcessOutputTest.java │ ├── ReadmeExamples.java │ ├── stream │ └── TeeOutputStreamTest.java │ └── test │ ├── ArgumentsAsList.java │ ├── BigOutput.java │ ├── CallerLoggerUtilTest.java │ ├── EmptyArgTest.java │ ├── ExitLikeABoss.java │ ├── HelloWorld.java │ ├── InputRedirectTest.java │ ├── InputStreamPumperTest.java │ ├── LogOutputStreamTest.java │ ├── Loop.java │ ├── MockProcessDestroyer.java │ ├── PrintArguments.java │ ├── PrintInputToOutput.java │ ├── ProcessExecutorArgsWithExtraSpaceTest.java │ ├── ProcessExecutorBigOutputTest.java │ ├── ProcessExecutorCommandLineTest.java │ ├── ProcessExecutorExitValueTest.java │ ├── ProcessExecutorHelloWorldTest.java │ ├── ProcessExecutorInputStreamTest.java │ ├── ProcessExecutorLoggerTest.java │ ├── ProcessExecutorMainTest.java │ ├── ProcessExecutorStreamCloseTest.java │ ├── ProcessExecutorTimeoutTest.java │ ├── ProcessInitExceptionTest.java │ ├── ProcessListenerSuccessTest.java │ ├── ProcessListenerThrowTest.java │ ├── RememberCloseOutputStream.java │ ├── SetFailExecuteStreamHandler.java │ └── shutdown │ ├── ProcessExecutorShutdownHookTest.java │ ├── WriterLoop.java │ ├── WriterLoopStarterAfterExit.java │ └── WriterLoopStarterBeforeExit.java └── resources └── logback.xml /.github/workflows/maven.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | fail-fast: false 10 | matrix: 11 | java: [ 6, 8, 11, 17 ] 12 | name: Build with Java ${{ matrix.java }} 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v3 16 | 17 | - name: Set up JDK 6 18 | if: ${{ matrix.java == '6'}} 19 | # We use v1 as newer versions do not seem to support Java 6 20 | # Inspired by https://github.com/junit-team/junit4/blob/main/.github/workflows/main.yml 21 | uses: actions/setup-java@v1 22 | with: 23 | java-version: ${{ matrix.java }} 24 | mvn-toolchain-id: ${{ matrix.java }} 25 | - name: Set up toolchains.xml for Java 6 26 | # We need to manually set up toolchains.xml as setup-java@v1 does not seem to support toolchains.xml 27 | if: ${{ matrix.java == '6'}} 28 | run: | 29 | echo "" >> $HOME/.m2/toolchains.xml 30 | echo "" >> $HOME/.m2/toolchains.xml 31 | echo " " >> $HOME/.m2/toolchains.xml 32 | echo " jdk" >> $HOME/.m2/toolchains.xml 33 | echo " " >> $HOME/.m2/toolchains.xml 34 | echo " 6" >> $HOME/.m2/toolchains.xml 35 | echo " " >> $HOME/.m2/toolchains.xml 36 | echo " " >> $HOME/.m2/toolchains.xml 37 | echo " ${JAVA_HOME_6_0_119_X64}" >> $HOME/.m2/toolchains.xml 38 | echo " " >> $HOME/.m2/toolchains.xml 39 | echo " " >> $HOME/.m2/toolchains.xml 40 | echo "" >> $HOME/.m2/toolchains.xml 41 | cat $HOME/.m2/toolchains.xml 42 | - name: Set up JDK 43 | if: ${{ matrix.java != '6'}} 44 | uses: actions/setup-java@v3 45 | with: 46 | java-version: ${{ matrix.java }} 47 | mvn-toolchain-id: ${{ matrix.java }} 48 | distribution: temurin 49 | cache: maven 50 | - name: Set up JDK 17 51 | # Set up JDK 17 as the last step before building so it becomes default and Maven would use it 52 | uses: actions/setup-java@v3 53 | with: 54 | java-version: 17 55 | mvn-toolchain-id: 17 56 | distribution: temurin 57 | cache: maven 58 | 59 | - name: Build on Java 6, run tests with Java 17 60 | # Java 6 is special case, we need to activate a profile on it 61 | if: ${{ matrix.java == '6'}} 62 | run: | 63 | chmod +x mvnw 64 | ./mvnw -ntp -B -P java6 -DtestJdk=17 test 65 | 66 | - name: Build on Java 8, run tests with Java 17 67 | # Java 8 is also a special case, we need to activate a profile on it 68 | if: ${{ matrix.java == '8'}} 69 | run: | 70 | chmod +x mvnw 71 | ./mvnw -ntp -B -P java8 -DtestJdk=17 test 72 | 73 | - name: Build and test on Java ${{ matrix.java }} 74 | # More modern JDKs support specifying a target JDK release for the compiler 75 | if: ${{ matrix.java != '6' && matrix.java != '8' }} 76 | run: | 77 | chmod +x mvnw 78 | ./mvnw -ntp -B -DmainJdk=${{ matrix.java }} verify package 79 | mkdir upload && cp target/*.jar upload 80 | 81 | - uses: actions/upload-artifact@v3 82 | with: 83 | name: Upload artifacts 84 | path: upload -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .classpath 2 | .hg/ 3 | .hgignore 4 | .hgtags 5 | .project 6 | .settings/ 7 | target/ 8 | test.log 9 | .swp 10 | 11 | .idea 12 | *.iml 13 | *.ipr 14 | *.iws 15 | 16 | writeLoop.data 17 | src/main/resources/rebel.xml 18 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zeroturnaround/zt-exec/6149c117ef835355c2e96bd331a3fa27b6a14394/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 18 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar 19 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - openjdk8 4 | - openjdk11 5 | -------------------------------------------------------------------------------- /Changelog.txt: -------------------------------------------------------------------------------- 1 | ******************************************** 2 | ZT Exec Changelog 3 | ******************************************** 4 | 5 | 1.12 (2nd of September 2020) 6 | * Removed rebel.xml from the release archive 7 | 8 | 1.11 (5th of July 2019) 9 | * Improved usage - eliminated cases of log and throw 10 | * Removed dependency on Apache's commons-io library 11 | * Use ping instead of sleep on Windows to test timeout exceptions 12 | * Some bug fixes 13 | 14 | 1.10 (1st August 2017) 15 | * Added support for flushing output after each write 16 | * Added support for hooks for thread executor creation 17 | * Added support of preserving MDC context of the caller thread 18 | * Changed LogOutpuStream to handle line breaks like ProcessOutput 19 | 20 | 1.9 (12th April 2016) 21 | * Fixed using empty arguments on Windows 22 | * Fixed closing stdin on Java 8 23 | * Fixed redirecting PipedInputStream. 24 | * Added ProcessInitException to expose error code 25 | * Added ProcessExecutor.checkExitValue() for unit testing 26 | * Added getters to ProcessExecutor 27 | 28 | 1.8 (17th March 2015) 29 | * Fixed blocking JVM shutdown 30 | * Improved TimeoutException message 31 | * Improved Slf4jStream 32 | * Added ProcessExecutor.closeTimeout() 33 | * Added ProcessOutput.getLines() 34 | * Added ProcessOutput.command(Iterable) 35 | * Added ProcessListener.afterFinish() and InvalidOutputException support 36 | * Added Level class 37 | 38 | 1.7 (30th June 2014) 39 | * Fixed ProcessExecutor.commandSplit() 40 | * Added ProcessExecutor.stopper(ProcessStopper) to customize stopping in case of timeout or cancellation 41 | * Added ProcessExecutor.setMessageLogger() to customize log message level 42 | * Added ProcessExecutor.executeNoTimeout() to avoid catching TimeoutException 43 | * Added ProcessExecutor.environment(String, String) for convenience 44 | * Improved logging and error handling 45 | * Added get-prefixes to StartedProcess methods (old ones are deprecated) 46 | 47 | 1.6 (30th January 2014) 48 | * Improved Slf4j logging support 49 | * Added ProcessExecutor.redirectInput() 50 | * Improved adding/removing destroyers and listeners 51 | * InvalidExitValueException now includes process output if it was read 52 | * Added get-prefixes to some methods (old ones are deprecated) 53 | 54 | 1.5 (14th October 2013) 55 | * By default any exit code is now allowed (use exitValueNormal() to only allow zero) 56 | * Fixed an issue with starting processes on JVM shutdown 57 | * Fixed an issue with supporting Java 1.5 58 | -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | ==== The following is the original NOTICE.txt file from the Apache Commons Exec project 2 | that this project is derived from. 3 | 4 | Apache Commons Exec 5 | Copyright 2005-2010 The Apache Software Foundation 6 | 7 | This product includes software developed by 8 | The Apache Software Foundation (http://www.apache.org/). 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ZT Process Executor 2 | ================ 3 | 4 | ### Continuous Integration 5 | ![Build Status](https://github.com/zeroturnaround/zt-exec/actions/workflows/maven.yml/badge.svg) 6 | 7 | ### Quick Overview 8 | 9 | The project was created to merge similar functionality of projects at [ZeroTurnaround](http://zeroturnaround.com/) into a single codebase. 10 | It's designed to be powerful but still simple to use. By using a single class **ProcessExecutor** 11 | the user gets the functionality from both **java.lang.ProcessBuilder** and [Apache Commons Exec](http://commons.apache.org/proper/commons-exec/). 12 | 13 | ### Dependencies 14 | 15 | * Minimal required Java version is 6 (tested also with 8, 11 and 17). 16 | * The only 3rd-party dependency is [SLF4J](https://www.slf4j.org/). 17 | 18 | ### Installation 19 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.zeroturnaround/zt-exec/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.zeroturnaround/zt-exec) 20 | 21 | The project artifacts are available in [Maven Central Repository](https://search.maven.org/artifact/org.zeroturnaround/zt-exec/). 22 | 23 | To include it in your maven project then you have to specify the dependency. 24 | 25 | ```xml 26 | ... 27 | 28 | org.zeroturnaround 29 | zt-exec 30 | 1.12 31 | 32 | ... 33 | ``` 34 | 35 | ## Motivation 36 | 37 | There are many approaches to take when running external processes from Java. There are the **JRE** options such as the **Runtime.exec()** and **ProcessBuilder**. Also there is the [Apache Commons Exec](http://commons.apache.org/proper/commons-exec/). Nevertheless we created yet another process library (**YAPL**). 38 | 39 | Some of the reasons for this crazy endeavour 40 | 41 | * Improved handling of streams 42 | * Reading/writing to streams 43 | * Redirecting stderr to stdout 44 | * Improved handling of timeouts 45 | * Improved checking of exit codes 46 | * Improved API 47 | * One liners for quite complex usecases 48 | * One liners to get process output into a String 49 | * Access to the **Process** object available 50 | * Support for async processes ( **Future** ) 51 | * Improved logging with [SLF4J API](http://www.slf4j.org/) 52 | * Support for multiple processes 53 | 54 | **Can zt-exec also kill processes?** 55 | 56 | No. There is [zt-process-killer](https://github.com/zeroturnaround/zt-process-killer) for that. 57 | 58 | ## Examples 59 | 60 | * Output is pumped to NullOutputStream 61 | 62 | ```java 63 | new ProcessExecutor().command("java", "-version").execute(); 64 | ``` 65 | 66 |
67 | 68 | * Returning the exit code 69 | * Output is pumped to NullOutputStream 70 | 71 | ```java 72 | int exit = new ProcessExecutor().command("java", "-version") 73 | .execute().getExitValue(); 74 | ``` 75 | 76 |
77 | 78 | * Return output as UTF8 String 79 | 80 | ```java 81 | String output = new ProcessExecutor().command("java", "-version") 82 | .readOutput(true).execute() 83 | .outputUTF8(); 84 | ``` 85 | 86 |
87 | 88 | * Pumping the output to a logger 89 | 90 | ```java 91 | new ProcessExecutor().command("java", "-version") 92 | .redirectOutput(Slf4jStream.of(LoggerFactory.getLogger(getClass().getName() + ".MyProcess")).asInfo()).execute(); 93 | ``` 94 | 95 |
96 | 97 | * Pumping the output to a logger (short form for previous) 98 | 99 | ```java 100 | new ProcessExecutor().command("java", "-version") 101 | .redirectOutput(Slf4jStream.of("MyProcess").asInfo()).execute(); 102 | ``` 103 | 104 |
105 | 106 | * Pumping the output to the logger of the caller class 107 | 108 | ```java 109 | new ProcessExecutor().command("java", "-version") 110 | .redirectOutput(Slf4jStream.ofCaller().asInfo()).execute(); 111 | ``` 112 | 113 |
114 | 115 | * Pumping the output to a logger 116 | * Returning output as UTF8 String 117 | 118 | ```java 119 | String output = new ProcessExecutor().command("java", "-version") 120 | .redirectOutput(Slf4jStream.of(getClass()).asInfo()) 121 | .readOutput(true).execute().outputUTF8(); 122 | ``` 123 | 124 |
125 | 126 | * Pumping the stderr to a logger 127 | * Returning the output as UTF8 String 128 | 129 | ```java 130 | String output = new ProcessExecutor().command("java", "-version") 131 | .redirectError(Slf4jStream.of(getClass()).asInfo()) 132 | .readOutput(true).execute() 133 | .outputUTF8(); 134 | ``` 135 | 136 |
137 | 138 | * Running with a timeout of **60** seconds 139 | * Output pumped to NullOutputStream 140 | 141 | ```java 142 | try { 143 | new ProcessExecutor().command("java", "-version") 144 | .timeout(60, TimeUnit.SECONDS).execute(); 145 | } 146 | catch (TimeoutException e) { 147 | // process is automatically destroyed 148 | } 149 | ``` 150 | 151 |
152 | 153 | * Pumping output to another OutputStream 154 | 155 | ```java 156 | OutputStream out = ...; 157 | new ProcessExecutor().command("java", "-version") 158 | .redirectOutput(out).execute(); 159 | ``` 160 | 161 |
162 | 163 | * Handling output line-by-line while process is running (Java 8+) 164 | 165 | ```java 166 | new ProcessExecutor().command("java", "-version") 167 | .redirectOutput(line -> ...) 168 | .execute(); 169 | ``` 170 | 171 |
172 | 173 | * Handling output line-by-line while process is running (prior to Java 8) 174 | 175 | ```java 176 | new ProcessExecutor().command("java", "-version") 177 | .redirectOutput(new LogOutputStream() { 178 | @Override 179 | protected void processLine(String line) { 180 | ... 181 | } 182 | }) 183 | .execute(); 184 | ``` 185 | 186 |
187 | 188 | * Destroy the running process when VM exits 189 | * Output pumped to NullOutputStream 190 | 191 | ```java 192 | new ProcessExecutor().command("java", "-version").destroyOnExit().execute(); 193 | ``` 194 | 195 |
196 | 197 | * Run process with a specific environment variable 198 | * Output pumped to NullOutputStream 199 | 200 | ```java 201 | new ProcessExecutor().command("java", "-version") 202 | .environment("foo", "bar").execute(); 203 | ``` 204 | 205 |
206 | 207 | * Run process with a specific environment 208 | * Output pumped to NullOutputStream 209 | 210 | ```java 211 | Map env = ... 212 | new ProcessExecutor().command("java", "-version") 213 | .environment(env).execute(); 214 | ``` 215 | 216 |
217 | 218 | * Throw exception when wrong exit code 219 | * Output is pumped to NullOutputStream 220 | 221 | ```java 222 | try { 223 | new ProcessExecutor().command("java", "-version") 224 | .exitValues(3).execute(); 225 | } 226 | catch (InvalidExitValueException e) { 227 | System.out.println("Process exited with " + e.getExitValue()); 228 | } 229 | ``` 230 | 231 |
232 | 233 | * Throw exception when wrong exit code 234 | * Return output as UTF8 String 235 | 236 | ```java 237 | String output; 238 | boolean success = false; 239 | try { 240 | output = new ProcessExecutor().command("java", "-version") 241 | .readOutput(true).exitValues(3) 242 | .execute().outputUTF8(); 243 | success = true; 244 | } 245 | catch (InvalidExitValueException e) { 246 | System.out.println("Process exited with " + e.getExitValue()); 247 | output = e.getResult().outputUTF8(); 248 | } 249 | ``` 250 | 251 |
252 | 253 | * Starting process in the background 254 | * Output is pumped to NullOutputStream 255 | 256 | ```java 257 | Future future = new ProcessExecutor() 258 | .command("java", "-version") 259 | .start().getFuture(); 260 | // do some stuff 261 | future.get(60, TimeUnit.SECONDS); 262 | ``` 263 | 264 |
265 | 266 | * Start process in the background 267 | * Return output as UTF8 String 268 | 269 | ```java 270 | Future future = new ProcessExecutor() 271 | .command("java", "-version") 272 | .readOutput(true) 273 | .start().getFuture(); 274 | // do some stuff 275 | String output = future.get(60, TimeUnit.SECONDS).outputUTF8(); 276 | ``` 277 | -------------------------------------------------------------------------------- /get_release_commands.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | 4 | if [[ "$1" = "-h" ]] || [[ "$1" = "--help" ]]; then 5 | echo "Usage: './get_release_commands.sh VERSION_TO_RELEASE [NEW_DEV_VERSION]'" 6 | echo "Prints out commands to run to release zt-exec to Maven Central." 7 | echo "VERSION_TO_RELEASE must be a number (integer or floating). NEW_DEV_VERSION is optional" 8 | exit 0 9 | fi 10 | 11 | if ! [[ "$1" =~ ^[0-9]*\.?[0-9]*$ ]] || [[ "$1z" = "z" ]]; then 12 | echo "Not a valid version number given, use -h to get usage options" 13 | exit 1 14 | fi 15 | 16 | version=$1 17 | 18 | version_after_dot=${version##*.} 19 | version_str_length=${#version}-${#version_after_dot} 20 | 21 | incr_last_digit=$((${version_after_dot}+1)) 22 | 23 | version_without_last_digit=${version: 0:${version_str_length}} 24 | new_version="${version_without_last_digit}${incr_last_digit}-SNAPSHOT" 25 | 26 | if [[ "$2z" = "z" ]]; then 27 | echo "No new development version given using ${new_version}" 28 | else 29 | new_version=$2 30 | echo "New development version will be ${new_version}" 31 | fi 32 | 33 | echo "To release zt-exec version ${version} run these commands:" 34 | echo "" 35 | echo "1) set release version" 36 | echo "mvn versions:set -DnewVersion=${version}" 37 | 38 | echo "" 39 | echo "2) commit & tag" 40 | echo "git add pom.xml; " 41 | echo "git commit -m \"Prepare release zt-exec-${version}\"" 42 | echo "git tag zt-exec-${version}" 43 | 44 | echo "" 45 | echo "3) build release" 46 | echo "mvn clean install" 47 | 48 | echo "" 49 | echo "4) generate javadoc archive" 50 | echo "mvn javadoc:jar" 51 | 52 | echo "" 53 | echo "5) generate sources archive" 54 | echo "mvn source:jar" 55 | 56 | echo "" 57 | echo "6) deploy and sign releases archive" 58 | echo "mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=pom.xml -Dfile=target/zt-exec-$1.jar" 59 | 60 | echo "" 61 | echo "7) deploy and sign sources archive" 62 | echo "mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=pom.xml -Dfile=target/zt-exec-$1-sources.jar -Dclassifier=sources" 63 | 64 | echo "" 65 | echo "8) deploy and sign javadoc archive" 66 | echo "mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=pom.xml -Dfile=target/zt-exec-$1-javadoc.jar -Dclassifier=javadoc" 67 | 68 | echo "" 69 | echo "9) set new development version" 70 | echo "mvn versions:set -DnewVersion=${new_version}" 71 | echo "git add pom.xml; " 72 | echo "git commit -m \"prepare for next development iteration\"" 73 | 74 | echo "" 75 | echo "10) push to GitHub" 76 | echo "git push" 77 | echo "git push --tags" 78 | 79 | echo "" 80 | echo "11) clean local repo" 81 | echo "git clean -f" 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Apache Maven Wrapper startup batch script, version 3.2.0 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 28 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 29 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 30 | @REM e.g. to debug Maven itself, use 31 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 32 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 33 | @REM ---------------------------------------------------------------------------- 34 | 35 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 36 | @echo off 37 | @REM set title of command window 38 | title %0 39 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 40 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 41 | 42 | @REM set %HOME% to equivalent of $HOME 43 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 44 | 45 | @REM Execute a user defined script before this one 46 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 47 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 48 | if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* 49 | if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* 50 | :skipRcPre 51 | 52 | @setlocal 53 | 54 | set ERROR_CODE=0 55 | 56 | @REM To isolate internal variables from possible post scripts, we use another setlocal 57 | @setlocal 58 | 59 | @REM ==== START VALIDATION ==== 60 | if not "%JAVA_HOME%" == "" goto OkJHome 61 | 62 | echo. 63 | echo Error: JAVA_HOME not found in your environment. >&2 64 | echo Please set the JAVA_HOME variable in your environment to match the >&2 65 | echo location of your Java installation. >&2 66 | echo. 67 | goto error 68 | 69 | :OkJHome 70 | if exist "%JAVA_HOME%\bin\java.exe" goto init 71 | 72 | echo. 73 | echo Error: JAVA_HOME is set to an invalid directory. >&2 74 | echo JAVA_HOME = "%JAVA_HOME%" >&2 75 | echo Please set the JAVA_HOME variable in your environment to match the >&2 76 | echo location of your Java installation. >&2 77 | echo. 78 | goto error 79 | 80 | @REM ==== END VALIDATION ==== 81 | 82 | :init 83 | 84 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 85 | @REM Fallback to current working directory if not found. 86 | 87 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 88 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 89 | 90 | set EXEC_DIR=%CD% 91 | set WDIR=%EXEC_DIR% 92 | :findBaseDir 93 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 94 | cd .. 95 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 96 | set WDIR=%CD% 97 | goto findBaseDir 98 | 99 | :baseDirFound 100 | set MAVEN_PROJECTBASEDIR=%WDIR% 101 | cd "%EXEC_DIR%" 102 | goto endDetectBaseDir 103 | 104 | :baseDirNotFound 105 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 106 | cd "%EXEC_DIR%" 107 | 108 | :endDetectBaseDir 109 | 110 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 111 | 112 | @setlocal EnableExtensions EnableDelayedExpansion 113 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 114 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 115 | 116 | :endReadAdditionalConfig 117 | 118 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" 123 | 124 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 125 | IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B 126 | ) 127 | 128 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 129 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 130 | if exist %WRAPPER_JAR% ( 131 | if "%MVNW_VERBOSE%" == "true" ( 132 | echo Found %WRAPPER_JAR% 133 | ) 134 | ) else ( 135 | if not "%MVNW_REPOURL%" == "" ( 136 | SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" 137 | ) 138 | if "%MVNW_VERBOSE%" == "true" ( 139 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 140 | echo Downloading from: %WRAPPER_URL% 141 | ) 142 | 143 | powershell -Command "&{"^ 144 | "$webclient = new-object System.Net.WebClient;"^ 145 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 146 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 147 | "}"^ 148 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ 149 | "}" 150 | if "%MVNW_VERBOSE%" == "true" ( 151 | echo Finished downloading %WRAPPER_JAR% 152 | ) 153 | ) 154 | @REM End of extension 155 | 156 | @REM If specified, validate the SHA-256 sum of the Maven wrapper jar file 157 | SET WRAPPER_SHA_256_SUM="" 158 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 159 | IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B 160 | ) 161 | IF NOT %WRAPPER_SHA_256_SUM%=="" ( 162 | powershell -Command "&{"^ 163 | "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^ 164 | "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^ 165 | " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^ 166 | " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^ 167 | " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^ 168 | " exit 1;"^ 169 | "}"^ 170 | "}" 171 | if ERRORLEVEL 1 goto error 172 | ) 173 | 174 | @REM Provide a "standardized" way to retrieve the CLI args that will 175 | @REM work with both Windows and non-Windows executions. 176 | set MAVEN_CMD_LINE_ARGS=%* 177 | 178 | %MAVEN_JAVA_EXE% ^ 179 | %JVM_CONFIG_MAVEN_PROPS% ^ 180 | %MAVEN_OPTS% ^ 181 | %MAVEN_DEBUG_OPTS% ^ 182 | -classpath %WRAPPER_JAR% ^ 183 | "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ 184 | %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 185 | if ERRORLEVEL 1 goto error 186 | goto end 187 | 188 | :error 189 | set ERROR_CODE=1 190 | 191 | :end 192 | @endlocal & set ERROR_CODE=%ERROR_CODE% 193 | 194 | if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost 195 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 196 | if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" 197 | if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" 198 | :skipRcPost 199 | 200 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 201 | if "%MAVEN_BATCH_PAUSE%"=="on" pause 202 | 203 | if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% 204 | 205 | cmd /C exit /B %ERROR_CODE% 206 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/InvalidExitUtil.java: -------------------------------------------------------------------------------- 1 | package org.zeroturnaround.exec; 2 | 3 | import java.util.Set; 4 | import java.util.concurrent.TimeoutException; 5 | 6 | /** 7 | * Helper for checking the exit code of the finished process. 8 | */ 9 | class InvalidExitUtil { 10 | 11 | /** 12 | * In case {@link InvalidExitValueException} or {@link TimeoutException} is thrown and we have read the process output 13 | * we include the output up to this length in the error message. If the output is longer we truncate it. 14 | */ 15 | private static final int MAX_OUTPUT_SIZE_IN_ERROR_MESSAGE = 5000; 16 | 17 | /** 18 | * Check the process exit value. 19 | */ 20 | public static void checkExit(ProcessAttributes attributes, ProcessResult result) { 21 | Set allowedExitValues = attributes.getAllowedExitValues(); 22 | if (allowedExitValues != null && !allowedExitValues.contains(result.getExitValue())) { 23 | StringBuilder sb = new StringBuilder(); 24 | sb.append("Unexpected exit value: ").append(result.getExitValue()); 25 | sb.append(", allowed exit values: ").append(allowedExitValues); 26 | addExceptionMessageSuffix(attributes, sb, result.hasOutput() ? result.getOutput() : null); 27 | throw new InvalidExitValueException(sb.toString(), result); 28 | } 29 | } 30 | 31 | public static void addExceptionMessageSuffix(ProcessAttributes attributes, StringBuilder sb, ProcessOutput output) { 32 | sb.append(", executed command ").append(attributes.getCommand()); 33 | if (attributes.getDirectory() != null) { 34 | sb.append(" in directory ").append(attributes.getDirectory()); 35 | } 36 | if (!attributes.getEnvironment().isEmpty()) { 37 | sb.append(" with environment ").append(attributes.getEnvironment()); 38 | } 39 | if (output != null) { 40 | int length = output.getBytes().length; 41 | String out = output.getString(); 42 | if (out.length() <= MAX_OUTPUT_SIZE_IN_ERROR_MESSAGE) { 43 | sb.append(", output was ").append(length).append(" bytes:\n").append(out.trim()); 44 | } 45 | else { 46 | sb.append(", output was ").append(length).append(" bytes (truncated):\n"); 47 | int halfLimit = MAX_OUTPUT_SIZE_IN_ERROR_MESSAGE / 2; 48 | sb.append(out.substring(0, halfLimit)).append("\n...\n").append(out.substring(out.length() - halfLimit).trim()); 49 | } 50 | } 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/InvalidExitValueException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec; 19 | 20 | /** 21 | * Process finished with a forbidden exit value. 22 | * 23 | * @author Rein Raudjärv 24 | * 25 | * @see ProcessExecutor#exitValues(Integer...) 26 | */ 27 | public class InvalidExitValueException extends InvalidResultException { 28 | 29 | private static final long serialVersionUID = 1L; 30 | 31 | /** 32 | * @param message the detail message of the exception 33 | * @param result result of execution (contains also the exit value) 34 | */ 35 | public InvalidExitValueException(String message, ProcessResult result) { 36 | super(message, result); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/InvalidOutputException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec; 19 | 20 | import org.zeroturnaround.exec.listener.ProcessListener; 21 | 22 | /** 23 | * Process finished with an unexpected output. 24 | * 25 | * @author Rein Raudjärv 26 | * @see ProcessListener#afterFinish(Process, ProcessResult) 27 | * @since 1.8 28 | */ 29 | public class InvalidOutputException extends InvalidResultException { 30 | 31 | private static final long serialVersionUID = 1L; 32 | 33 | /** 34 | * @param message the detail message of the exception 35 | * @param result result of execution (contains also the exit value) 36 | */ 37 | public InvalidOutputException(String message, ProcessResult result) { 38 | super(message, result); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/InvalidResultException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec; 19 | 20 | /** 21 | * Process finished with an unexpected result. 22 | * 23 | * @author Rein Raudjärv 24 | * @since 1.8 25 | */ 26 | public class InvalidResultException extends RuntimeException { 27 | 28 | private static final long serialVersionUID = 1L; 29 | 30 | /** 31 | * Actual exit value and process output. 32 | */ 33 | private final ProcessResult result; 34 | 35 | /** 36 | * @param message the detail message of the exception 37 | * @param result result of execution (contains also the exit value) 38 | */ 39 | public InvalidResultException(String message, ProcessResult result) { 40 | super(message); 41 | this.result = result; 42 | } 43 | 44 | /** 45 | * @return actual process result. 46 | */ 47 | public ProcessResult getResult() { 48 | return result; 49 | } 50 | 51 | /** 52 | * @return the exit value of the finished process. 53 | */ 54 | public int getExitValue() { 55 | return result.getExitValue(); 56 | } 57 | 58 | /** 59 | * @return actual process result. 60 | * @deprecated use {@link #getResult()} 61 | */ 62 | public ProcessResult result() { 63 | return getResult(); 64 | } 65 | 66 | /** 67 | * @return the exit value of the finished process. 68 | * @deprecated use {@link #getExitValue()} 69 | */ 70 | public int exitValue() { 71 | return getExitValue(); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/MDCCallableAdapter.java: -------------------------------------------------------------------------------- 1 | package org.zeroturnaround.exec; 2 | 3 | import java.util.Map; 4 | import java.util.concurrent.Callable; 5 | 6 | import org.slf4j.MDC; 7 | 8 | /** 9 | * Restores the MDC context map for the target action. 10 | */ 11 | public class MDCCallableAdapter implements Callable { 12 | 13 | private final Callable target; 14 | 15 | private final Map contextMap; 16 | 17 | public MDCCallableAdapter(Callable target, Map contextMap) { 18 | this.target = target; 19 | this.contextMap = contextMap; 20 | } 21 | 22 | public T call() throws Exception { 23 | MDC.setContextMap(contextMap); 24 | try { 25 | return target.call(); 26 | } 27 | finally { 28 | MDC.clear(); 29 | } 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/MDCRunnableAdapter.java: -------------------------------------------------------------------------------- 1 | package org.zeroturnaround.exec; 2 | 3 | import java.util.Map; 4 | 5 | import org.slf4j.MDC; 6 | 7 | /** 8 | * Restores the MDC context map for the target action. 9 | */ 10 | public class MDCRunnableAdapter implements Runnable { 11 | 12 | private final Runnable target; 13 | 14 | private final Map contextMap; 15 | 16 | public MDCRunnableAdapter(Runnable target, Map contextMap) { 17 | this.target = target; 18 | this.contextMap = contextMap; 19 | } 20 | 21 | public void run() { 22 | MDC.setContextMap(contextMap); 23 | try { 24 | target.run(); 25 | } 26 | finally { 27 | MDC.clear(); 28 | } 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/MessageLogger.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec; 19 | 20 | import org.slf4j.Logger; 21 | 22 | /** 23 | * Logs messages at certain level. 24 | */ 25 | public interface MessageLogger { 26 | 27 | /** 28 | * Log a message at certain level according to the specified format and arguments. 29 | * 30 | * @param log logger to be used. 31 | * @param format the format string 32 | * @param arguments a list of arguments 33 | */ 34 | void message(Logger log, String format, Object... arguments); 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/MessageLoggers.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec; 19 | 20 | import org.slf4j.Logger; 21 | import org.zeroturnaround.exec.stream.slf4j.Level; 22 | 23 | /** 24 | * Contains {@link MessageLogger} instances for various log levels. 25 | */ 26 | public class MessageLoggers { 27 | 28 | public static final MessageLogger NOP = new MessageLogger() { 29 | public void message(Logger log, String format, Object... arguments) { 30 | // do nothing 31 | } 32 | }; 33 | 34 | public static final MessageLogger TRACE = new MessageLogger() { 35 | public void message(Logger log, String format, Object... arguments) { 36 | log.trace(format, arguments); 37 | } 38 | }; 39 | 40 | public static final MessageLogger DEBUG = new MessageLogger() { 41 | public void message(Logger log, String format, Object... arguments) { 42 | log.debug(format, arguments); 43 | } 44 | }; 45 | 46 | public static final MessageLogger INFO = new MessageLogger() { 47 | public void message(Logger log, String format, Object... arguments) { 48 | log.info(format, arguments); 49 | } 50 | }; 51 | 52 | public static final MessageLogger get(Level level) { 53 | switch (level) { 54 | case TRACE: return TRACE; 55 | case DEBUG: return DEBUG; 56 | case INFO: return INFO; 57 | default: 58 | throw new IllegalArgumentException("Invalid level " + level); 59 | } 60 | } 61 | 62 | private MessageLoggers() { 63 | // hide 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/ProcessAttributes.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec; 19 | 20 | import java.io.File; 21 | import java.util.List; 22 | import java.util.Map; 23 | import java.util.Set; 24 | 25 | /** 26 | * Immutable set of attributes used to start a process. 27 | */ 28 | class ProcessAttributes { 29 | 30 | /** 31 | * The external program and its arguments. 32 | */ 33 | private final List command; 34 | 35 | /** 36 | * Working directory, null in case of current working directory. 37 | */ 38 | private final File directory; 39 | 40 | /** 41 | * Environment variables which are added (removed in case of null values) to the started process. 42 | */ 43 | private final Map environment; 44 | 45 | /** 46 | * Set of accepted exit codes or null if all exit codes are allowed. 47 | */ 48 | private final Set allowedExitValues; 49 | 50 | public ProcessAttributes(List command, File directory, Map environment, Set allowedExitValues) { 51 | this.command = command; 52 | this.directory = directory; 53 | this.environment = environment; 54 | this.allowedExitValues = allowedExitValues; 55 | } 56 | 57 | public List getCommand() { 58 | return command; 59 | } 60 | 61 | public File getDirectory() { 62 | return directory; 63 | } 64 | 65 | public Map getEnvironment() { 66 | return environment; 67 | } 68 | 69 | public Set getAllowedExitValues() { 70 | return allowedExitValues; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/ProcessInitException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec; 19 | 20 | import java.io.IOException; 21 | 22 | /** 23 | * Creating a process failed providing an error code. 24 | * 25 | *

26 | * Wraps an {@link IOException} like: 27 | *

    28 | *
  • java.io.IOException: Cannot run program "ls": java.io.IOException: error=12, Cannot allocate memory
  • 29 | *
  • java.io.IOException: Cannot run program "ls": error=316, Unknown error: 316
  • 30 | *
31 | */ 32 | public class ProcessInitException extends IOException { 33 | 34 | private static final String BEFORE_CODE = " error="; 35 | private static final String AFTER_CODE = ", "; 36 | private static final String NEW_INFIX = " Error="; 37 | 38 | private final int errorCode; 39 | 40 | public ProcessInitException(String message, Throwable cause, int errorCode) { 41 | super(message, cause); 42 | this.errorCode = errorCode; 43 | } 44 | 45 | /** 46 | * @return error code raised when a process failed to start. 47 | */ 48 | public int getErrorCode() { 49 | return errorCode; 50 | } 51 | 52 | /** 53 | * Try to wrap a given {@link IOException} into a {@link ProcessInitException}. 54 | * 55 | * @param prefix prefix to be added in the message. 56 | * @param e existing exception possibly containing an error code in its message. 57 | * @return new exception containing the prefix, error code and its description in the message plus the error code value as a field, 58 | * null if we were unable to find an error code from the original message. 59 | */ 60 | public static ProcessInitException newInstance(String prefix, IOException e) { 61 | String m = e.getMessage(); 62 | if (m == null) { 63 | return null; 64 | } 65 | int i = m.lastIndexOf(BEFORE_CODE); 66 | if (i == -1) { 67 | return null; 68 | } 69 | int j = m.indexOf(AFTER_CODE, i); 70 | if (j == -1) { 71 | return null; 72 | } 73 | int code; 74 | try { 75 | code = Integer.parseInt(m.substring(i + BEFORE_CODE.length(), j)); 76 | } 77 | catch (NumberFormatException n) { 78 | return null; 79 | } 80 | return new ProcessInitException(prefix + NEW_INFIX + m.substring(i + BEFORE_CODE.length()), e, code); 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/ProcessOutput.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec; 19 | 20 | import java.io.UnsupportedEncodingException; 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | import java.util.StringTokenizer; 24 | 25 | /** 26 | * Standard output of a finished process. 27 | * 28 | * @author Rein Raudjärv 29 | * @see ProcessExecutor 30 | */ 31 | public class ProcessOutput { 32 | 33 | /** 34 | * Process output (not null). 35 | */ 36 | private final byte[] data; 37 | 38 | public ProcessOutput(byte[] data) { 39 | this.data = data; 40 | } 41 | 42 | /** 43 | * @return binary output of the finished process. 44 | */ 45 | public byte[] getBytes() { 46 | return data; 47 | } 48 | 49 | /** 50 | * @return output of the finished process converted to a String using platform's default encoding. 51 | */ 52 | public String getString() { 53 | return new String(getBytes()); 54 | } 55 | 56 | /** 57 | * @return output of the finished process converted to UTF-8 String. 58 | */ 59 | public String getUTF8() { 60 | return getString("UTF-8"); 61 | } 62 | 63 | /** 64 | * @return output of the finished process converted to a String. 65 | * 66 | * @param charset The name of a supported char set. 67 | */ 68 | public String getString(String charset) { 69 | try { 70 | return new String(getBytes(), charset); 71 | } 72 | catch (UnsupportedEncodingException e) { 73 | throw new IllegalStateException(e.getMessage()); 74 | } 75 | } 76 | 77 | /** 78 | * @return output lines of the finished process converted using platform's default encoding. 79 | */ 80 | public List getLines() { 81 | return getLinesFrom(getString()); 82 | } 83 | 84 | /** 85 | * @return output lines of the finished process converted using UTF-8. 86 | */ 87 | public List getLinesAsUTF8() { 88 | return getLinesFrom(getUTF8()); 89 | } 90 | 91 | /** 92 | * @return output lines of the finished process converted using a given char set. 93 | * 94 | * @param charset The name of a supported char set. 95 | */ 96 | public List getLines(String charset) { 97 | return getLinesFrom(getString(charset)); 98 | } 99 | 100 | static List getLinesFrom(String output) { 101 | // Split using both Windows and UNIX line separators 102 | List result = new ArrayList(); 103 | StringTokenizer st = new StringTokenizer(output, "\n\r"); 104 | while (st.hasMoreTokens()) { 105 | result.add(st.nextToken()); 106 | } 107 | return result; 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/ProcessResult.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec; 19 | 20 | /** 21 | * Exit value and output of a finished process. 22 | * 23 | * @author Rein Raudjärv 24 | * @see ProcessExecutor 25 | */ 26 | public class ProcessResult { 27 | 28 | /** 29 | * Exit value of the finished process. 30 | */ 31 | private final int exitValue; 32 | 33 | /** 34 | * Process output or null if it wasn't read. 35 | */ 36 | private final ProcessOutput output; 37 | 38 | public ProcessResult(int exitCode, ProcessOutput output) { 39 | this.exitValue = exitCode; 40 | this.output = output; 41 | } 42 | 43 | /** 44 | * @return the exit value of the finished process. 45 | */ 46 | public int getExitValue() { 47 | return exitValue; 48 | } 49 | 50 | /** 51 | * @return the exit value of the finished process. 52 | * @deprecated use {@link #getExitValue()} 53 | */ 54 | public int exitValue() { 55 | return getExitValue(); 56 | } 57 | 58 | /** 59 | * @return true if the process output was read. 60 | */ 61 | public boolean hasOutput() { 62 | return output != null; 63 | } 64 | 65 | /** 66 | * @return output of the finished process. 67 | * You have to invoke {@link ProcessExecutor#readOutput(boolean)} to set the process output to be read. 68 | * 69 | * @throws IllegalStateException if the output was not read. 70 | */ 71 | public ProcessOutput getOutput() { 72 | if (output == null) 73 | throw new IllegalStateException("Process output was not read. To enable output reading please call ProcessExecutor.readOutput(true) before starting the process."); 74 | return output; 75 | } 76 | 77 | /** 78 | * @return binary output of the finished process. 79 | * You have to invoke {@link ProcessExecutor#readOutput(boolean)} to set the process output to be read. 80 | * 81 | * @throws IllegalStateException if the output was not read. 82 | */ 83 | public byte[] output() { 84 | return getOutput().getBytes(); 85 | } 86 | 87 | /** 88 | * @return output of the finished process converted to a String using platform's default encoding. 89 | * You have to invoke {@link ProcessExecutor#readOutput(boolean)} to set the process output to be read. 90 | * 91 | * @throws IllegalStateException if the output was not read. 92 | */ 93 | public String outputString() { 94 | return getOutput().getString(); 95 | } 96 | 97 | /** 98 | * @return output of the finished process converted to UTF-8 String. 99 | * You have to invoke {@link ProcessExecutor#readOutput(boolean)} to set the process output to be read. 100 | * 101 | * @throws IllegalStateException if the output was not read. 102 | */ 103 | public String outputUTF8() { 104 | return getOutput().getUTF8(); 105 | } 106 | 107 | /** 108 | * @return output of the finished process converted to a String. 109 | * You have to invoke {@link ProcessExecutor#readOutput(boolean)} to set the process output to be read. 110 | * 111 | * @param charset The name of a supported char set. 112 | * @throws IllegalStateException if the output was not read or the char set was not supported. 113 | */ 114 | public String outputString(String charset) { 115 | return getOutput().getString(charset); 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/StartedProcess.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec; 19 | 20 | import java.util.concurrent.Future; 21 | 22 | /** 23 | * Represents a process that has started. It may or may not have finished. 24 | * 25 | * @author Rein Raudjärv 26 | */ 27 | public class StartedProcess { 28 | 29 | /** 30 | * The sub process started. 31 | */ 32 | private final Process process; 33 | 34 | /** 35 | * The asynchronous result of the started process. 36 | */ 37 | private final Future future; 38 | 39 | public StartedProcess(Process process, Future future) { 40 | this.process = process; 41 | this.future = future; 42 | } 43 | 44 | /** 45 | * @return the started process. 46 | */ 47 | public Process getProcess() { 48 | return process; 49 | } 50 | 51 | /** 52 | * @return asynchronous result of the started process. 53 | */ 54 | public Future getFuture() { 55 | return future; 56 | } 57 | 58 | /** 59 | * @return the started process. 60 | * @deprecated use {@link #getProcess()} instead. 61 | */ 62 | public Process process() { 63 | return getProcess(); 64 | } 65 | 66 | /** 67 | * @return asynchronous result of the started process. 68 | * @deprecated use {@link #getFuture()} instead. 69 | */ 70 | public Future future() { 71 | return getFuture(); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/WaitForProcess.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec; 19 | 20 | import java.io.ByteArrayOutputStream; 21 | import java.io.IOException; 22 | import java.util.concurrent.Callable; 23 | 24 | import org.slf4j.Logger; 25 | import org.slf4j.LoggerFactory; 26 | import org.zeroturnaround.exec.close.ProcessCloser; 27 | import org.zeroturnaround.exec.listener.ProcessListener; 28 | import org.zeroturnaround.exec.stop.ProcessStopper; 29 | 30 | 31 | /** 32 | * Handles the executed process. 33 | * 34 | * @author Rein Raudjärv 35 | */ 36 | class WaitForProcess implements Callable { 37 | 38 | private static final Logger log = LoggerFactory.getLogger(WaitForProcess.class); 39 | 40 | private final Process process; 41 | 42 | /** 43 | * Set of main attributes used to start the process. 44 | */ 45 | private final ProcessAttributes attributes; 46 | 47 | /** 48 | * Helper for stopping the process in case of interruption. 49 | */ 50 | private final ProcessStopper stopper; 51 | 52 | /** 53 | * Helper for closing the process' standard streams. 54 | */ 55 | private final ProcessCloser closer; 56 | 57 | /** 58 | * Buffer where the process output is redirected to or null if it's not used. 59 | */ 60 | private final ByteArrayOutputStream out; 61 | 62 | /** 63 | * Process event listener (not null). 64 | */ 65 | private final ProcessListener listener; 66 | 67 | /** 68 | * Helper for logging messages about starting and waiting for the processes. 69 | */ 70 | private final MessageLogger messageLogger; 71 | 72 | /** 73 | * Thread which executes this operation. 74 | */ 75 | private volatile Thread workerThread; 76 | 77 | public WaitForProcess(Process process, ProcessAttributes attributes, ProcessStopper stopper, ProcessCloser closer, ByteArrayOutputStream out, ProcessListener listener, MessageLogger messageLogger) { 78 | this.process = process; 79 | this.attributes = attributes; 80 | this.stopper = stopper; 81 | this.closer = closer; 82 | this.out = out; 83 | this.listener = listener; 84 | this.messageLogger = messageLogger; 85 | } 86 | 87 | /** 88 | * @return the sub process. 89 | */ 90 | public Process getProcess() { 91 | return process; 92 | } 93 | 94 | public ProcessResult call() throws IOException, InterruptedException { 95 | try { 96 | workerThread = Thread.currentThread(); 97 | int exit; 98 | boolean finished = false; 99 | try { 100 | exit = process.waitFor(); 101 | finished = true; 102 | messageLogger.message(log, "{} stopped with exit code {}", this, exit); 103 | } 104 | finally { 105 | if (!finished) { 106 | messageLogger.message(log, "Stopping {}...", this); 107 | stopper.stop(process); 108 | } 109 | 110 | closer.close(process); 111 | } 112 | ProcessOutput output = getCurrentOutput(); 113 | ProcessResult result = new ProcessResult(exit, output); 114 | InvalidExitUtil.checkExit(attributes, result); 115 | listener.afterFinish(process, result); 116 | return result; 117 | } 118 | finally { 119 | // Invoke listeners - regardless process finished or got cancelled 120 | listener.afterStop(process); 121 | workerThread = null; 122 | } 123 | } 124 | 125 | private ProcessOutput getCurrentOutput() { 126 | return out == null ? null : new ProcessOutput(out.toByteArray()); 127 | } 128 | 129 | /** 130 | * Adds a suffix for an error message including: 131 | *
    132 | *
  • executed command
  • 133 | *
  • working directory (unless it's inherited from parent)
  • 134 | *
  • environment (unless it's the same with the parent)
  • 135 | *
  • output read so far (unless it's not read)
  • 136 | *
137 | * @param sb where the suffix is appended to. 138 | */ 139 | public void addExceptionMessageSuffix(StringBuilder sb) { 140 | InvalidExitUtil.addExceptionMessageSuffix(attributes, sb, getCurrentOutput()); 141 | } 142 | 143 | /** 144 | * @return current stacktrace of the worker thread, null if this operation is currently not running. 145 | */ 146 | public StackTraceElement[] getStackTrace() { 147 | Thread t = workerThread; 148 | return t == null ? null : t.getStackTrace(); 149 | } 150 | 151 | @Override 152 | public String toString() { 153 | return process.toString(); 154 | } 155 | 156 | } 157 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/close/ExceptionUtil.java: -------------------------------------------------------------------------------- 1 | package org.zeroturnaround.exec.close; 2 | 3 | import java.lang.reflect.Method; 4 | 5 | class ExceptionUtil { 6 | 7 | /** 8 | * Throwable.addSuppressed(Throwable) added in Java 7. 9 | */ 10 | private static final Method METHOD_ADD_SUPPRESSED = findAddSuppressed(); 11 | 12 | private static Method findAddSuppressed() { 13 | try { 14 | return Throwable.class.getDeclaredMethod("addSuppressed", Throwable.class); 15 | } 16 | catch (Exception e) { 17 | // ignore 18 | } 19 | return null; 20 | } 21 | 22 | /** 23 | * If supported. appends the specified exception to the exceptions that were suppressed in order to deliver this exception. 24 | */ 25 | public static void addSuppressed(Throwable t, Throwable suppressed) { 26 | if (METHOD_ADD_SUPPRESSED != null) { 27 | try { 28 | METHOD_ADD_SUPPRESSED.invoke(t, suppressed); 29 | } 30 | catch (Exception e) { 31 | throw new IllegalStateException("Could not add suppressed exception:", e); 32 | } 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/close/ProcessCloser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.close; 19 | 20 | import java.io.IOException; 21 | 22 | /** 23 | * Abstraction for closing sub process' streams. 24 | */ 25 | public interface ProcessCloser { 26 | 27 | /** 28 | * Closes standard streams of a given sub process. 29 | * 30 | * @param process sub process (not null). 31 | * @throws IOException if I/O errors occur while closing the underlying stream 32 | * @throws InterruptedException if underlying throws a InterruptedException 33 | */ 34 | void close(Process process) throws IOException, InterruptedException; 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/close/StandardProcessCloser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.close; 19 | 20 | import java.io.IOException; 21 | 22 | import org.slf4j.Logger; 23 | import org.slf4j.LoggerFactory; 24 | import org.zeroturnaround.exec.stream.ExecuteStreamHandler; 25 | 26 | /** 27 | * Stops {@link ExecuteStreamHandler} from pumping the streams and closes them. 28 | */ 29 | public class StandardProcessCloser implements ProcessCloser { 30 | 31 | private static final Logger log = LoggerFactory.getLogger(StandardProcessCloser.class); 32 | 33 | protected final ExecuteStreamHandler streams; 34 | 35 | public StandardProcessCloser(ExecuteStreamHandler streams) { 36 | this.streams = streams; 37 | } 38 | 39 | public void close(Process process) throws IOException, InterruptedException { 40 | if (streams != null) { 41 | streams.stop(); 42 | } 43 | closeStreams(process); 44 | } 45 | 46 | /** 47 | * Close the streams belonging to the given Process. 48 | */ 49 | private void closeStreams(final Process process) throws IOException { 50 | IOException caught = null; 51 | 52 | try { 53 | process.getOutputStream().close(); 54 | } 55 | catch (IOException e) { 56 | if (e.getMessage().equals("Stream closed")) { 57 | /** 58 | * OutputStream's contract for the close() method: If the stream is already closed then invoking this method has no effect. 59 | * 60 | * When a UNIXProcess exits ProcessPipeOutputStream automatically closes its target FileOutputStream and replaces it with NullOutputStream. 61 | * However the ProcessPipeOutputStream doesn't close itself at that moment. 62 | * As ProcessPipeOutputStream extends BufferedOutputStream extends FilterOutputStream closing it flushes the buffer first. 63 | * In Java 7 closing FilterOutputStream ignores any exception thrown by the target OutputStream. Since Java 8 these exceptions are now thrown. 64 | * 65 | * So since Java 8 after UNIXProcess detects the exit and there's something in the output buffer closing this stream throws IOException 66 | * with message "Stream closed" from NullOutputStream. 67 | */ 68 | log.trace("Failed to close process output stream:", e); 69 | } 70 | else { 71 | caught = add(caught, e); 72 | } 73 | } 74 | 75 | try { 76 | process.getInputStream().close(); 77 | } 78 | catch (IOException e) { 79 | caught = add(caught, e); 80 | } 81 | 82 | try { 83 | process.getErrorStream().close(); 84 | } 85 | catch (IOException e) { 86 | caught = add(caught, e); 87 | } 88 | 89 | if (caught != null) { 90 | throw caught; 91 | } 92 | } 93 | 94 | private static IOException add(IOException exception, IOException newException) { 95 | if (exception == null) { 96 | return newException; 97 | } 98 | ExceptionUtil.addSuppressed(exception, newException); 99 | return exception; 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/close/TimeoutProcessCloser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.close; 19 | 20 | import java.io.IOException; 21 | import java.util.concurrent.Callable; 22 | import java.util.concurrent.ExecutionException; 23 | import java.util.concurrent.ExecutorService; 24 | import java.util.concurrent.Executors; 25 | import java.util.concurrent.Future; 26 | import java.util.concurrent.TimeUnit; 27 | import java.util.concurrent.TimeoutException; 28 | 29 | import org.slf4j.Logger; 30 | import org.slf4j.LoggerFactory; 31 | import org.zeroturnaround.exec.stream.ExecuteStreamHandler; 32 | import org.zeroturnaround.exec.stream.PumpStreamHandler; 33 | 34 | /** 35 | * Same as {@link StandardProcessCloser} but only waits fixed period for the closing. 36 | * On timeout a warning is logged but no error is thrown. 37 | *

38 | * This is used on Windows where sometimes sub process' streams do not close properly. 39 | */ 40 | public class TimeoutProcessCloser extends StandardProcessCloser { 41 | 42 | private static final Logger log = LoggerFactory.getLogger(TimeoutProcessCloser.class); 43 | 44 | private final long timeout; 45 | 46 | private final TimeUnit unit; 47 | 48 | /** 49 | * Creates new instance of {@link TimeoutProcessCloser}. 50 | * 51 | * @param streams helper for pumping the streams. 52 | * @param timeout how long should we wait for the closing. 53 | * @param unit unit of the timeout value. 54 | */ 55 | public TimeoutProcessCloser(ExecuteStreamHandler streams, long timeout, TimeUnit unit) { 56 | super(streams); 57 | this.timeout = timeout; 58 | this.unit = unit; 59 | } 60 | 61 | public void close(final Process process) throws IOException, InterruptedException { 62 | ExecutorService service = Executors.newSingleThreadScheduledExecutor(); 63 | Future future = service.submit(new Callable() { 64 | public Void call() throws Exception { 65 | doClose(process); 66 | return null; 67 | } 68 | }); 69 | // Previously submitted tasks are executed but no new tasks will be accepted. 70 | service.shutdown(); 71 | 72 | try { 73 | future.get(timeout, unit); 74 | } 75 | catch (ExecutionException e) { 76 | throw new IllegalStateException("Could not close streams of " + process, e.getCause()); 77 | } 78 | catch (TimeoutException e) { 79 | log.warn("Could not close streams of {} in {} {}", process, timeout, getUnitsAsString(timeout, unit)); 80 | } 81 | finally { 82 | // Ensure that any data received so far is flushed from buffers 83 | if (streams instanceof PumpStreamHandler) { 84 | ((PumpStreamHandler) streams).flush(); 85 | } 86 | } 87 | } 88 | 89 | protected void doClose(final Process process) throws IOException, InterruptedException { 90 | super.close(process); 91 | } 92 | 93 | private static String getUnitsAsString(long d, TimeUnit unit) { 94 | String result = unit.toString().toLowerCase(); 95 | if (d == 1) { 96 | result = result.substring(0, result.length() - 1); 97 | } 98 | return result; 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/listener/CompositeProcessListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.listener; 19 | 20 | import java.util.Iterator; 21 | import java.util.List; 22 | import java.util.concurrent.CopyOnWriteArrayList; 23 | 24 | import org.zeroturnaround.exec.ProcessExecutor; 25 | import org.zeroturnaround.exec.ProcessResult; 26 | 27 | 28 | /** 29 | * Composite process event handler. 30 | * 31 | * @author Rein Raudjärv 32 | */ 33 | public class CompositeProcessListener extends ProcessListener implements Cloneable { 34 | 35 | private final List children = new CopyOnWriteArrayList(); 36 | 37 | public CompositeProcessListener() { 38 | // no children 39 | } 40 | 41 | public CompositeProcessListener(List children) { 42 | this.children.addAll(children); 43 | } 44 | 45 | /** 46 | * Add new listener. 47 | * 48 | * @param listener listener to be added. 49 | */ 50 | public void add(ProcessListener listener) { 51 | children.add(listener); 52 | } 53 | 54 | /** 55 | * Remove existing listener. 56 | * 57 | * @param listener listener to be removed. 58 | */ 59 | public void remove(ProcessListener listener) { 60 | children.remove(listener); 61 | } 62 | 63 | /** 64 | * Remove existing listeners of given type or its sub-types. 65 | * 66 | * @param type listener type. 67 | */ 68 | public void removeAll(Class type) { 69 | for (Iterator it = children.iterator(); it.hasNext();) { 70 | if (type.isInstance(it.next())) 71 | it.remove(); 72 | } 73 | } 74 | 75 | /** 76 | * Remove all existing listeners. 77 | */ 78 | public void clear() { 79 | children.clear(); 80 | } 81 | 82 | public CompositeProcessListener clone() { 83 | return new CompositeProcessListener(children); 84 | } 85 | 86 | @Override 87 | public void beforeStart(ProcessExecutor executor) { 88 | for (ProcessListener child : children) { 89 | child.beforeStart(executor); 90 | } 91 | } 92 | 93 | @Override 94 | public void afterStart(Process process, ProcessExecutor executor) { 95 | for (ProcessListener child : children) { 96 | child.afterStart(process, executor); 97 | } 98 | } 99 | 100 | @Override 101 | public void afterFinish(Process process, ProcessResult result) { 102 | for (ProcessListener child : children) { 103 | child.afterFinish(process, result); 104 | } 105 | } 106 | 107 | @Override 108 | public void afterStop(Process process) { 109 | for (ProcessListener child : children) { 110 | child.afterStop(process); 111 | } 112 | } 113 | 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/listener/DestroyerListenerAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.listener; 19 | 20 | import org.zeroturnaround.exec.ProcessExecutor; 21 | 22 | /** 23 | * Process event handler that wraps a process destroyer. 24 | * 25 | * @author Rein Raudjärv 26 | * 27 | * @see ProcessDestroyer 28 | */ 29 | public class DestroyerListenerAdapter extends ProcessListener { 30 | 31 | private final ProcessDestroyer destroyer; 32 | 33 | public DestroyerListenerAdapter(ProcessDestroyer destroyer) { 34 | if (destroyer == null) 35 | throw new IllegalArgumentException("Process destroyer must be provided."); 36 | this.destroyer = destroyer; 37 | } 38 | 39 | @Override 40 | public void afterStart(Process process, ProcessExecutor executor) { 41 | destroyer.add(process); 42 | } 43 | 44 | @Override 45 | public void afterStop(Process process) { 46 | destroyer.remove(process); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/listener/ProcessDestroyer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * NOTICE: This file originates from the Apache Commons Exec package. 19 | * It has been modified to fit our needs. 20 | * 21 | * The following is the original header of the file in Apache Commons Exec: 22 | * 23 | * Licensed to the Apache Software Foundation (ASF) under one or more 24 | * contributor license agreements. See the NOTICE file distributed with 25 | * this work for additional information regarding copyright ownership. 26 | * The ASF licenses this file to You under the Apache License, Version 2.0 27 | * (the "License"); you may not use this file except in compliance with 28 | * the License. You may obtain a copy of the License at 29 | * 30 | * http://www.apache.org/licenses/LICENSE-2.0 31 | * 32 | * Unless required by applicable law or agreed to in writing, software 33 | * distributed under the License is distributed on an "AS IS" BASIS, 34 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 35 | * See the License for the specific language governing permissions and 36 | * limitations under the License. 37 | */ 38 | package org.zeroturnaround.exec.listener; 39 | 40 | /** 41 | * Destroys all registered {@link java.lang.Process} after a certain event, 42 | * typically when the VM exits 43 | * @see ShutdownHookProcessDestroyer 44 | */ 45 | public interface ProcessDestroyer { 46 | 47 | /** 48 | * Returns true if the specified 49 | * {@link java.lang.Process} was 50 | * successfully added to the list of processes to be destroy. 51 | * 52 | * @param process 53 | * the process to add 54 | * @return true if the specified 55 | * {@link java.lang.Process} was 56 | * successfully added 57 | */ 58 | boolean add(Process process); 59 | 60 | /** 61 | * Returns true if the specified 62 | * {@link java.lang.Process} was 63 | * successfully removed from the list of processes to be destroy. 64 | * 65 | * @param process 66 | * the process to remove 67 | * @return true if the specified 68 | * {@link java.lang.Process} was 69 | * successfully removed 70 | */ 71 | boolean remove(Process process); 72 | 73 | /** 74 | * Returns the number of registered processes. 75 | * 76 | * @return the number of register process 77 | */ 78 | int size(); 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/listener/ProcessListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.listener; 19 | 20 | import org.zeroturnaround.exec.ProcessExecutor; 21 | import org.zeroturnaround.exec.ProcessResult; 22 | 23 | /** 24 | * Event handler for process events. 25 | *

26 | * This is a class instead of interface in order to add new methods without updating all implementations. 27 | * 28 | * @author Rein Raudjärv 29 | * @see ProcessExecutor#addListener(ProcessListener) 30 | */ 31 | public abstract class ProcessListener { 32 | 33 | /** 34 | * Invoked before a process is started. 35 | * 36 | * @param executor executor used for starting a process. 37 | * Any changes made here apply to the starting process. 38 | * Once the process has started it is not affected by the {@link ProcessExecutor} any more. 39 | */ 40 | public void beforeStart(ProcessExecutor executor) { 41 | // do nothing 42 | } 43 | 44 | /** 45 | * Invoked after a process has started. 46 | * 47 | * @param process the process started. 48 | * @param executor executor used for starting the process. 49 | * Modifying the {@link ProcessExecutor} only affects the following processes 50 | * not the one just started. 51 | */ 52 | public void afterStart(Process process, ProcessExecutor executor) { 53 | // do nothing 54 | } 55 | 56 | /** 57 | * Invoked after a process has finished successfully. 58 | * 59 | * @param process process just finished. 60 | * @param result result of the finished process. 61 | * @since 1.8 62 | */ 63 | public void afterFinish(Process process, ProcessResult result) { 64 | // do nothing 65 | } 66 | 67 | /** 68 | * Invoked after a process has exited (whether finished or cancelled). 69 | * 70 | * @param process process just stopped. 71 | */ 72 | public void afterStop(Process process) { 73 | // do nothing 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/listener/ShutdownHookProcessDestroyer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * NOTICE: This file originates from the Apache Commons Exec package. 19 | * It has been modified to fit our needs. 20 | * 21 | * The following is the original header of the file in Apache Commons Exec: 22 | * 23 | * Licensed to the Apache Software Foundation (ASF) under one or more 24 | * contributor license agreements. See the NOTICE file distributed with 25 | * this work for additional information regarding copyright ownership. 26 | * The ASF licenses this file to You under the Apache License, Version 2.0 27 | * (the "License"); you may not use this file except in compliance with 28 | * the License. You may obtain a copy of the License at 29 | * 30 | * http://www.apache.org/licenses/LICENSE-2.0 31 | * 32 | * Unless required by applicable law or agreed to in writing, software 33 | * distributed under the License is distributed on an "AS IS" BASIS, 34 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 35 | * See the License for the specific language governing permissions and 36 | * limitations under the License. 37 | */ 38 | package org.zeroturnaround.exec.listener; 39 | 40 | import java.util.Enumeration; 41 | import java.util.Vector; 42 | 43 | import org.slf4j.Logger; 44 | import org.slf4j.LoggerFactory; 45 | 46 | /** 47 | * Destroys all registered Processes when the VM exits. 48 | *

49 | * This class is copied from Commons Exec. 50 | */ 51 | public class ShutdownHookProcessDestroyer implements ProcessDestroyer, Runnable { 52 | 53 | private static final Logger log = LoggerFactory.getLogger(ShutdownHookProcessDestroyer.class); 54 | 55 | /** 56 | * Singleton instance of the {@link ShutdownHookProcessDestroyer}. 57 | */ 58 | public static final ProcessDestroyer INSTANCE = new ShutdownHookProcessDestroyer(); 59 | 60 | /** the list of currently running processes */ 61 | private final Vector processes = new Vector(); 62 | 63 | /** The thread registered at the JVM to execute the shutdown handler */ 64 | private ProcessDestroyerImpl destroyProcessThread = null; 65 | 66 | /** Whether or not this ProcessDestroyer has been registered as a shutdown hook */ 67 | private boolean added = false; 68 | 69 | /** Whether the shut down hook routine was already run **/ 70 | private volatile boolean shutDownHookExecuted = false; 71 | 72 | /** 73 | * Whether or not this ProcessDestroyer is currently running as shutdown hook 74 | */ 75 | private volatile boolean running = false; 76 | 77 | private class ProcessDestroyerImpl extends Thread { 78 | 79 | private boolean shouldDestroy = true; 80 | 81 | public ProcessDestroyerImpl() { 82 | super("ProcessDestroyer Shutdown Hook"); 83 | } 84 | 85 | public void run() { 86 | if (shouldDestroy) { 87 | ShutdownHookProcessDestroyer.this.run(); 88 | } 89 | } 90 | 91 | public void setShouldDestroy(final boolean shouldDestroy) { 92 | this.shouldDestroy = shouldDestroy; 93 | } 94 | } 95 | 96 | /** 97 | * Constructs a ProcessDestroyer and obtains 98 | * Runtime.addShutdownHook() and 99 | * Runtime.removeShutdownHook() through reflection. The 100 | * ProcessDestroyer manages a list of processes to be destroyed when the VM 101 | * exits. If a process is added when the list is empty, this 102 | * ProcessDestroyer is registered as a shutdown hook. If 103 | * removing a process results in an empty list, the 104 | * ProcessDestroyer is removed as a shutdown hook. 105 | */ 106 | public ShutdownHookProcessDestroyer() { 107 | } 108 | 109 | /** 110 | * Registers this ProcessDestroyer as a shutdown hook, uses 111 | * reflection to ensure pre-JDK 1.3 compatibility. 112 | */ 113 | private void addShutdownHook() { 114 | if (!running) { 115 | destroyProcessThread = new ProcessDestroyerImpl(); 116 | Runtime.getRuntime().addShutdownHook(destroyProcessThread); 117 | added = true; 118 | } 119 | } 120 | 121 | /** 122 | * Removes this ProcessDestroyer as a shutdown hook, uses 123 | * reflection to ensure pre-JDK 1.3 compatibility 124 | */ 125 | private void removeShutdownHook() { 126 | if (added && !running) { 127 | boolean removed = Runtime.getRuntime().removeShutdownHook( 128 | destroyProcessThread); 129 | if (!removed) { 130 | log.error("Could not remove shutdown hook"); 131 | } 132 | /* 133 | * start the hook thread, a unstarted thread may not be eligible for 134 | * garbage collection Cf.: http://developer.java.sun.com/developer/ 135 | * bugParade/bugs/4533087.html 136 | */ 137 | 138 | destroyProcessThread.setShouldDestroy(false); 139 | destroyProcessThread.start(); 140 | // this should return quickly, since it basically is a NO-OP. 141 | try { 142 | destroyProcessThread.join(20000); 143 | } 144 | catch (InterruptedException ie) { 145 | // the thread didn't die in time 146 | // it should not kill any processes unexpectedly 147 | } 148 | destroyProcessThread = null; 149 | added = false; 150 | } 151 | } 152 | 153 | /** 154 | * Returns whether or not the ProcessDestroyer is registered as as shutdown 155 | * hook 156 | * 157 | * @return true if this is currently added as shutdown hook 158 | */ 159 | public boolean isAddedAsShutdownHook() { 160 | return added; 161 | } 162 | 163 | /** 164 | * Returns true if the specified Process was 165 | * successfully added to the list of processes to destroy upon VM exit. 166 | * 167 | * @param process 168 | * the process to add 169 | * @return true if the specified Process was 170 | * successfully added 171 | */ 172 | public boolean add(final Process process) { 173 | synchronized (processes) { 174 | // if this list is empty, register the shutdown hook 175 | if (processes.size() == 0) { 176 | try { 177 | if(shutDownHookExecuted) { 178 | throw new IllegalStateException(); 179 | } 180 | addShutdownHook(); 181 | } 182 | // kill the process now if the JVM is currently shutting down 183 | catch (IllegalStateException e) { 184 | destroy(process); 185 | } 186 | } 187 | processes.addElement(process); 188 | return processes.contains(process); 189 | } 190 | } 191 | 192 | /** 193 | * Returns true if the specified Process was 194 | * successfully removed from the list of processes to destroy upon VM exit. 195 | * 196 | * @param process 197 | * the process to remove 198 | * @return true if the specified Process was 199 | * successfully removed 200 | */ 201 | public boolean remove(final Process process) { 202 | synchronized (processes) { 203 | boolean processRemoved = processes.removeElement(process); 204 | if (processRemoved && processes.size() == 0) { 205 | try { 206 | removeShutdownHook(); 207 | } catch (IllegalStateException e) { 208 | /* if the JVM is shutting down, the hook cannot be removed */ 209 | shutDownHookExecuted = true; 210 | } 211 | } 212 | return processRemoved; 213 | } 214 | } 215 | 216 | /** 217 | * Returns the number of registered processes. 218 | * 219 | * @return the number of register process 220 | */ 221 | public int size() { 222 | return processes.size(); 223 | } 224 | 225 | /** 226 | * Invoked by the VM when it is exiting. 227 | */ 228 | public void run() { 229 | /* check if running the routine is still necessary */ 230 | if(shutDownHookExecuted) { 231 | return; 232 | } 233 | synchronized (processes) { 234 | running = true; 235 | Enumeration e = processes.elements(); 236 | while (e.hasMoreElements()) { 237 | destroy((Process) e.nextElement()); 238 | } 239 | processes.clear(); 240 | shutDownHookExecuted = true; 241 | } 242 | } 243 | 244 | private void destroy(Process process) { 245 | try { 246 | process.destroy(); 247 | } 248 | catch (Throwable t) { 249 | log.error("Unable to terminate process during process shutdown"); 250 | } 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/stop/DestroyProcessStopper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.stop; 19 | 20 | /** 21 | * Default {@link ProcessStopper} implementation that just invokes {@link Process#destroy()}. 22 | */ 23 | public class DestroyProcessStopper implements ProcessStopper { 24 | 25 | /** 26 | * Singleton instance of the {@link DestroyProcessStopper}. 27 | */ 28 | public static final ProcessStopper INSTANCE = new DestroyProcessStopper(); 29 | 30 | public void stop(Process process) { 31 | process.destroy(); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/stop/NopProcessStopper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.stop; 19 | 20 | /** 21 | * {@link ProcessStopper} implementation that does nothing - it keeps the process running. 22 | */ 23 | public class NopProcessStopper implements ProcessStopper { 24 | 25 | /** 26 | * Singleton instance of the {@link NopProcessStopper}. 27 | */ 28 | public static final ProcessStopper INSTANCE = new NopProcessStopper(); 29 | 30 | public void stop(Process process) { 31 | // don't stop the process 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/stop/ProcessStopper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.stop; 19 | 20 | import java.util.concurrent.Future; 21 | 22 | /** 23 | * Abstraction for stopping sub processes. 24 | *

25 | * This is used in case a process runs too long (timeout is reached) or it's cancelled via {@link Future#cancel(boolean)}. 26 | */ 27 | public interface ProcessStopper { 28 | 29 | /** 30 | * Stops a given sub process. 31 | * It does not wait for the process to actually stop and it has no guarantee that the process terminates. 32 | * 33 | * @param process sub process being stopped (not null). 34 | */ 35 | void stop(Process process); 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/stream/CallerLoggerUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.stream; 19 | 20 | /** 21 | * Constructs name for the caller logger. 22 | * 23 | * @author Rein Raudjärv 24 | */ 25 | public abstract class CallerLoggerUtil { 26 | 27 | /** 28 | * Returns full name for the caller class' logger. 29 | * 30 | * @param name name of the logger. In case of full name (it contains dots) same value is just returned. 31 | * In case of short names (no dots) the given name is prefixed by caller's class name and a dot. 32 | * In case of null the caller's class name is just returned. 33 | * @return full name for the caller class' logger. 34 | */ 35 | public static String getName(String name) { 36 | return getName(name, 1); 37 | } 38 | 39 | /** 40 | * Returns full name for the caller class' logger. 41 | * 42 | * @param name name of the logger. In case of full name (it contains dots) same value is just returned. 43 | * In case of short names (no dots) the given name is prefixed by caller's class name and a dot. 44 | * In case of null the caller's class name is just returned. 45 | * @param level no of call stack levels to get the caller (0 means the caller of this method). 46 | * @return full name for the caller class' logger. 47 | */ 48 | public static String getName(String name, int level) { 49 | level++; 50 | String fullName; 51 | if (name == null) 52 | fullName = getCallerClassName(level); 53 | else if (name.contains(".")) 54 | fullName = name; 55 | else 56 | fullName = getCallerClassName(level) + "." + name; 57 | return fullName; 58 | } 59 | 60 | /** 61 | * @return caller class name of the given level. 62 | */ 63 | private static String getCallerClassName(int level) { 64 | return Thread.currentThread().getStackTrace()[level + 2].getClassName(); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/stream/ExecuteStreamHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * NOTICE: This file originates from the Apache Commons Exec package. 19 | * It has been modified to fit our needs. 20 | * 21 | * The following is the original header of the file in Apache Commons Exec: 22 | * 23 | * Licensed to the Apache Software Foundation (ASF) under one or more 24 | * contributor license agreements. See the NOTICE file distributed with 25 | * this work for additional information regarding copyright ownership. 26 | * The ASF licenses this file to You under the Apache License, Version 2.0 27 | * (the "License"); you may not use this file except in compliance with 28 | * the License. You may obtain a copy of the License at 29 | * 30 | * http://www.apache.org/licenses/LICENSE-2.0 31 | * 32 | * Unless required by applicable law or agreed to in writing, software 33 | * distributed under the License is distributed on an "AS IS" BASIS, 34 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 35 | * See the License for the specific language governing permissions and 36 | * limitations under the License. 37 | */ 38 | package org.zeroturnaround.exec.stream; 39 | 40 | import java.io.IOException; 41 | import java.io.InputStream; 42 | import java.io.OutputStream; 43 | 44 | /** 45 | * Used by Execute to handle input and output stream of 46 | * subprocesses. 47 | */ 48 | public interface ExecuteStreamHandler { 49 | 50 | /** 51 | * Install a handler for the input stream of the subprocess. 52 | * 53 | * @param os 54 | * output stream to write to the standard input stream of the 55 | * subprocess 56 | * @throws IOException throws a IO exception in case of IO issues of the underlying stream 57 | */ 58 | void setProcessInputStream(OutputStream os) throws IOException; 59 | 60 | /** 61 | * Install a handler for the error stream of the subprocess. 62 | * 63 | * @param is 64 | * input stream to read from the error stream from the subprocess 65 | * @throws IOException throws a IO exception in case of IO issues of the underlying stream 66 | */ 67 | void setProcessErrorStream(InputStream is) throws IOException; 68 | 69 | /** 70 | * Install a handler for the output stream of the subprocess. 71 | * 72 | * @param is 73 | * input stream to read from the error stream from the subprocess 74 | * @throws IOException throws a IO exception in case of IO issues of the underlying stream 75 | */ 76 | void setProcessOutputStream(InputStream is) throws IOException; 77 | 78 | /** 79 | * Start handling of the streams. 80 | * 81 | * @throws IOException throws a IO exception in case of IO issues of the underlying stream 82 | */ 83 | void start() throws IOException; 84 | 85 | /** 86 | * Stop handling of the streams - will not be restarted. 87 | * Will wait for pump threads to complete. 88 | */ 89 | void stop(); 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/stream/InputStreamPumper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * NOTICE: This file originates from the Apache Commons Exec package. 19 | * It has been modified to fit our needs. 20 | * 21 | * The following is the original header of the file in Apache Commons Exec: 22 | * 23 | * Licensed to the Apache Software Foundation (ASF) under one or more 24 | * contributor license agreements. See the NOTICE file distributed with 25 | * this work for additional information regarding copyright ownership. 26 | * The ASF licenses this file to You under the Apache License, Version 2.0 27 | * (the "License"); you may not use this file except in compliance with 28 | * the License. You may obtain a copy of the License at 29 | * 30 | * http://www.apache.org/licenses/LICENSE-2.0 31 | * 32 | * Unless required by applicable law or agreed to in writing, software 33 | * distributed under the License is distributed on an "AS IS" BASIS, 34 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 35 | * See the License for the specific language governing permissions and 36 | * limitations under the License. 37 | */ 38 | package org.zeroturnaround.exec.stream; 39 | 40 | import java.io.InputStream; 41 | import java.io.OutputStream; 42 | 43 | import org.slf4j.Logger; 44 | import org.slf4j.LoggerFactory; 45 | 46 | /** 47 | * Copies all data from an System.input stream to an output stream of the executed process. 48 | * 49 | * @author mkleint 50 | */ 51 | public class InputStreamPumper implements Runnable { 52 | 53 | private static final Logger log = LoggerFactory.getLogger(InputStreamPumper.class); 54 | 55 | public static final int SLEEPING_TIME = 100; 56 | 57 | /** the input stream to pump from */ 58 | private final InputStream is; 59 | 60 | /** the output stream to pmp into */ 61 | private final OutputStream os; 62 | 63 | /** flag to stop the stream pumping */ 64 | private volatile boolean stop; 65 | 66 | /** 67 | * Create a new stream pumper. 68 | * 69 | * @param is input stream to read data from 70 | * @param os output stream to write data to. 71 | */ 72 | public InputStreamPumper(final InputStream is, final OutputStream os) { 73 | this.is = is; 74 | this.os = os; 75 | this.stop = false; 76 | } 77 | 78 | /** 79 | * Copies data from the input stream to the output stream. Terminates as 80 | * soon as the input stream is closed or an error occurs. 81 | */ 82 | public void run() { 83 | try { 84 | while (!stop) { 85 | while (is.available() > 0 && !stop) { 86 | os.write(is.read()); 87 | } 88 | os.flush(); 89 | Thread.sleep(SLEEPING_TIME); 90 | } 91 | } 92 | catch (Exception e) { 93 | log.error("Got exception while reading/writing the stream", e); 94 | } 95 | } 96 | 97 | public void stopProcessing() { 98 | stop = true; 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/stream/LineConsumer.java: -------------------------------------------------------------------------------- 1 | package org.zeroturnaround.exec.stream; 2 | 3 | /** 4 | * This is equivalent to {@code java.util.function.Consumer} while staying compatible with 5 | * Java versions earlier than 8. 6 | */ 7 | public interface LineConsumer { 8 | void accept(String line); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/stream/LogOutputStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.stream; 19 | 20 | import java.io.ByteArrayOutputStream; 21 | import java.io.IOException; 22 | import java.io.OutputStream; 23 | import java.io.UnsupportedEncodingException; 24 | 25 | /** 26 | * Base class to connect a logging system to the output and/or 27 | * error stream of then external process. The implementation 28 | * parses the incoming data to construct a line and passes 29 | * the complete line to an user-defined implementation. 30 | */ 31 | public abstract class LogOutputStream extends OutputStream { 32 | 33 | /** Initial buffer size. */ 34 | private static final int INITIAL_SIZE = 132; 35 | 36 | /** Carriage return */ 37 | private static final int CR = 0x0d; 38 | 39 | /** Linefeed */ 40 | private static final int LF = 0x0a; 41 | 42 | /** the internal buffer */ 43 | private final ByteArrayOutputStream buffer = new ByteArrayOutputStream( 44 | INITIAL_SIZE); 45 | 46 | byte lastReceivedByte; 47 | 48 | private String outputCharset; 49 | 50 | public LogOutputStream setOutputCharset(final String outputCharset) { 51 | this.outputCharset = outputCharset; 52 | return this; 53 | } 54 | 55 | /** 56 | * Write the data to the buffer and flush the buffer, if a line separator is 57 | * detected. 58 | * 59 | * @param cc data to log (byte). 60 | * @see java.io.OutputStream#write(int) 61 | */ 62 | public void write(final int cc) throws IOException { 63 | final byte c = (byte) cc; 64 | if ((c == '\n') || (c == '\r')) { 65 | // new line is started in case of 66 | // - CR (regardless of previous character) 67 | // - LF if previous character was not CR and not LF 68 | if (c == '\r' || (c == '\n' && (lastReceivedByte != '\r' && lastReceivedByte != '\n'))) { 69 | processBuffer(); 70 | } 71 | } else { 72 | buffer.write(cc); 73 | } 74 | lastReceivedByte = c; 75 | } 76 | 77 | /** 78 | * Flush this log stream. 79 | * 80 | * @see java.io.OutputStream#flush() 81 | */ 82 | public void flush() { 83 | if (buffer.size() > 0) { 84 | processBuffer(); 85 | } 86 | } 87 | 88 | /** 89 | * Writes all remaining data from the buffer. 90 | * 91 | * @see java.io.OutputStream#close() 92 | */ 93 | public void close() throws IOException { 94 | if (buffer.size() > 0) { 95 | processBuffer(); 96 | } 97 | super.close(); 98 | } 99 | 100 | /** 101 | * Write a block of characters to the output stream 102 | * 103 | * @param b the array containing the data 104 | * @param off the offset into the array where data starts 105 | * @param len the length of block 106 | * @throws java.io.IOException if the data cannot be written into the stream. 107 | * @see java.io.OutputStream#write(byte[], int, int) 108 | */ 109 | public void write(final byte[] b, final int off, final int len) 110 | throws IOException { 111 | // find the line breaks and pass other chars through in blocks 112 | int offset = off; 113 | int blockStartOffset = offset; 114 | int remaining = len; 115 | while (remaining > 0) { 116 | while (remaining > 0 && b[offset] != LF && b[offset] != CR) { 117 | offset++; 118 | remaining--; 119 | } 120 | // either end of buffer or a line separator char 121 | int blockLength = offset - blockStartOffset; 122 | if (blockLength > 0) { 123 | buffer.write(b, blockStartOffset, blockLength); 124 | lastReceivedByte = 0; 125 | } 126 | while (remaining > 0 && (b[offset] == LF || b[offset] == CR)) { 127 | write(b[offset]); 128 | offset++; 129 | remaining--; 130 | } 131 | blockStartOffset = offset; 132 | } 133 | } 134 | 135 | /** 136 | * Converts the buffer to a string and sends it to processLine. 137 | */ 138 | protected void processBuffer() { 139 | final String line; 140 | if (outputCharset == null) { 141 | line = buffer.toString(); 142 | } else { 143 | try { 144 | line = buffer.toString(outputCharset); 145 | } catch (UnsupportedEncodingException e) { 146 | throw new IllegalArgumentException(e); 147 | } 148 | } 149 | processLine(line); 150 | buffer.reset(); 151 | } 152 | 153 | /** 154 | * Logs a line to the log system of the user. 155 | * 156 | * @param line 157 | * the line to log. 158 | */ 159 | protected abstract void processLine(String line); 160 | 161 | /** 162 | * Factory method to create a LogOutputStream that passes each line to the specified consumer. 163 | *

Mostly useful with Java 8+, so the consumer can be passed as a lambda expression.

164 | * 165 | * @param consumer the consumer to consume the log lines 166 | * @return the created LogOutputStream, passing each line to the specified consumer. 167 | */ 168 | public static LogOutputStream create(final LineConsumer consumer) { 169 | if (consumer == null) { 170 | throw new IllegalArgumentException("Line consumer must be provided."); 171 | } 172 | return new LogOutputStream() { 173 | @Override 174 | protected void processLine(String line) { 175 | consumer.accept(line); 176 | } 177 | }; 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/stream/NullOutputStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 Ketan Padegaonkar 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.zeroturnaround.exec.stream; 18 | 19 | import java.io.IOException; 20 | import java.io.OutputStream; 21 | 22 | /** 23 | * An OutputStream which ignores everything written to it. 24 | */ 25 | public class NullOutputStream extends OutputStream { 26 | 27 | /** 28 | * A singleton. 29 | */ 30 | public static final NullOutputStream NULL_OUTPUT_STREAM = new NullOutputStream(); 31 | 32 | public void write(byte[] b, int off, int len) { 33 | // discard! 34 | } 35 | 36 | public void write(int b) { 37 | // discard! 38 | } 39 | 40 | public void write(byte[] b) throws IOException { 41 | // discard! 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/stream/StreamPumper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * NOTICE: This file originates from the Apache Commons Exec package. 19 | * It has been modified to fit our needs. 20 | * 21 | * The following is the original header of the file in Apache Commons Exec: 22 | * 23 | * Licensed to the Apache Software Foundation (ASF) under one or more 24 | * contributor license agreements. See the NOTICE file distributed with 25 | * this work for additional information regarding copyright ownership. 26 | * The ASF licenses this file to You under the Apache License, Version 2.0 27 | * (the "License"); you may not use this file except in compliance with 28 | * the License. You may obtain a copy of the License at 29 | * 30 | * http://www.apache.org/licenses/LICENSE-2.0 31 | * 32 | * Unless required by applicable law or agreed to in writing, software 33 | * distributed under the License is distributed on an "AS IS" BASIS, 34 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 35 | * See the License for the specific language governing permissions and 36 | * limitations under the License. 37 | */ 38 | package org.zeroturnaround.exec.stream; 39 | 40 | import java.io.IOException; 41 | import java.io.InputStream; 42 | import java.io.OutputStream; 43 | 44 | import org.slf4j.Logger; 45 | import org.slf4j.LoggerFactory; 46 | 47 | /** 48 | * Copies all data from an input stream to an output stream. 49 | */ 50 | public class StreamPumper implements Runnable { 51 | 52 | private static final Logger log = LoggerFactory.getLogger(StreamPumper.class); 53 | 54 | /** the default size of the internal buffer for copying the streams */ 55 | private static final int DEFAULT_SIZE = 1024; 56 | 57 | /** the input stream to pump from */ 58 | private final InputStream is; 59 | 60 | /** the output stream to pmp into */ 61 | private final OutputStream os; 62 | 63 | /** the size of the internal buffer for copying the streams */ 64 | private final int size; 65 | 66 | /** was the end of the stream reached */ 67 | private boolean finished; 68 | 69 | /** close the output stream when exhausted */ 70 | private final boolean closeWhenExhausted; 71 | 72 | /** flush the output stream after each write */ 73 | private final boolean flushImmediately; 74 | 75 | /** 76 | * Create a new stream pumper. 77 | * 78 | * @param is input stream to read data from 79 | * @param os output stream to write data to. 80 | * @param closeWhenExhausted if true, the output stream will be closed when the input is exhausted. 81 | * @param flushImmediately flush the output stream whenever data was written to it 82 | */ 83 | public StreamPumper(final InputStream is, final OutputStream os, 84 | final boolean closeWhenExhausted, boolean flushImmediately) { 85 | this.is = is; 86 | this.os = os; 87 | this.size = DEFAULT_SIZE; 88 | this.closeWhenExhausted = closeWhenExhausted; 89 | this.flushImmediately = flushImmediately; 90 | } 91 | 92 | /** 93 | * Create a new stream pumper. 94 | * 95 | * @param is input stream to read data from 96 | * @param os output stream to write data to. 97 | * @param closeWhenExhausted if true, the output stream will be closed when the input is exhausted. 98 | * @param size the size of the internal buffer for copying the streams 99 | * @param flushImmediately flush the output stream whenever data was written to it 100 | */ 101 | public StreamPumper(final InputStream is, final OutputStream os, 102 | final boolean closeWhenExhausted, final int size, boolean flushImmediately) { 103 | this.is = is; 104 | this.os = os; 105 | this.size = (size > 0 ? size : DEFAULT_SIZE); 106 | this.closeWhenExhausted = closeWhenExhausted; 107 | this.flushImmediately = flushImmediately; 108 | } 109 | 110 | /** 111 | * Create a new stream pumper. 112 | * 113 | * @param is input stream to read data from 114 | * @param os output stream to write data to. 115 | * @param closeWhenExhausted if true, the output stream will be closed when the input is exhausted. 116 | */ 117 | public StreamPumper(final InputStream is, final OutputStream os, 118 | final boolean closeWhenExhausted) { 119 | this.is = is; 120 | this.os = os; 121 | this.size = DEFAULT_SIZE; 122 | this.closeWhenExhausted = closeWhenExhausted; 123 | this.flushImmediately = false; 124 | } 125 | 126 | /** 127 | * Create a new stream pumper. 128 | * 129 | * @param is input stream to read data from 130 | * @param os output stream to write data to. 131 | * @param closeWhenExhausted if true, the output stream will be closed when the input is exhausted. 132 | * @param size the size of the internal buffer for copying the streams 133 | */ 134 | public StreamPumper(final InputStream is, final OutputStream os, 135 | final boolean closeWhenExhausted, final int size) { 136 | this.is = is; 137 | this.os = os; 138 | this.size = (size > 0 ? size : DEFAULT_SIZE); 139 | this.closeWhenExhausted = closeWhenExhausted; 140 | this.flushImmediately = false; 141 | } 142 | 143 | /** 144 | * Create a new stream pumper. 145 | * 146 | * @param is input stream to read data from 147 | * @param os output stream to write data to. 148 | */ 149 | public StreamPumper(final InputStream is, final OutputStream os) { 150 | this(is, os, false); 151 | } 152 | 153 | /** 154 | * Copies data from the input stream to the output stream. Terminates as 155 | * soon as the input stream is closed or an error occurs. 156 | */ 157 | public void run() { 158 | log.trace("{} started.", this); 159 | synchronized (this) { 160 | // Just in case this object is reused in the future 161 | finished = false; 162 | } 163 | 164 | final byte[] buf = new byte[this.size]; 165 | 166 | int length; 167 | try { 168 | while ((length = is.read(buf)) > 0) { 169 | os.write(buf, 0, length); 170 | if(flushImmediately) { 171 | os.flush(); 172 | } 173 | } 174 | } catch (Exception e) { 175 | // nothing to do - happens quite often with watchdog 176 | } finally { 177 | log.trace("{} finished.", this); 178 | if (closeWhenExhausted) { 179 | try { 180 | os.close(); 181 | } catch (IOException e) { 182 | log.error("Got exception while closing exhausted output stream", e); 183 | } 184 | } 185 | synchronized (this) { 186 | finished = true; 187 | notifyAll(); 188 | } 189 | } 190 | } 191 | 192 | /** 193 | * Tells whether the end of the stream has been reached. 194 | * 195 | * @return true is the stream has been exhausted. 196 | */ 197 | public synchronized boolean isFinished() { 198 | return finished; 199 | } 200 | 201 | /** 202 | * This method blocks until the stream pumper finishes. 203 | * 204 | * @see #isFinished() 205 | * @throws InterruptedException throws when the waiting is interrupted 206 | */ 207 | public synchronized void waitFor() throws InterruptedException { 208 | while (!isFinished()) { 209 | wait(); 210 | } 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/stream/TeeOutputStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 Ketan Padegaonkar 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.zeroturnaround.exec.stream; 18 | 19 | import java.io.IOException; 20 | import java.io.OutputStream; 21 | 22 | /** 23 | * Splits an OutputStream into two. Named after the unix 'tee' 24 | * command. It allows a stream to be branched off so there 25 | * are now two streams. 26 | */ 27 | public class TeeOutputStream extends OutputStream { 28 | private final OutputStream left; 29 | private final OutputStream right; 30 | 31 | public TeeOutputStream(OutputStream left, OutputStream right) { 32 | this.left = left; 33 | this.right = right; 34 | } 35 | 36 | /** 37 | * Write a byte array to both output streams. 38 | * 39 | * @param b the data. 40 | * @param off the start offset in the data. 41 | * @param len the number of bytes to write. 42 | * @throws IOException on error. 43 | */ 44 | public void write(byte[] b, int off, int len) throws IOException { 45 | left.write(b, off, len); 46 | right.write(b, off, len); 47 | } 48 | 49 | 50 | /** 51 | * Write a byte to both output streams. 52 | * 53 | * @param b the byte to write. 54 | * @throws IOException on error. 55 | */ 56 | public void write(int b) throws IOException { 57 | left.write(b); 58 | right.write(b); 59 | } 60 | 61 | 62 | /** 63 | * Write a byte array to both output streams. 64 | * 65 | * @param b an array of bytes. 66 | * @throws IOException on error. 67 | */ 68 | public void write(byte[] b) throws IOException { 69 | left.write(b); 70 | right.write(b); 71 | } 72 | 73 | /** 74 | * Closes both output streams 75 | * 76 | * @throws IOException on error. 77 | */ 78 | @Override 79 | public void close() throws IOException { 80 | try { 81 | left.close(); 82 | } finally { 83 | right.close(); 84 | } 85 | } 86 | 87 | 88 | /** 89 | * Flush both output streams. 90 | * 91 | * @throws IOException on error 92 | */ 93 | @Override 94 | public void flush() throws IOException { 95 | left.flush(); 96 | right.flush(); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/stream/slf4j/Level.java: -------------------------------------------------------------------------------- 1 | package org.zeroturnaround.exec.stream.slf4j; 2 | 3 | /** 4 | * Slf4j logging level. 5 | */ 6 | public enum Level { 7 | 8 | TRACE, 9 | DEBUG, 10 | INFO, 11 | WARN, 12 | ERROR 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/stream/slf4j/Slf4jDebugOutputStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.stream.slf4j; 19 | 20 | import org.slf4j.Logger; 21 | 22 | /** 23 | * Output stream that writes debug level messages to a given {@link Logger}. 24 | * 25 | * @author Rein Raudjärv 26 | */ 27 | public class Slf4jDebugOutputStream extends Slf4jOutputStream { 28 | 29 | public Slf4jDebugOutputStream(Logger logger) { 30 | super(logger); 31 | } 32 | 33 | @Override 34 | protected void processLine(String line) { 35 | log.debug(line); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/stream/slf4j/Slf4jErrorOutputStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.stream.slf4j; 19 | 20 | import org.slf4j.Logger; 21 | 22 | /** 23 | * Output stream that writes error level messages to a given {@link Logger}. 24 | * 25 | * @author Rein Raudjärv 26 | */ 27 | public class Slf4jErrorOutputStream extends Slf4jOutputStream { 28 | 29 | public Slf4jErrorOutputStream(Logger logger) { 30 | super(logger); 31 | } 32 | 33 | @Override 34 | protected void processLine(String line) { 35 | log.error(line); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/stream/slf4j/Slf4jInfoOutputStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.stream.slf4j; 19 | 20 | import org.slf4j.Logger; 21 | 22 | /** 23 | * Output stream that writes info level messages to a given {@link Logger}. 24 | * 25 | * @author Rein Raudjärv 26 | */ 27 | public class Slf4jInfoOutputStream extends Slf4jOutputStream { 28 | 29 | public Slf4jInfoOutputStream(Logger logger) { 30 | super(logger); 31 | } 32 | 33 | @Override 34 | protected void processLine(String line) { 35 | log.info(line); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/stream/slf4j/Slf4jOutputStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.stream.slf4j; 19 | 20 | import org.slf4j.Logger; 21 | import org.zeroturnaround.exec.stream.LogOutputStream; 22 | 23 | /** 24 | * Output stream that writes to a given {@link Logger}. 25 | * 26 | * @author Rein Raudjärv 27 | */ 28 | public abstract class Slf4jOutputStream extends LogOutputStream { 29 | 30 | protected final Logger log; 31 | 32 | public Slf4jOutputStream(Logger logger) { 33 | this.log = logger; 34 | } 35 | 36 | public Logger getLogger() { 37 | return log; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/stream/slf4j/Slf4jStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.stream.slf4j; 19 | 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | import org.zeroturnaround.exec.stream.CallerLoggerUtil; 23 | 24 | /** 25 | * Creates output streams that write to {@link Logger}s. 26 | * 27 | * @author Rein Raudjärv 28 | */ 29 | public class Slf4jStream { 30 | 31 | private final Logger log; 32 | 33 | private Slf4jStream(Logger log) { 34 | this.log = log; 35 | } 36 | 37 | /** 38 | * @param log logger which an output stream redirects to. 39 | * @return Slf4jStream with the given logger. 40 | */ 41 | public static Slf4jStream of(Logger log) { 42 | return new Slf4jStream(log); 43 | } 44 | 45 | /** 46 | * @param klass class which is used to get the logger's name. 47 | * @return Slf4jStream with a logger named after the given class. 48 | */ 49 | public static Slf4jStream of(Class klass) { 50 | return of(LoggerFactory.getLogger(klass)); 51 | } 52 | 53 | /** 54 | * Constructs a logger from a class name and an additional name, 55 | * appended to the end. So the final logger name will be: 56 | *
klass.getName() + "." + name
57 | * 58 | * @param klass class which is used to get the logger's name. 59 | * @param name logger's name, appended to the class name. 60 | * @return Slf4jStream with a logger named after the given class. 61 | * 62 | * @since 1.8 63 | */ 64 | public static Slf4jStream of(Class klass, String name) { 65 | if (name == null) { 66 | return of(klass); 67 | } else { 68 | return of(LoggerFactory.getLogger(klass.getName() + "." + name)); 69 | } 70 | } 71 | 72 | /** 73 | * @param name logger's name (full or short). 74 | * In case of short name (no dots) the given name is prefixed by caller's class name and a dot. 75 | * @return Slf4jStream with the given logger. 76 | */ 77 | public static Slf4jStream of(String name) { 78 | return of(LoggerFactory.getLogger(CallerLoggerUtil.getName(name, 1))); 79 | } 80 | 81 | /** 82 | * @return Slf4jStream with the logger of caller of this method. 83 | */ 84 | public static Slf4jStream ofCaller() { 85 | return of(LoggerFactory.getLogger(CallerLoggerUtil.getName(null, 1))); 86 | } 87 | 88 | /** 89 | * @param level the desired logging level 90 | * @return output stream that writes with a given level. 91 | */ 92 | public Slf4jOutputStream as(Level level) { 93 | switch (level) { 94 | case TRACE: return asTrace(); 95 | case DEBUG: return asDebug(); 96 | case INFO: return asInfo(); 97 | case WARN: return asWarn(); 98 | case ERROR: return asError(); 99 | } 100 | throw new IllegalArgumentException("Invalid level " + level); 101 | } 102 | 103 | /** 104 | * @return output stream that writes trace level. 105 | */ 106 | public Slf4jOutputStream asTrace() { 107 | return new Slf4jTraceOutputStream(log); 108 | } 109 | 110 | /** 111 | * @return output stream that writes debug level. 112 | */ 113 | public Slf4jOutputStream asDebug() { 114 | return new Slf4jDebugOutputStream(log); 115 | } 116 | 117 | /** 118 | * @return output stream that writes info level. 119 | */ 120 | public Slf4jOutputStream asInfo() { 121 | return new Slf4jInfoOutputStream(log); 122 | } 123 | 124 | /** 125 | * @return output stream that writes warn level. 126 | */ 127 | public Slf4jOutputStream asWarn() { 128 | return new Slf4jWarnOutputStream(log); 129 | } 130 | 131 | /** 132 | * @return output stream that writes error level. 133 | */ 134 | public Slf4jOutputStream asError() { 135 | return new Slf4jErrorOutputStream(log); 136 | } 137 | 138 | } 139 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/stream/slf4j/Slf4jTraceOutputStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.stream.slf4j; 19 | 20 | import org.slf4j.Logger; 21 | 22 | /** 23 | * Output stream that writes trace level messages to a given {@link Logger}. 24 | * 25 | * @author Rein Raudjärv 26 | */ 27 | public class Slf4jTraceOutputStream extends Slf4jOutputStream { 28 | 29 | public Slf4jTraceOutputStream(Logger logger) { 30 | super(logger); 31 | } 32 | 33 | @Override 34 | protected void processLine(String line) { 35 | log.trace(line); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/org/zeroturnaround/exec/stream/slf4j/Slf4jWarnOutputStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.stream.slf4j; 19 | 20 | import org.slf4j.Logger; 21 | 22 | /** 23 | * Output stream that writes warn level messages to a given {@link Logger}. 24 | * 25 | * @author Rein Raudjärv 26 | */ 27 | public class Slf4jWarnOutputStream extends Slf4jOutputStream { 28 | 29 | public Slf4jWarnOutputStream(Logger logger) { 30 | super(logger); 31 | } 32 | 33 | @Override 34 | protected void processLine(String line) { 35 | log.warn(line); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/org/zeroturnaround/exec/ProcessOutputTest.java: -------------------------------------------------------------------------------- 1 | package org.zeroturnaround.exec; 2 | 3 | import java.util.Arrays; 4 | 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | 8 | public class ProcessOutputTest { 9 | 10 | @Test(expected = NullPointerException.class) 11 | public void testNull() { 12 | ProcessOutput.getLinesFrom(null); 13 | } 14 | 15 | @Test 16 | public void testSimple() { 17 | Assert.assertEquals(Arrays.asList("foo"), ProcessOutput.getLinesFrom("foo")); 18 | } 19 | 20 | @Test 21 | public void testNewLine() { 22 | Assert.assertEquals(Arrays.asList("foo", "bar"), ProcessOutput.getLinesFrom("foo\nbar")); 23 | } 24 | 25 | @Test 26 | public void testNewLineWithMultipleLines() { 27 | Assert.assertEquals(Arrays.asList("foo1", "bar1", "foo2", "bar2"), ProcessOutput.getLinesFrom("foo1\nbar1\nfoo2\nbar2")); 28 | } 29 | 30 | @Test 31 | public void testCarriageReturn() { 32 | Assert.assertEquals(Arrays.asList("foo", "bar"), ProcessOutput.getLinesFrom("foo\rbar")); 33 | } 34 | 35 | @Test 36 | public void testCarriageReturnWithMultipleLines() { 37 | Assert.assertEquals(Arrays.asList("foo1", "bar1", "foo2", "bar2"), ProcessOutput.getLinesFrom("foo1\rbar1\rfoo2\rbar2")); 38 | } 39 | 40 | @Test 41 | public void testCarriageReturnAndNewLine() { 42 | Assert.assertEquals(Arrays.asList("foo", "bar"), ProcessOutput.getLinesFrom("foo\r\nbar")); 43 | } 44 | 45 | @Test 46 | public void testCarriageReturnAndNewLineWithMultipleLines() { 47 | Assert.assertEquals(Arrays.asList("foo1", "bar1", "foo2", "bar2"), ProcessOutput.getLinesFrom("foo1\r\nbar1\r\nfoo2\r\nbar2")); 48 | } 49 | 50 | @Test 51 | public void testTwoNewLines() { 52 | Assert.assertEquals(Arrays.asList("foo", "bar"), ProcessOutput.getLinesFrom("foo\n\nbar")); 53 | } 54 | 55 | @Test 56 | public void testNewLineAtTheEnd() { 57 | Assert.assertEquals(Arrays.asList("foo"), ProcessOutput.getLinesFrom("foo\n")); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/test/java/org/zeroturnaround/exec/ReadmeExamples.java: -------------------------------------------------------------------------------- 1 | package org.zeroturnaround.exec; 2 | 3 | import java.io.OutputStream; 4 | import java.util.Map; 5 | import java.util.concurrent.Future; 6 | import java.util.concurrent.TimeUnit; 7 | import java.util.concurrent.TimeoutException; 8 | 9 | import org.slf4j.LoggerFactory; 10 | import org.zeroturnaround.exec.stream.LogOutputStream; 11 | import org.zeroturnaround.exec.stream.slf4j.Slf4jStream; 12 | 13 | /** 14 | * Examples of the readme. 15 | */ 16 | class ReadmeExamples { 17 | 18 | void justExecute() throws Exception { 19 | new ProcessExecutor().command("java", "-version").execute(); 20 | } 21 | 22 | int getExitCode() throws Exception { 23 | int exit = new ProcessExecutor().command("java", "-version") 24 | .execute().getExitValue(); 25 | return exit; 26 | } 27 | 28 | String getOutput() throws Exception { 29 | String output = new ProcessExecutor().command("java", "-version") 30 | .readOutput(true).execute() 31 | .outputUTF8(); 32 | return output; 33 | } 34 | 35 | void pumpOutputToLogger() throws Exception { 36 | new ProcessExecutor().command("java", "-version") 37 | .redirectOutput(Slf4jStream.of(LoggerFactory.getLogger(getClass().getName() + ".MyProcess")).asInfo()).execute(); 38 | } 39 | 40 | void pumpOutputToLoggerShorter() throws Exception { 41 | new ProcessExecutor().command("java", "-version") 42 | .redirectOutput(Slf4jStream.of("MyProcess").asInfo()).execute(); 43 | } 44 | 45 | void pumpOutputToLoggerOfCaller() throws Exception { 46 | new ProcessExecutor().command("java", "-version") 47 | .redirectOutput(Slf4jStream.ofCaller().asInfo()).execute(); 48 | } 49 | 50 | String pumpOutputToLoggerAndGetOutput() throws Exception { 51 | String output = new ProcessExecutor().command("java", "-version") 52 | .redirectOutput(Slf4jStream.of(getClass()).asInfo()) 53 | .readOutput(true).execute().outputUTF8(); 54 | return output; 55 | } 56 | 57 | String pumpErrorToLoggerAndGetOutput() throws Exception { 58 | String output = new ProcessExecutor().command("java", "-version") 59 | .redirectError(Slf4jStream.of(getClass()).asInfo()) 60 | .readOutput(true).execute() 61 | .outputUTF8(); 62 | return output; 63 | } 64 | 65 | void executeWithTimeout() throws Exception { 66 | try { 67 | new ProcessExecutor().command("java", "-version") 68 | .timeout(60, TimeUnit.SECONDS).execute(); 69 | } 70 | catch (TimeoutException e) { 71 | // process is automatically destroyed 72 | } 73 | } 74 | 75 | void pumpOutputToStream(OutputStream out) throws Exception { 76 | new ProcessExecutor().command("java", "-version") 77 | .redirectOutput(out).execute(); 78 | } 79 | 80 | void pumpOutputToLogStream(OutputStream out) throws Exception { 81 | new ProcessExecutor().command("java", "-version") 82 | .redirectOutput(new LogOutputStream() { 83 | @Override 84 | protected void processLine(String line) { 85 | // ... 86 | } 87 | }) 88 | .execute(); 89 | } 90 | 91 | void destroyProcessOnJvmExit() throws Exception { 92 | new ProcessExecutor().command("java", "-version").destroyOnExit().execute(); 93 | } 94 | 95 | void executeWithEnvironmentVariable() throws Exception { 96 | new ProcessExecutor().command("java", "-version") 97 | .environment("foo", "bar") 98 | .execute(); 99 | } 100 | 101 | void executeWithEnvironment(Map env) throws Exception { 102 | new ProcessExecutor().command("java", "-version") 103 | .environment(env) 104 | .execute(); 105 | } 106 | 107 | void checkExitCode() throws Exception { 108 | try { 109 | new ProcessExecutor().command("java", "-version") 110 | .exitValues(3).execute(); 111 | } 112 | catch (InvalidExitValueException e) { 113 | System.out.println("Process exited with " + e.getExitValue()); 114 | } 115 | } 116 | 117 | void checkExitCodeAndGetOutput() throws Exception { 118 | String output; 119 | boolean success = false; 120 | try { 121 | output = new ProcessExecutor().command("java", "-version") 122 | .readOutput(true).exitValues(3) 123 | .execute().outputUTF8(); 124 | success = true; 125 | } 126 | catch (InvalidExitValueException e) { 127 | System.out.println("Process exited with " + e.getExitValue()); 128 | output = e.getResult().outputUTF8(); 129 | } 130 | } 131 | 132 | void startInBackground() throws Exception { 133 | Future future = new ProcessExecutor() 134 | .command("java", "-version") 135 | .start().getFuture(); 136 | //do some stuff 137 | future.get(60, TimeUnit.SECONDS); 138 | } 139 | 140 | String startInBackgroundAndGetOutput() throws Exception { 141 | Future future = new ProcessExecutor() 142 | .command("java", "-version") 143 | .readOutput(true) 144 | .start().getFuture(); 145 | //do some stuff 146 | String output = future.get(60, TimeUnit.SECONDS).outputUTF8(); 147 | return output; 148 | } 149 | 150 | } 151 | -------------------------------------------------------------------------------- /src/test/java/org/zeroturnaround/exec/stream/TeeOutputStreamTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019 Ketan Padegaonkar 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.zeroturnaround.exec.stream; 18 | 19 | import org.junit.Assert; 20 | import org.junit.Test; 21 | import org.zeroturnaround.exec.test.RememberCloseOutputStream; 22 | 23 | import java.io.ByteArrayOutputStream; 24 | import java.io.IOException; 25 | import java.io.OutputStream; 26 | 27 | public class TeeOutputStreamTest { 28 | 29 | public static class ExceptionOnCloseByteArrayOutputStream extends RememberCloseOutputStream { 30 | 31 | public ExceptionOnCloseByteArrayOutputStream(OutputStream out) { 32 | super(out); 33 | } 34 | 35 | @Override 36 | public void close() throws IOException { 37 | super.close(); 38 | throw new IOException(); 39 | } 40 | } 41 | 42 | @Test 43 | public void shouldCopyContentsToBothStreams() throws IOException { 44 | ByteArrayOutputStream left = new ByteArrayOutputStream(); 45 | ByteArrayOutputStream right = new ByteArrayOutputStream(); 46 | TeeOutputStream teeOutputStream = new TeeOutputStream(left, right); 47 | 48 | teeOutputStream.write(10); 49 | teeOutputStream.write(new byte[]{1, 2, 3}); 50 | teeOutputStream.write(new byte[]{10, 11, 12, 13, 14, 15, 15, 16}, 2, 3); 51 | 52 | Assert.assertArrayEquals(new byte[]{10, 1, 2, 3, 12, 13, 14}, left.toByteArray()); 53 | Assert.assertArrayEquals(new byte[]{10, 1, 2, 3, 12, 13, 14}, right.toByteArray()); 54 | } 55 | 56 | @Test 57 | public void shouldCloseBothStreamsWhenClosingTee() throws IOException { 58 | RememberCloseOutputStream left = new RememberCloseOutputStream(NullOutputStream.NULL_OUTPUT_STREAM); 59 | RememberCloseOutputStream right = new RememberCloseOutputStream(NullOutputStream.NULL_OUTPUT_STREAM); 60 | TeeOutputStream teeOutputStream = new TeeOutputStream(left, right); 61 | 62 | teeOutputStream.close(); 63 | 64 | Assert.assertTrue(left.isClosed()); 65 | Assert.assertTrue(right.isClosed()); 66 | } 67 | 68 | @Test 69 | public void shouldCloseSecondStreamWhenClosingFirstFails() { 70 | ExceptionOnCloseByteArrayOutputStream left = new ExceptionOnCloseByteArrayOutputStream(NullOutputStream.NULL_OUTPUT_STREAM); 71 | RememberCloseOutputStream right = new RememberCloseOutputStream(NullOutputStream.NULL_OUTPUT_STREAM); 72 | TeeOutputStream teeOutputStream = new TeeOutputStream(left, right); 73 | try { 74 | teeOutputStream.close(); 75 | Assert.fail("Was expecting an exception!"); 76 | } catch (IOException expected) { 77 | 78 | } 79 | 80 | Assert.assertTrue(left.isClosed()); 81 | Assert.assertTrue(right.isClosed()); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/test/java/org/zeroturnaround/exec/test/ArgumentsAsList.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.test; 19 | 20 | import java.util.Arrays; 21 | 22 | public class ArgumentsAsList { 23 | 24 | public static void main(String[] args) { 25 | System.out.print(Arrays.asList(args)); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/test/java/org/zeroturnaround/exec/test/BigOutput.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.test; 19 | 20 | public class BigOutput { 21 | 22 | public static final int LENGTH = 100000; 23 | 24 | public static void main(String[] args) { 25 | for (int i = 0; i < LENGTH; i++) { 26 | System.out.print("+"); 27 | System.err.print("-"); 28 | } 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/test/java/org/zeroturnaround/exec/test/CallerLoggerUtilTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.test; 19 | 20 | import org.junit.Assert; 21 | import org.junit.Test; 22 | import org.zeroturnaround.exec.stream.CallerLoggerUtil; 23 | 24 | 25 | public class CallerLoggerUtilTest { 26 | 27 | @Test 28 | public void testFullName() throws Exception { 29 | String fullName = "my.full.Logger"; 30 | Assert.assertEquals(fullName, CallerLoggerUtil.getName(fullName)); 31 | } 32 | 33 | @Test 34 | public void testShortName() throws Exception { 35 | String shortName = "MyLogger"; 36 | String fullName = getClass().getName() + "." + shortName; 37 | Assert.assertEquals(fullName, CallerLoggerUtil.getName(shortName)); 38 | } 39 | 40 | @Test 41 | public void testMyClassName() throws Exception { 42 | String fullName = getClass().getName(); 43 | Assert.assertEquals(fullName, CallerLoggerUtil.getName(null)); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/test/java/org/zeroturnaround/exec/test/EmptyArgTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.test; 19 | 20 | import org.junit.Assert; 21 | import org.junit.Test; 22 | import org.zeroturnaround.exec.ProcessExecutor; 23 | 24 | import java.util.ArrayList; 25 | import java.util.Arrays; 26 | import java.util.List; 27 | 28 | /** 29 | * Tests passing empty arguments. 30 | * 31 | * @see ProcessExecutor 32 | * @see ArgumentsAsList 33 | */ 34 | public class EmptyArgTest { 35 | 36 | @Test 37 | public void testReadOutputAndError() throws Exception { 38 | String output = argumentsAsList("arg1", "", "arg3", "").readOutput(true).execute().outputUTF8(); 39 | Assert.assertEquals("[arg1, , arg3, ]", output); 40 | } 41 | 42 | private ProcessExecutor argumentsAsList(String... args) { 43 | List command = new ArrayList(); 44 | command.addAll(Arrays.asList("java", "-cp", "target/test-classes", ArgumentsAsList.class.getName())); 45 | command.addAll(Arrays.asList(args)); 46 | return new ProcessExecutor(command); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/test/java/org/zeroturnaround/exec/test/ExitLikeABoss.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.test; 19 | 20 | public class ExitLikeABoss { 21 | public static void main(String[] args) { 22 | int exitCode = 0; 23 | if (args.length>0) { 24 | try { 25 | exitCode = Integer.parseInt(args[0]); 26 | } 27 | catch (NumberFormatException e) { 28 | System.out.println("Please provide valid exit code as parameter"); 29 | } 30 | } 31 | System.exit(exitCode); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/test/java/org/zeroturnaround/exec/test/HelloWorld.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.test; 19 | 20 | public class HelloWorld { 21 | 22 | public static void main(String[] args) { 23 | System.out.print("Hello "); 24 | System.err.print("world!"); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/test/java/org/zeroturnaround/exec/test/InputRedirectTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.test; 19 | 20 | import java.io.ByteArrayInputStream; 21 | 22 | import org.apache.commons.lang3.SystemUtils; 23 | import org.junit.Assume; 24 | import org.junit.Test; 25 | import org.slf4j.Logger; 26 | import org.slf4j.LoggerFactory; 27 | import org.zeroturnaround.exec.ProcessExecutor; 28 | 29 | /** 30 | * Reported in https://github.com/zeroturnaround/zt-exec/issues/30 31 | */ 32 | public class InputRedirectTest { 33 | 34 | private static final Logger log = LoggerFactory.getLogger(InputRedirectTest.class); 35 | 36 | @Test 37 | public void testRedirectInput() throws Exception { 38 | String binTrue; 39 | if (SystemUtils.IS_OS_LINUX) { 40 | binTrue = "/bin/true"; 41 | } 42 | else if (SystemUtils.IS_OS_MAC_OSX) { 43 | binTrue = "/usr/bin/true"; 44 | } 45 | else { 46 | Assume.assumeTrue("Unsupported OS " + SystemUtils.OS_NAME, false); 47 | return; // Skip this test 48 | } 49 | 50 | // We need to put something in the buffer 51 | ByteArrayInputStream bais = new ByteArrayInputStream("foo".getBytes()); 52 | ProcessExecutor exec = new ProcessExecutor().command(binTrue); 53 | // Test that we don't get IOException: Stream closed 54 | int exit = exec.redirectInput(bais).readOutput(true).execute().getExitValue(); 55 | log.debug("Exit: {}", exit); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/test/java/org/zeroturnaround/exec/test/InputStreamPumperTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.test; 19 | 20 | import java.io.ByteArrayInputStream; 21 | import java.io.ByteArrayOutputStream; 22 | 23 | import org.junit.Assert; 24 | import org.junit.Test; 25 | import org.zeroturnaround.exec.ProcessExecutor; 26 | import org.zeroturnaround.exec.stream.PumpStreamHandler; 27 | 28 | /** 29 | * Tests that test redirected input for the process to be run. 30 | */ 31 | public class InputStreamPumperTest { 32 | 33 | @Test 34 | public void testPumpFromInputToOutput() throws Exception { 35 | String str = "Tere Minu Uus vihik"; 36 | ByteArrayInputStream bais = new ByteArrayInputStream(str.getBytes()); 37 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 38 | PumpStreamHandler handler = new PumpStreamHandler(baos, System.err, bais); 39 | 40 | ProcessExecutor exec = new ProcessExecutor("java", "-cp", "target/test-classes", 41 | PrintInputToOutput.class.getName()).readOutput(true); 42 | exec.streams(handler); 43 | 44 | String result = exec.execute().outputUTF8(); 45 | Assert.assertEquals(str, result); 46 | } 47 | 48 | @Test 49 | public void testPumpFromInputToOutputWithInput() throws Exception { 50 | String str = "Tere Minu Uus vihik"; 51 | ByteArrayInputStream bais = new ByteArrayInputStream(str.getBytes()); 52 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 53 | 54 | ProcessExecutor exec = new ProcessExecutor("java", "-cp", "target/test-classes", 55 | PrintInputToOutput.class.getName()).readOutput(true).redirectInput(bais); 56 | 57 | String result = exec.execute().outputUTF8(); 58 | Assert.assertEquals(str, result); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/test/java/org/zeroturnaround/exec/test/LogOutputStreamTest.java: -------------------------------------------------------------------------------- 1 | package org.zeroturnaround.exec.test; 2 | 3 | import java.io.IOException; 4 | import java.io.UnsupportedEncodingException; 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.List; 8 | 9 | import org.junit.Assert; 10 | import org.junit.Test; 11 | import org.zeroturnaround.exec.stream.LineConsumer; 12 | import org.zeroturnaround.exec.stream.LogOutputStream; 13 | 14 | public class LogOutputStreamTest { 15 | 16 | private void testLogOutputStream(String multiLineString, String... expectedLines) throws UnsupportedEncodingException, IOException { 17 | final List processedLines = new ArrayList(); 18 | LogOutputStream logOutputStream = new LogOutputStream() { 19 | 20 | @Override 21 | protected void processLine(String line) { 22 | processedLines.add(line); 23 | } 24 | }; 25 | try { 26 | logOutputStream.write(multiLineString.getBytes("UTF-8")); 27 | } finally { 28 | logOutputStream.close(); 29 | } 30 | Assert.assertEquals(Arrays.asList(expectedLines), processedLines); 31 | } 32 | 33 | @Test 34 | public void testSimple() throws UnsupportedEncodingException, IOException { 35 | testLogOutputStream("foo", "foo"); 36 | } 37 | 38 | @Test 39 | public void testNewLine() throws UnsupportedEncodingException, IOException { 40 | testLogOutputStream("foo\nbar", "foo", "bar"); 41 | } 42 | 43 | @Test 44 | public void testNewLineWithMultipleLines() throws UnsupportedEncodingException, IOException { 45 | testLogOutputStream("foo1\nbar1\nfoo2\nbar2", "foo1", "bar1", "foo2", "bar2"); 46 | } 47 | 48 | @Test 49 | public void testCarriageReturn() throws UnsupportedEncodingException, IOException { 50 | testLogOutputStream("foo\rbar", "foo", "bar"); 51 | } 52 | 53 | @Test 54 | public void testCarriageReturnWithMultipleLines() throws UnsupportedEncodingException, IOException { 55 | testLogOutputStream("foo1\rbar1\rfoo2\rbar2", "foo1", "bar1", "foo2", "bar2"); 56 | } 57 | 58 | @Test 59 | public void testCarriageReturnAndNewLine() throws UnsupportedEncodingException, IOException { 60 | testLogOutputStream("foo\r\nbar", "foo", "bar"); 61 | } 62 | 63 | @Test 64 | public void testCarriageReturnAndNewLineWithMultipleLines() throws UnsupportedEncodingException, IOException { 65 | testLogOutputStream("foo1\r\nbar1\r\nfoo2\r\nbar2", "foo1", "bar1", "foo2", "bar2"); 66 | } 67 | 68 | @Test 69 | public void testTwoNewLines() throws UnsupportedEncodingException, IOException { 70 | testLogOutputStream("foo\n\nbar", "foo", "bar"); 71 | } 72 | 73 | @Test 74 | public void testNewLineAtTheEnd() throws UnsupportedEncodingException, IOException { 75 | testLogOutputStream("foo\n", "foo"); 76 | } 77 | 78 | @Test 79 | public void lambda() throws IOException { 80 | final List lines = new ArrayList(); 81 | LogOutputStream out = LogOutputStream.create(new LineConsumer() { 82 | @Override 83 | public void accept(String line) { 84 | lines.add(line); 85 | } 86 | }); 87 | out.write("foo\nbar\n".getBytes()); 88 | Assert.assertEquals(Arrays.asList("foo", "bar"), lines); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/test/java/org/zeroturnaround/exec/test/Loop.java: -------------------------------------------------------------------------------- 1 | package org.zeroturnaround.exec.test; 2 | 3 | import java.io.PrintStream; 4 | 5 | class Loop { 6 | 7 | private static final long INTERVAL = 1000; 8 | private static final long COUNT = 10; 9 | 10 | public static void main(String[] args) throws Exception { 11 | PrintStream out = System.out; 12 | out.println("Started"); 13 | for (int i = 0; i < COUNT; i++) { 14 | out.println(i); 15 | Thread.sleep(INTERVAL); 16 | } 17 | out.println("Finished"); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/org/zeroturnaround/exec/test/MockProcessDestroyer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.test; 19 | 20 | import org.zeroturnaround.exec.listener.ProcessDestroyer; 21 | 22 | public class MockProcessDestroyer implements ProcessDestroyer { 23 | 24 | volatile Process added; 25 | volatile Process removed; 26 | 27 | public boolean add(Process process) { 28 | added = process; 29 | return true; 30 | } 31 | 32 | public boolean remove(Process process) { 33 | removed = process; 34 | return true; 35 | } 36 | 37 | public int size() { 38 | return 0; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/org/zeroturnaround/exec/test/PrintArguments.java: -------------------------------------------------------------------------------- 1 | package org.zeroturnaround.exec.test; 2 | 3 | public class PrintArguments { 4 | 5 | public static void main(String[] args) { 6 | for (String arg : args) { 7 | System.out.println(arg); 8 | } 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/test/java/org/zeroturnaround/exec/test/PrintInputToOutput.java: -------------------------------------------------------------------------------- 1 | package org.zeroturnaround.exec.test; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.InputStreamReader; 5 | 6 | public class PrintInputToOutput { 7 | public static void main(String[] args) throws Exception { 8 | InputStreamReader isr = new InputStreamReader(System.in); 9 | BufferedReader br = new BufferedReader(isr); 10 | 11 | String line = null; 12 | int count = 0; 13 | while ((line = br.readLine()) != null) { 14 | System.out.print(line); 15 | count++; 16 | if (count == 3) 17 | break; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/org/zeroturnaround/exec/test/ProcessExecutorArgsWithExtraSpaceTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.test; 19 | 20 | import org.junit.Assert; 21 | import org.junit.Test; 22 | import org.zeroturnaround.exec.ProcessExecutor; 23 | 24 | 25 | /** 26 | * Tests argument splitting. 27 | * 28 | * @see ProcessExecutor 29 | * @see ArgumentsAsList 30 | */ 31 | public class ProcessExecutorArgsWithExtraSpaceTest { 32 | 33 | @Test 34 | public void testReadOutputAndError() throws Exception { 35 | String output = argumentsAsList("arg1 arg2 arg3").readOutput(true).execute().outputUTF8(); 36 | Assert.assertEquals("[arg1, arg2, arg3]", output); 37 | } 38 | 39 | private ProcessExecutor argumentsAsList(String args) { 40 | return new ProcessExecutor().commandSplit("java -cp target/test-classes " + ArgumentsAsList.class.getName() + " " + args); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/org/zeroturnaround/exec/test/ProcessExecutorBigOutputTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.test; 19 | 20 | import java.io.ByteArrayOutputStream; 21 | import java.util.concurrent.TimeUnit; 22 | 23 | import org.junit.Assert; 24 | import org.junit.Test; 25 | import org.zeroturnaround.exec.ProcessExecutor; 26 | 27 | 28 | /** 29 | * Tests reading large output that doesn't fit into a buffer between this process and sub process. 30 | * 31 | * @author Rein Raudjärv 32 | * 33 | * @see ProcessExecutor 34 | * @see BigOutput 35 | */ 36 | public class ProcessExecutorBigOutputTest { 37 | 38 | @Test 39 | public void testDevNull() throws Exception { 40 | bigOutput().execute(); 41 | } 42 | 43 | @Test 44 | public void testDevNullSeparate() throws Exception { 45 | bigOutput().redirectErrorStream(false).execute(); 46 | } 47 | 48 | @Test 49 | public void testReadOutputAndError() throws Exception { 50 | String output = bigOutput().readOutput(true).execute().outputUTF8(); 51 | Assert.assertEquals(repeat("+-"), output); 52 | } 53 | 54 | @Test 55 | public void testReadOutputOnly() throws Exception { 56 | String output = bigOutput().readOutput(true).redirectErrorStream(false).execute().outputUTF8(); 57 | Assert.assertEquals(repeat("+"), output); 58 | } 59 | 60 | @Test 61 | public void testRedirectOutputOnly() throws Exception { 62 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 63 | bigOutput().redirectOutput(out).redirectErrorStream(false).execute(); 64 | Assert.assertEquals(repeat("+"), new String(out.toByteArray())); 65 | } 66 | 67 | @Test 68 | public void testRedirectErrorOnly() throws Exception { 69 | ByteArrayOutputStream err = new ByteArrayOutputStream(); 70 | bigOutput().redirectError(err).redirectErrorStream(false).execute(); 71 | Assert.assertEquals(repeat("-"), new String(err.toByteArray())); 72 | } 73 | 74 | private ProcessExecutor bigOutput() { 75 | // Use timeout in case we get stuck 76 | return new ProcessExecutor("java", "-cp", "target/test-classes", BigOutput.class.getName()).timeout(10, TimeUnit.SECONDS); 77 | } 78 | 79 | private static String repeat(String s) { 80 | StringBuffer sb = new StringBuffer(BigOutput.LENGTH * 2); 81 | for (int i = 0; i < BigOutput.LENGTH; i++) 82 | sb.append(s); 83 | return sb.toString(); 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /src/test/java/org/zeroturnaround/exec/test/ProcessExecutorCommandLineTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.test; 19 | 20 | import java.io.ByteArrayInputStream; 21 | import java.io.IOException; 22 | import java.util.ArrayList; 23 | import java.util.Arrays; 24 | import java.util.List; 25 | import java.util.concurrent.TimeoutException; 26 | 27 | import org.apache.commons.io.IOUtils; 28 | import org.junit.Assert; 29 | import org.junit.Test; 30 | import org.zeroturnaround.exec.ProcessExecutor; 31 | 32 | 33 | /** 34 | * Tests passing command line arguments to a Java process. 35 | */ 36 | public class ProcessExecutorCommandLineTest { 37 | 38 | @Test 39 | public void testOneArg() throws Exception { 40 | testArguments("foo"); 41 | } 42 | 43 | @Test 44 | public void testTwoArgs() throws Exception { 45 | testArguments("foo", "bar"); 46 | } 47 | 48 | @Test 49 | public void testSpaces() throws Exception { 50 | testArguments("foo foo", "bar bar"); 51 | } 52 | 53 | @Test 54 | public void testQuotes() throws Exception { 55 | String[] args = new String[]{"\"a\"", "b \"c\" d", "f \"e\"", "\"g\" h"}; 56 | List expected = Arrays.asList("\"a\"", "b \"c\" d", "f \"e\"", "\"g\" h"); 57 | if (System.getProperty("os.name").startsWith("Windows")) 58 | expected = Arrays.asList("a", "b c d", "f e", "g h"); 59 | testArguments(expected, args); 60 | } 61 | 62 | @Test 63 | public void testSlashes() throws Exception { 64 | testArguments("/o\\", "\\/.*"); 65 | } 66 | 67 | private void testArguments(String... args) throws IOException, InterruptedException, TimeoutException { 68 | byte[] bytes = printArguments(args).execute().output(); 69 | List expected = Arrays.asList(args); 70 | List actual = IOUtils.readLines(new ByteArrayInputStream(bytes)); 71 | Assert.assertEquals(expected, actual); 72 | } 73 | 74 | private void testArguments(List expected, String... args) throws IOException, InterruptedException, TimeoutException { 75 | byte[] bytes = printArguments(args).execute().output(); 76 | List actual = IOUtils.readLines(new ByteArrayInputStream(bytes)); 77 | Assert.assertEquals(expected, actual); 78 | } 79 | 80 | private ProcessExecutor printArguments(String... args) { 81 | List command = new ArrayList(); 82 | command.addAll(Arrays.asList("java", "-cp", "target/test-classes", PrintArguments.class.getName())); 83 | command.addAll(Arrays.asList(args)); 84 | return new ProcessExecutor(command).readOutput(true); 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /src/test/java/org/zeroturnaround/exec/test/ProcessExecutorExitValueTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.test; 19 | 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | import java.util.concurrent.TimeUnit; 23 | 24 | import org.junit.Test; 25 | import org.zeroturnaround.exec.InvalidExitValueException; 26 | import org.zeroturnaround.exec.ProcessExecutor; 27 | 28 | 29 | public class ProcessExecutorExitValueTest { 30 | 31 | @Test(expected=InvalidExitValueException.class) 32 | public void testJavaVersionExitValueCheck() throws Exception { 33 | new ProcessExecutor().command("java", "-version").exitValues(3).execute(); 34 | } 35 | 36 | @Test(expected=InvalidExitValueException.class) 37 | public void testJavaVersionExitValueCheckTimeout() throws Exception { 38 | new ProcessExecutor().command("java", "-version").exitValues(3).timeout(60, TimeUnit.SECONDS).execute(); 39 | } 40 | 41 | public void testNonZeroExitValueByDefault() throws Exception { 42 | new ProcessExecutor(exitLikeABoss(17)).execute(); 43 | } 44 | 45 | @Test 46 | public void testCustomExitValueValid() throws Exception { 47 | new ProcessExecutor(exitLikeABoss(17)).exitValues(17).execute(); 48 | } 49 | 50 | @Test(expected=InvalidExitValueException.class) 51 | public void testCustomExitValueInvalid() throws Exception { 52 | new ProcessExecutor(exitLikeABoss(17)).exitValues(15).execute(); 53 | } 54 | 55 | private static List exitLikeABoss(int exitValue) { 56 | List result = new ArrayList(); 57 | result.add("java"); 58 | result.add("-cp"); 59 | result.add("target/test-classes"); 60 | result.add(ExitLikeABoss.class.getName()); 61 | result.add(String.valueOf(exitValue)); 62 | return result; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/test/java/org/zeroturnaround/exec/test/ProcessExecutorHelloWorldTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.test; 19 | 20 | import java.io.ByteArrayOutputStream; 21 | 22 | import org.junit.Assert; 23 | import org.junit.Test; 24 | import org.zeroturnaround.exec.ProcessExecutor; 25 | 26 | 27 | /** 28 | * Tests redirecting stream. 29 | * 30 | * @author Rein Raudjärv 31 | * 32 | * @see ProcessExecutor 33 | * @see HelloWorld 34 | */ 35 | public class ProcessExecutorHelloWorldTest { 36 | 37 | @Test 38 | public void testReadOutputAndError() throws Exception { 39 | String output = helloWorld().readOutput(true).execute().outputUTF8(); 40 | Assert.assertEquals("Hello world!", output); 41 | } 42 | 43 | @Test 44 | public void testReadOutputOnly() throws Exception { 45 | String output = helloWorld().readOutput(true).redirectErrorStream(false).execute().outputUTF8(); 46 | Assert.assertEquals("Hello ", output); 47 | } 48 | 49 | @Test 50 | public void testRedirectOutputAndError() throws Exception { 51 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 52 | helloWorld().redirectOutput(out).execute(); 53 | Assert.assertEquals("Hello world!", new String(out.toByteArray())); 54 | } 55 | 56 | @Test 57 | public void testRedirectOutputAndErrorMerged() throws Exception { 58 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 59 | ByteArrayOutputStream err = new ByteArrayOutputStream(); 60 | helloWorld().redirectOutput(out).redirectError(err).execute(); 61 | Assert.assertEquals("Hello ", new String(out.toByteArray())); 62 | Assert.assertEquals("world!", new String(err.toByteArray())); 63 | } 64 | 65 | @Test 66 | public void testRedirectOutputAndErrorAndReadOutput() throws Exception { 67 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 68 | String output = helloWorld().redirectOutput(out).readOutput(true).execute().outputUTF8(); 69 | Assert.assertEquals("Hello world!", output); 70 | Assert.assertEquals("Hello world!", new String(out.toByteArray())); 71 | } 72 | 73 | @Test 74 | public void testRedirectOutputOnly() throws Exception { 75 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 76 | helloWorld().redirectOutput(out).redirectErrorStream(false).execute(); 77 | Assert.assertEquals("Hello ", new String(out.toByteArray())); 78 | } 79 | 80 | @Test 81 | public void testRedirectOutputOnlyAndReadOutput() throws Exception { 82 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 83 | String output = helloWorld().redirectOutput(out).redirectErrorStream(false).readOutput(true).execute().outputUTF8(); 84 | Assert.assertEquals("Hello ", output); 85 | Assert.assertEquals("Hello ", new String(out.toByteArray())); 86 | } 87 | 88 | @Test 89 | public void testRedirectErrorOnly() throws Exception { 90 | ByteArrayOutputStream err = new ByteArrayOutputStream(); 91 | helloWorld().redirectError(err).redirectErrorStream(false).execute(); 92 | Assert.assertEquals("world!", new String(err.toByteArray())); 93 | } 94 | 95 | @Test 96 | public void testRedirectErrorOnlyAndReadOutput() throws Exception { 97 | ByteArrayOutputStream err = new ByteArrayOutputStream(); 98 | String output = helloWorld().redirectError(err).redirectErrorStream(false).readOutput(true).execute().outputUTF8(); 99 | Assert.assertEquals("Hello ", output); 100 | Assert.assertEquals("world!", new String(err.toByteArray())); 101 | } 102 | 103 | @Test 104 | public void testRedirectOutputAndErrorSeparate() throws Exception { 105 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 106 | ByteArrayOutputStream err = new ByteArrayOutputStream(); 107 | helloWorld().redirectOutput(out).redirectError(err).redirectErrorStream(false).execute(); 108 | Assert.assertEquals("Hello ", new String(out.toByteArray())); 109 | Assert.assertEquals("world!", new String(err.toByteArray())); 110 | } 111 | 112 | @Test 113 | public void testRedirectOutputAndErrorSeparateAndReadOutput() throws Exception { 114 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 115 | ByteArrayOutputStream err = new ByteArrayOutputStream(); 116 | String output = helloWorld().redirectOutput(out).redirectError(err).redirectErrorStream(false).readOutput(true).execute().outputUTF8(); 117 | Assert.assertEquals("Hello ", output); 118 | Assert.assertEquals("Hello ", new String(out.toByteArray())); 119 | Assert.assertEquals("world!", new String(err.toByteArray())); 120 | } 121 | 122 | private ProcessExecutor helloWorld() { 123 | return new ProcessExecutor("java", "-cp", "target/test-classes", HelloWorld.class.getName()); 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /src/test/java/org/zeroturnaround/exec/test/ProcessExecutorInputStreamTest.java: -------------------------------------------------------------------------------- 1 | package org.zeroturnaround.exec.test; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.ByteArrayOutputStream; 5 | import java.io.PipedInputStream; 6 | import java.io.PipedOutputStream; 7 | import java.util.concurrent.TimeUnit; 8 | 9 | import org.junit.Assert; 10 | import org.junit.Test; 11 | import org.zeroturnaround.exec.ProcessExecutor; 12 | import org.zeroturnaround.exec.StartedProcess; 13 | 14 | /** 15 | * 16 | */ 17 | public class ProcessExecutorInputStreamTest { 18 | 19 | @Test 20 | public void testWithInputAndRedirectOutput() throws Exception { 21 | String str = "Tere Minu Uus vihik"; 22 | ByteArrayInputStream bais = new ByteArrayInputStream(str.getBytes()); 23 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 24 | 25 | ProcessExecutor exec = new ProcessExecutor("java", "-cp", "target/test-classes", PrintInputToOutput.class.getName()); 26 | exec.redirectInput(bais).redirectOutput(baos); 27 | 28 | exec.execute(); 29 | Assert.assertEquals(str, baos.toString()); 30 | } 31 | 32 | @Test 33 | public void testRedirectPipedInputStream() throws Exception { 34 | // Setup InputStream that will block on a read() 35 | PipedOutputStream pos = new PipedOutputStream(); 36 | PipedInputStream pis = new PipedInputStream(pos); 37 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 38 | 39 | ProcessExecutor exec = new ProcessExecutor("java", "-cp", "target/test-classes", PrintArguments.class.getName()); 40 | exec.redirectInput(pis); 41 | StartedProcess startedProcess = exec.start(); 42 | // Assert that we don't get a TimeoutException 43 | startedProcess.getFuture().get(5, TimeUnit.SECONDS); 44 | } 45 | 46 | @Test 47 | public void testDataIsFlushedToProcessWithANonEndingInputStream() throws Exception { 48 | String str = "Tere Minu Uus vihik " + System.nanoTime(); 49 | 50 | // Setup InputStream that will block on a read() 51 | PipedOutputStream pos = new PipedOutputStream(); 52 | PipedInputStream pis = new PipedInputStream(pos); 53 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 54 | 55 | ProcessExecutor exec = new ProcessExecutor("java", "-cp", "target/test-classes", PrintInputToOutput.class.getName()); 56 | exec.redirectInput(pis).redirectOutput(baos); 57 | StartedProcess startedProcess = exec.start(); 58 | pos.write(str.getBytes()); 59 | pos.write("\n\n\n".getBytes()); // PrintInputToOutput processes at most 3 lines 60 | 61 | // Assert that we don't get a TimeoutException 62 | startedProcess.getFuture().get(5, TimeUnit.SECONDS); 63 | Assert.assertEquals(str, baos.toString()); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/test/java/org/zeroturnaround/exec/test/ProcessExecutorLoggerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.test; 19 | 20 | import java.io.OutputStream; 21 | 22 | import org.junit.Assert; 23 | import org.junit.Test; 24 | import org.zeroturnaround.exec.ProcessExecutor; 25 | import org.zeroturnaround.exec.stream.PumpStreamHandler; 26 | import org.zeroturnaround.exec.stream.slf4j.Slf4jInfoOutputStream; 27 | import org.zeroturnaround.exec.stream.slf4j.Slf4jStream; 28 | 29 | 30 | public class ProcessExecutorLoggerTest { 31 | 32 | @Test 33 | public void testFullName() throws Exception { 34 | String fullName = "my.full.Logger"; 35 | testSlf4jLoggerName(fullName, Slf4jStream.of(fullName)); 36 | } 37 | 38 | @Test 39 | public void testShortName() throws Exception { 40 | String shortName = "MyLogger"; 41 | String fullName = getClass().getName() + "." + shortName; 42 | testSlf4jLoggerName(fullName, Slf4jStream.of(shortName)); 43 | } 44 | 45 | @Test 46 | public void testClassNameWithShortName() throws Exception { 47 | String shortName = "MyLogger"; 48 | String fullName = getClass().getName() + "." + shortName; 49 | testSlf4jLoggerName(fullName, Slf4jStream.of(getClass(), shortName)); 50 | } 51 | 52 | @Test 53 | public void testMyClassName() throws Exception { 54 | String fullName = getClass().getName(); 55 | testSlf4jLoggerName(fullName, Slf4jStream.ofCaller()); 56 | } 57 | 58 | private void testSlf4jLoggerName(String fullName, Slf4jStream stream) { 59 | ProcessExecutor executor = new ProcessExecutor(); 60 | executor.redirectOutput(stream.asInfo()); 61 | PumpStreamHandler pumps = executor.pumps(); 62 | OutputStream out = pumps.getOut(); 63 | Assert.assertTrue("Slf4jInfoOutputStream expected", out instanceof Slf4jInfoOutputStream); 64 | Assert.assertEquals(fullName, ((Slf4jInfoOutputStream) out).getLogger().getName()); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/test/java/org/zeroturnaround/exec/test/ProcessExecutorMainTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.test; 19 | 20 | import java.io.File; 21 | import java.io.IOException; 22 | import java.util.ArrayList; 23 | import java.util.Arrays; 24 | import java.util.List; 25 | 26 | import org.apache.commons.lang3.StringUtils; 27 | import org.junit.Assert; 28 | import org.junit.Test; 29 | import org.zeroturnaround.exec.ProcessExecutor; 30 | import org.zeroturnaround.exec.ProcessResult; 31 | import org.zeroturnaround.exec.stream.ExecuteStreamHandler; 32 | import org.zeroturnaround.exec.stream.slf4j.Slf4jStream; 33 | 34 | 35 | public class ProcessExecutorMainTest { 36 | 37 | @Test(expected=IllegalStateException.class) 38 | public void testNoCommand() throws Exception { 39 | new ProcessExecutor().execute(); 40 | } 41 | 42 | @Test(expected=IOException.class) 43 | public void testNoSuchFile() throws Exception { 44 | new ProcessExecutor().command("unknown command").execute(); 45 | } 46 | 47 | @Test 48 | public void testJavaVersion() throws Exception { 49 | int exit = new ProcessExecutor().command("java", "-version").execute().getExitValue(); 50 | Assert.assertEquals(0, exit); 51 | } 52 | 53 | @Test 54 | public void testJavaVersionCommandSplit() throws Exception { 55 | int exit = new ProcessExecutor().commandSplit("java -version").execute().getExitValue(); 56 | Assert.assertEquals(0, exit); 57 | } 58 | 59 | @Test 60 | public void testJavaVersionIterable() throws Exception { 61 | Iterable iterable = Arrays.asList("java", "-version"); 62 | int exit = new ProcessExecutor().command(iterable).execute().getExitValue(); 63 | Assert.assertEquals(0, exit); 64 | } 65 | 66 | @Test 67 | public void testJavaVersionFuture() throws Exception { 68 | int exit = new ProcessExecutor().command("java", "-version").start().getFuture().get().getExitValue(); 69 | Assert.assertEquals(0, exit); 70 | } 71 | 72 | @Test 73 | public void testJavaVersionOutput() throws Exception { 74 | ProcessResult result = new ProcessExecutor().command("java", "-version").readOutput(true).execute(); 75 | String str = result.outputUTF8(); 76 | Assert.assertFalse(StringUtils.isEmpty(str)); 77 | } 78 | 79 | @Test 80 | public void testJavaVersionOutputTwice() throws Exception { 81 | ProcessExecutor executor = new ProcessExecutor().command("java", "-version").readOutput(true); 82 | ProcessResult result = executor.execute(); 83 | String str = result.outputUTF8(); 84 | Assert.assertFalse(StringUtils.isEmpty(str)); 85 | Assert.assertEquals(str, executor.execute().outputUTF8()); 86 | } 87 | 88 | @Test 89 | public void testJavaVersionOutputFuture() throws Exception { 90 | ProcessResult result = new ProcessExecutor().command("java", "-version").readOutput(true).start().getFuture().get(); 91 | String str = result.outputUTF8(); 92 | Assert.assertFalse(StringUtils.isEmpty(str)); 93 | } 94 | 95 | @Test 96 | public void testJavaVersionLogInfo() throws Exception { 97 | // Just expect no errors - don't check the log file itself 98 | new ProcessExecutor().command("java", "-version").redirectOutput(Slf4jStream.of("testJavaVersionLogInfo").asInfo()).execute(); 99 | } 100 | 101 | @Test 102 | public void testJavaVersionLogInfoAndOutput() throws Exception { 103 | // Just expect no errors - don't check the log file itself 104 | ProcessResult result = new ProcessExecutor().command("java", "-version").redirectOutput(Slf4jStream.of("testJavaVersionLogInfoAndOutput").asInfo()).readOutput(true).execute(); 105 | String str = result.outputUTF8(); 106 | Assert.assertFalse(StringUtils.isEmpty(str)); 107 | } 108 | 109 | @Test 110 | public void testJavaVersionLogInfoAndOutputFuture() throws Exception { 111 | // Just expect no errors - don't check the log file itself 112 | ProcessResult result = new ProcessExecutor().command("java", "-version").redirectOutput(Slf4jStream.of("testJavaVersionLogInfoAndOutputFuture").asInfo()).readOutput(true).start().getFuture().get(); 113 | String str = result.outputUTF8(); 114 | Assert.assertFalse(StringUtils.isEmpty(str)); 115 | } 116 | 117 | @Test 118 | public void testJavaVersionNoStreams() throws Exception { 119 | // Just expect no errors 120 | new ProcessExecutor().command("java", "-version").streams(null).execute(); 121 | } 122 | 123 | @Test 124 | public void testProcessDestroyerEvents() throws Exception { 125 | MockProcessDestroyer mock = new MockProcessDestroyer(); 126 | new ProcessExecutor().command("java", "-version").destroyer(mock).execute(); 127 | Assert.assertNotNull(mock.added); 128 | Assert.assertEquals(mock.added, mock.removed); 129 | } 130 | 131 | @Test 132 | public void testProcessDestroyerEventsOnStreamsFail() throws Exception { 133 | MockProcessDestroyer mock = new MockProcessDestroyer(); 134 | ExecuteStreamHandler streams = new SetFailExecuteStreamHandler(); 135 | try { 136 | new ProcessExecutor().command("java", "-version").streams(streams).destroyer(mock).execute(); 137 | Assert.fail("IOException expected"); 138 | } 139 | catch (IOException e) { 140 | // Good 141 | } 142 | Assert.assertNull(mock.added); 143 | Assert.assertNull(mock.removed); 144 | } 145 | 146 | @Test 147 | public void testProcessExecutorListInit() throws Exception { 148 | // Use timeout in case we get stuck 149 | List args = new ArrayList() { 150 | { 151 | add("java"); 152 | add("-cp"); 153 | add("target/test-classes"); 154 | add(HelloWorld.class.getName()); 155 | } 156 | }; 157 | ProcessExecutor exec = new ProcessExecutor(args); 158 | ProcessResult result = exec.readOutput(true).execute(); 159 | Assert.assertEquals("Hello world!", result.outputUTF8()); 160 | } 161 | 162 | @Test 163 | public void testProcessExecutorCommand() throws Exception { 164 | // Use timeout in case we get stuck 165 | List args = new ArrayList() { 166 | { 167 | add("java"); 168 | add("-cp"); 169 | add("target/test-classes"); 170 | add(HelloWorld.class.getName()); 171 | } 172 | }; 173 | ProcessExecutor exec = new ProcessExecutor(); 174 | exec.command(args); 175 | ProcessResult result = exec.readOutput(true).execute(); 176 | Assert.assertEquals("Hello world!", result.outputUTF8()); 177 | } 178 | 179 | @Test 180 | public void testProcessExecutorSetDirectory() throws Exception { 181 | // Use timeout in case we get stuck 182 | List args = new ArrayList() { 183 | { 184 | add("java"); 185 | add("-cp"); 186 | add("test-classes"); 187 | add(HelloWorld.class.getName()); 188 | } 189 | }; 190 | ProcessExecutor exec = new ProcessExecutor().directory(new File("target")); 191 | exec.command(args); 192 | ProcessResult result = exec.readOutput(true).execute(); 193 | Assert.assertEquals("Hello world!", result.outputUTF8()); 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /src/test/java/org/zeroturnaround/exec/test/ProcessExecutorStreamCloseTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.test; 19 | 20 | import java.io.ByteArrayOutputStream; 21 | 22 | import org.junit.Assert; 23 | import org.junit.Test; 24 | import org.zeroturnaround.exec.ProcessExecutor; 25 | 26 | 27 | /** 28 | * Tests that redirect target stream are not closed. 29 | * 30 | * @author Rein Raudjärv 31 | * 32 | * @see ProcessExecutor 33 | * @see HelloWorld 34 | */ 35 | public class ProcessExecutorStreamCloseTest { 36 | 37 | @Test 38 | public void testRedirectOutputNotClosed() throws Exception { 39 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 40 | RememberCloseOutputStream close = new RememberCloseOutputStream(out); 41 | helloWorld().redirectOutput(close).redirectErrorStream(false).execute(); 42 | Assert.assertEquals("Hello ", new String(out.toByteArray())); 43 | Assert.assertFalse(close.isClosed()); 44 | } 45 | 46 | @Test 47 | public void testRedirectErrorNotClosed() throws Exception { 48 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 49 | RememberCloseOutputStream close = new RememberCloseOutputStream(out); 50 | helloWorld().redirectError(close).execute(); 51 | Assert.assertEquals("world!", new String(out.toByteArray())); 52 | Assert.assertFalse(close.isClosed()); 53 | } 54 | 55 | private ProcessExecutor helloWorld() { 56 | return new ProcessExecutor("java", "-cp", "target/test-classes", HelloWorld.class.getName()); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/test/java/org/zeroturnaround/exec/test/ProcessExecutorTimeoutTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.test; 19 | 20 | import org.apache.commons.lang3.SystemUtils; 21 | import org.hamcrest.CoreMatchers; 22 | import org.junit.Assert; 23 | import org.junit.Test; 24 | import org.zeroturnaround.exec.ProcessExecutor; 25 | 26 | import java.util.ArrayList; 27 | import java.util.List; 28 | import java.util.concurrent.TimeUnit; 29 | import java.util.concurrent.TimeoutException; 30 | 31 | public class ProcessExecutorTimeoutTest { 32 | 33 | @Test 34 | public void testExecuteTimeout() throws Exception { 35 | try { 36 | // Use timeout in case we get stuck 37 | List args = getWriterLoopCommand(); 38 | new ProcessExecutor().command(args).timeout(1, TimeUnit.SECONDS).execute(); 39 | Assert.fail("TimeoutException expected."); 40 | } 41 | catch (TimeoutException e) { 42 | Assert.assertThat(e.getMessage(), CoreMatchers.containsString("1 second")); 43 | Assert.assertThat(e.getMessage(), CoreMatchers.containsString(Loop.class.getName())); 44 | } 45 | } 46 | 47 | @Test 48 | public void testStartTimeout() throws Exception { 49 | try { 50 | // Use timeout in case we get stuck 51 | List args = getWriterLoopCommand(); 52 | new ProcessExecutor().command(args).start().getFuture().get(1, TimeUnit.SECONDS); 53 | Assert.fail("TimeoutException expected."); 54 | } 55 | catch (TimeoutException e) { 56 | Assert.assertNull(e.getMessage()); 57 | } 58 | } 59 | 60 | private List getWriterLoopCommand() { 61 | List args = new ArrayList() { 62 | { 63 | add("java"); 64 | add("-cp"); 65 | add("target/test-classes"); 66 | add(Loop.class.getName()); 67 | } 68 | }; 69 | return args; 70 | } 71 | 72 | /* 73 | * This is a test copied from https://github.com/zeroturnaround/zt-exec/issues/56 74 | */ 75 | @Test 76 | public void testExecuteTimeoutIssue56_1() throws Exception { 77 | try { 78 | List commands = new ArrayList(); 79 | if (SystemUtils.IS_OS_WINDOWS) { 80 | // native sleep command is not available on Windows platform 81 | // mock using standard ping to localhost instead 82 | // (Windows ping does 4 requests which takes about 3 seconds) 83 | commands.add("ping"); 84 | commands.add("127.0.0.1"); 85 | } 86 | else { 87 | commands.add("sleep"); 88 | commands.add("3"); 89 | } 90 | new ProcessExecutor() 91 | .command(commands) 92 | .timeout(1, TimeUnit.SECONDS) 93 | .execute(); 94 | Assert.fail("TimeoutException expected."); 95 | } 96 | catch (TimeoutException e) { 97 | Assert.assertThat(e.getMessage(), CoreMatchers.containsString("1 second")); 98 | } 99 | } 100 | 101 | /* 102 | * This is a test copied from https://github.com/zeroturnaround/zt-exec/issues/56 103 | */ 104 | @Test 105 | public void testStartTimeoutIssue56_2() throws Exception { 106 | try { 107 | List commands = new ArrayList(); 108 | if (SystemUtils.IS_OS_WINDOWS) { 109 | // native sleep command is not available on Windows platform 110 | // mock using standard ping to localhost instead 111 | // (Windows ping does 4 requests which takes about 3 seconds) 112 | commands.add("ping"); 113 | commands.add("127.0.0.1"); 114 | } 115 | else { 116 | commands.add("sleep"); 117 | commands.add("3"); 118 | } 119 | new ProcessExecutor() 120 | .command(commands) 121 | .start() 122 | .getFuture() 123 | .get(1, TimeUnit.SECONDS); 124 | Assert.fail("TimeoutException expected."); 125 | } 126 | catch (TimeoutException e) { 127 | Assert.assertNull(e.getMessage()); 128 | } 129 | } 130 | 131 | } 132 | -------------------------------------------------------------------------------- /src/test/java/org/zeroturnaround/exec/test/ProcessInitExceptionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.test; 19 | 20 | import java.io.IOException; 21 | 22 | import org.junit.Assert; 23 | import org.junit.Test; 24 | import org.zeroturnaround.exec.ProcessInitException; 25 | 26 | public class ProcessInitExceptionTest { 27 | 28 | @Test 29 | public void testNull() throws Exception { 30 | Assert.assertNull(ProcessInitException.newInstance(null, new IOException())); 31 | } 32 | 33 | @Test 34 | public void testEmpty() throws Exception { 35 | Assert.assertNull(ProcessInitException.newInstance(null, new IOException(""))); 36 | } 37 | 38 | @Test 39 | public void testSimple() throws Exception { 40 | ProcessInitException e = ProcessInitException.newInstance( 41 | "Could not run test.", new IOException("java.io.IOException: Cannot run program \"ls\": java.io.IOException: error=12, Cannot allocate memory")); 42 | Assert.assertNotNull(e); 43 | Assert.assertEquals("Could not run test. Error=12, Cannot allocate memory", e.getMessage()); 44 | Assert.assertEquals(12, e.getErrorCode()); 45 | } 46 | 47 | @Test 48 | public void testBeforeCode() throws Exception { 49 | ProcessInitException e = ProcessInitException.newInstance( 50 | "Could not run test.", new IOException("java.io.IOException: Cannot run program \"sleep\": java.io.IOException: CreateProcess error=2, The system cannot find the file specified")); 51 | Assert.assertNotNull(e); 52 | Assert.assertEquals("Could not run test. Error=2, The system cannot find the file specified", e.getMessage()); 53 | Assert.assertEquals(2, e.getErrorCode()); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/test/java/org/zeroturnaround/exec/test/ProcessListenerSuccessTest.java: -------------------------------------------------------------------------------- 1 | package org.zeroturnaround.exec.test; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | import org.zeroturnaround.exec.ProcessExecutor; 6 | import org.zeroturnaround.exec.ProcessResult; 7 | import org.zeroturnaround.exec.listener.ProcessListener; 8 | 9 | public class ProcessListenerSuccessTest { 10 | 11 | @Test 12 | public void testJavaVersion() throws Exception { 13 | ProcessListenerImpl listener = new ProcessListenerImpl(); 14 | ProcessResult result = new ProcessExecutor("java", "-version").addListener(listener).execute(); 15 | int exit = result.getExitValue(); 16 | Assert.assertEquals(0, exit); 17 | Assert.assertNotNull(listener.executor); 18 | Assert.assertNotNull(listener.process); 19 | Assert.assertNotNull(listener.result); 20 | Assert.assertEquals(result, listener.result); 21 | } 22 | 23 | static class ProcessListenerImpl extends ProcessListener { 24 | 25 | ProcessExecutor executor; 26 | 27 | Process process; 28 | 29 | ProcessResult result; 30 | 31 | @Override 32 | public void beforeStart(ProcessExecutor executor) { 33 | Assert.assertNotNull(executor); 34 | 35 | Assert.assertNull(this.executor); 36 | Assert.assertNull(this.process); 37 | Assert.assertNull(this.result); 38 | 39 | this.executor = executor; 40 | } 41 | 42 | @Override 43 | public void afterStart(Process process, ProcessExecutor executor) { 44 | Assert.assertNotNull(process); 45 | Assert.assertNotNull(executor); 46 | 47 | Assert.assertNotNull(this.executor); 48 | Assert.assertNull(this.process); 49 | Assert.assertNull(this.result); 50 | 51 | Assert.assertEquals(this.executor, executor); 52 | this.process = process; 53 | } 54 | 55 | @Override 56 | public void afterFinish(Process process, ProcessResult result) { 57 | Assert.assertNotNull(process); 58 | Assert.assertNotNull(result); 59 | 60 | Assert.assertNotNull(this.executor); 61 | Assert.assertNotNull(this.process); 62 | Assert.assertNull(this.result); 63 | 64 | Assert.assertEquals(this.process, process); 65 | this.result = result; 66 | } 67 | 68 | @Override 69 | public void afterStop(Process process) { 70 | Assert.assertNotNull(process); 71 | 72 | Assert.assertNotNull(this.executor); 73 | Assert.assertNotNull(this.process); 74 | Assert.assertNotNull(this.result); 75 | 76 | Assert.assertEquals(this.process, process); 77 | } 78 | 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/test/java/org/zeroturnaround/exec/test/ProcessListenerThrowTest.java: -------------------------------------------------------------------------------- 1 | package org.zeroturnaround.exec.test; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | 5 | import org.junit.Test; 6 | import org.zeroturnaround.exec.InvalidOutputException; 7 | import org.zeroturnaround.exec.ProcessExecutor; 8 | import org.zeroturnaround.exec.ProcessResult; 9 | import org.zeroturnaround.exec.test.ProcessListenerSuccessTest.ProcessListenerImpl; 10 | 11 | public class ProcessListenerThrowTest { 12 | 13 | @Test(expected=InvalidOutputException.class) 14 | public void testJavaVersion() throws Exception { 15 | new ProcessExecutor("java", "-version").readOutput(true).addListener(new ProcessListenerThrowImpl()).execute(); 16 | } 17 | 18 | @Test(expected=InvalidOutputException.class) 19 | public void testJavaVersionWithTimeout() throws Exception { 20 | new ProcessExecutor("java", "-version").readOutput(true).addListener(new ProcessListenerThrowImpl()).timeout(1, TimeUnit.MINUTES).execute(); 21 | } 22 | 23 | private static class ProcessListenerThrowImpl extends ProcessListenerImpl { 24 | 25 | @Override 26 | public void afterFinish(Process process, ProcessResult result) { 27 | super.afterFinish(process, result); 28 | 29 | if (result.getOutput().getString().contains("openjdk version") || result.getOutput().getString().contains("java version")) { 30 | throw new InvalidOutputException("Test", result); 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/org/zeroturnaround/exec/test/RememberCloseOutputStream.java: -------------------------------------------------------------------------------- 1 | package org.zeroturnaround.exec.test; 2 | 3 | import java.io.FilterOutputStream; 4 | import java.io.IOException; 5 | import java.io.OutputStream; 6 | 7 | public class RememberCloseOutputStream extends FilterOutputStream { 8 | 9 | private volatile boolean closed; 10 | 11 | public RememberCloseOutputStream(OutputStream out) { 12 | super(out); 13 | } 14 | 15 | @Override 16 | public void close() throws IOException { 17 | closed = true; 18 | super.close(); 19 | } 20 | 21 | public boolean isClosed() { 22 | return closed; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/test/java/org/zeroturnaround/exec/test/SetFailExecuteStreamHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.test; 19 | 20 | import java.io.IOException; 21 | import java.io.InputStream; 22 | import java.io.OutputStream; 23 | 24 | import org.zeroturnaround.exec.stream.ExecuteStreamHandler; 25 | 26 | 27 | public class SetFailExecuteStreamHandler implements ExecuteStreamHandler { 28 | 29 | public void setProcessInputStream(OutputStream os) throws IOException { 30 | throw new IOException(); 31 | } 32 | 33 | public void setProcessErrorStream(InputStream is) throws IOException { 34 | throw new IOException(); 35 | } 36 | 37 | public void setProcessOutputStream(InputStream is) throws IOException { 38 | throw new IOException(); 39 | } 40 | 41 | public void start() throws IOException { 42 | // do nothing 43 | } 44 | 45 | public void stop() { 46 | // do nothing 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/test/java/org/zeroturnaround/exec/test/shutdown/ProcessExecutorShutdownHookTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.test.shutdown; 19 | 20 | import java.io.File; 21 | 22 | import org.apache.commons.io.FileUtils; 23 | import org.apache.commons.lang3.SystemUtils; 24 | import org.junit.Assert; 25 | import org.junit.Test; 26 | import org.zeroturnaround.exec.ProcessExecutor; 27 | 28 | 29 | /** 30 | * Tests destroying processes on JVM exit. 31 | */ 32 | public class ProcessExecutorShutdownHookTest { 33 | 34 | private static final long SLEEP_FOR_RECHECKING_FILE = 2000; 35 | 36 | @Test 37 | public void testDestroyOnExit() throws Exception { 38 | testDestroyOnExit(WriterLoopStarterBeforeExit.class, true); 39 | } 40 | 41 | @Test 42 | public void testDestroyOnExitInShutdownHook() throws Exception { 43 | testDestroyOnExit(WriterLoopStarterAfterExit.class, false); 44 | } 45 | 46 | private void testDestroyOnExit(Class starter, boolean fileIsAlwaysCreated) throws Exception { 47 | File file = WriterLoop.getFile(); 48 | if (file.exists()) 49 | FileUtils.forceDelete(file); 50 | new ProcessExecutor("java", "-cp", SystemUtils.JAVA_CLASS_PATH, starter.getName()).redirectOutputAsInfo().execute(); 51 | // After WriterLoopStarter has finished we expect that WriterLoop is also finished - no-one is updating the file 52 | if (fileIsAlwaysCreated || file.exists()) { 53 | checkFileStaysTheSame(file); 54 | FileUtils.forceDelete(file); 55 | } 56 | } 57 | 58 | private static void checkFileStaysTheSame(File file) throws InterruptedException { 59 | Assert.assertTrue(file.exists()); 60 | long length = file.length(); 61 | Thread.sleep(SLEEP_FOR_RECHECKING_FILE); 62 | Assert.assertEquals("File '" + file + "' was still updated.", length, file.length()); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/test/java/org/zeroturnaround/exec/test/shutdown/WriterLoop.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.test.shutdown; 19 | 20 | import java.io.File; 21 | import java.io.FileWriter; 22 | import java.io.PrintWriter; 23 | 24 | /** 25 | * Program which regularly writes into a file. 26 | * By checking whether the file gets updates we know whether this program is still running or it's finished. 27 | */ 28 | class WriterLoop { 29 | 30 | private static final File FILE = new File("writeLoop.data"); 31 | 32 | private static final long INTERVAL = 1000; 33 | private static final long COUNT = 10; 34 | 35 | public static File getFile() { 36 | return FILE; 37 | } 38 | 39 | public static void main(String[] args) throws Exception { 40 | PrintWriter out = new PrintWriter(new FileWriter(FILE), true); 41 | try { 42 | out.println("Started"); 43 | for (int i = 0; i < COUNT; i++) { 44 | out.println(i); 45 | Thread.sleep(INTERVAL); 46 | } 47 | out.println("Finished"); 48 | } 49 | finally { 50 | out.close(); 51 | } 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/test/java/org/zeroturnaround/exec/test/shutdown/WriterLoopStarterAfterExit.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.test.shutdown; 19 | 20 | import org.zeroturnaround.exec.ProcessExecutor; 21 | 22 | /** 23 | * Starts {@link WriterLoop} inside shutdown hook and destroys it. 24 | */ 25 | public class WriterLoopStarterAfterExit implements Runnable { 26 | 27 | private static final long SLEEP_AFTER_START = 2000; 28 | 29 | public static void main(String[] args) throws Exception { 30 | Runtime.getRuntime().addShutdownHook(new Thread(new WriterLoopStarterAfterExit())); 31 | // Launch the process and also destroy it 32 | System.exit(0); 33 | } 34 | 35 | public void run() { 36 | try { 37 | new ProcessExecutor("java", "-cp", "target/test-classes", WriterLoop.class.getName()).destroyOnExit().start(); 38 | Thread.sleep(SLEEP_AFTER_START); 39 | } 40 | catch (Exception e) { 41 | e.printStackTrace(); 42 | } 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/org/zeroturnaround/exec/test/shutdown/WriterLoopStarterBeforeExit.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 ZeroTurnaround 3 | * Contains fragments of code from Apache Commons Exec, rights owned 4 | * by Apache Software Foundation (ASF). 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package org.zeroturnaround.exec.test.shutdown; 19 | 20 | import org.zeroturnaround.exec.ProcessExecutor; 21 | 22 | /** 23 | * Starts {@link WriterLoop} and destroys it on JVM exit. 24 | */ 25 | public class WriterLoopStarterBeforeExit { 26 | 27 | private static final long SLEEP_AFTER_START = 2000; 28 | 29 | public static void main(String[] args) throws Exception { 30 | new ProcessExecutor("java", "-cp", "target/test-classes", WriterLoop.class.getName()).destroyOnExit().start(); 31 | Thread.sleep(SLEEP_AFTER_START); 32 | // Cause the launched process to be destroyed 33 | System.exit(0); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/test/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | target/test.log 5 | true 6 | 7 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%logger{0}] %msg%n 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | --------------------------------------------------------------------------------