View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * 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, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.exec.issues;
19  
20  import static org.junit.jupiter.api.Assertions.assertTrue;
21  
22  import java.io.File;
23  
24  import org.apache.commons.exec.CommandLine;
25  import org.apache.commons.exec.DefaultExecutor;
26  import org.apache.commons.exec.ExecuteException;
27  import org.apache.commons.exec.ExecuteWatchdog;
28  import org.apache.commons.exec.OS;
29  import org.apache.commons.exec.PumpStreamHandler;
30  import org.apache.commons.exec.TestUtil;
31  import org.junit.jupiter.api.Test;
32  
33  /**
34   * Test the patch for EXEC-41 (https://issues.apache.org/jira/browse/EXEC-41).
35   */
36  public class Exec41Test {
37  
38      private final File testDir = new File("src/test/scripts");
39      private final File pingScript = TestUtil.resolveScriptForOS(testDir + "/ping");
40  
41      /**
42       * Test EXEC-41 with a disabled PumpStreamHandler to check if we could return immediately after killing the process (no streams implies no blocking stream
43       * pumper threads). But you have to be 100% sure that the subprocess is not writing to 'stdout' and 'stderr'.
44       *
45       * For this test we are using the batch file - under Windows the 'ping' process can't be killed (not supported by Win32) and will happily run the given time
46       * (e.g. 10 seconds) even hwen the batch file is already killed.
47       *
48       * @throws Exception the test failed
49       */
50      @Test
51      public void testExec41WithoutStreams() throws Exception {
52  
53          final CommandLine cmdLine = new CommandLine(pingScript);
54          cmdLine.addArgument("10"); // sleep 10 seconds
55          final DefaultExecutor executor = DefaultExecutor.builder().get();
56          final ExecuteWatchdog watchdog = new ExecuteWatchdog(2 * 1000); // allow process no more than 2 seconds
57  
58          // create a custom "PumpStreamHandler" doing no pumping at all
59          final PumpStreamHandler pumpStreamHandler = new PumpStreamHandler(null, null, null);
60  
61          executor.setWatchdog(watchdog);
62          executor.setStreamHandler(pumpStreamHandler);
63  
64          final long startTime = System.currentTimeMillis();
65  
66          try {
67              executor.execute(cmdLine);
68          } catch (final ExecuteException e) {
69              System.out.println(e);
70          }
71  
72          final long duration = System.currentTimeMillis() - startTime;
73  
74          System.out.println("Process completed in " + duration + " millis; below is its output");
75  
76          if (watchdog.killedProcess()) {
77              System.out.println("Process timed out and was killed.");
78          }
79  
80          assertTrue(watchdog.killedProcess(), "The process was killed by the watchdog");
81          assertTrue(duration < 9000, () -> "Skipping the Thread.join() did not work, duration=" + duration);
82      }
83  
84      /**
85       *
86       * When a process runs longer than allowed by a configured watchdog's timeout, the watchdog tries to destroy it and then DefaultExecutor tries to clean up
87       * by joining with all installed pump stream threads. Problem is, that sometimes the native process doesn't die and thus streams aren't closed and the
88       * stream threads do not complete.
89       *
90       * @throws Exception the test failed
91       */
92      @Test
93      public void testExec41WithStreams() throws Exception {
94  
95          CommandLine cmdLine;
96  
97          if (OS.isFamilyWindows()) {
98              cmdLine = CommandLine.parse("ping.exe -n 10 -w 1000 127.0.0.1");
99          } else if ("HP-UX".equals(System.getProperty("os.name"))) {
100             // see EXEC-52 - option must appear after the hostname!
101             cmdLine = CommandLine.parse("ping 127.0.0.1 -n 10");
102         } else if (OS.isFamilyUnix()) {
103             cmdLine = CommandLine.parse("ping -c 10 127.0.0.1");
104         } else {
105             System.err.println("The test 'testExec41WithStreams' does not support the following OS : " + System.getProperty("os.name"));
106             return;
107         }
108 
109         final DefaultExecutor executor = DefaultExecutor.builder().get();
110         final ExecuteWatchdog watchdog = new ExecuteWatchdog(2 * 1000); // allow process no more than 2 seconds
111         final PumpStreamHandler pumpStreamHandler = new PumpStreamHandler(System.out, System.err);
112         // this method was part of the patch I reverted
113         // pumpStreamHandler.setAlwaysWaitForStreamThreads(false);
114 
115         executor.setWatchdog(watchdog);
116         executor.setStreamHandler(pumpStreamHandler);
117 
118         final long startTime = System.currentTimeMillis();
119 
120         try {
121             executor.execute(cmdLine);
122         } catch (final ExecuteException e) {
123             // nothing to do
124         }
125 
126         final long duration = System.currentTimeMillis() - startTime;
127 
128         System.out.println("Process completed in " + duration + " millis; below is its output");
129 
130         if (watchdog.killedProcess()) {
131             System.out.println("Process timed out and was killed by watchdog.");
132         }
133 
134         assertTrue(watchdog.killedProcess(), "The process was killed by the watchdog");
135         assertTrue(duration < 9000, "Skipping the Thread.join() did not work");
136     }
137 }