You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jmeter.apache.org by vl...@apache.org on 2019/10/19 21:54:49 UTC

[jmeter] branch master updated: Rework SystemCommand and OS Process Sampler: avoid throwing InterruptedException on timeout

This is an automated email from the ASF dual-hosted git repository.

vladimirsitnikov pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/jmeter.git


The following commit(s) were added to refs/heads/master by this push:
     new 75011a5  Rework SystemCommand and OS Process Sampler: avoid throwing InterruptedException on timeout
75011a5 is described below

commit 75011a5f91ab89c1fcba31348d1428cdea0ad6b1
Author: Vladimir Sitnikov <si...@gmail.com>
AuthorDate: Sat Oct 19 23:37:53 2019 +0300

    Rework SystemCommand and OS Process Sampler: avoid throwing InterruptedException on timeout
    
    "os command" timeout should not cause thread re-interrupt, so new InterruptedException() pattern should not be used
---
 bin/testfiles/OS_TESTS.csv                         |  2 +-
 bin/testfiles/OS_TESTS.jmx                         |  4 +-
 bin/testfiles/OS_TESTS.xml                         |  2 +-
 .../src/main/java/org/apache/jmeter/JMeter.java    |  4 +-
 .../jmeter/gui/action/HtmlReportGenerator.java     |  6 ++-
 .../java/org/apache/jorphan/exec/KeyToolUtils.java | 11 ++++++
 .../org/apache/jorphan/exec/SystemCommand.java     | 43 ++++++++++------------
 .../jmeter/protocol/system/SystemSampler.java      | 11 ++++--
 xdocs/usermanual/properties_reference.xml          |  7 ----
 9 files changed, 48 insertions(+), 42 deletions(-)

diff --git a/bin/testfiles/OS_TESTS.csv b/bin/testfiles/OS_TESTS.csv
index c180fc4..8f0b59b 100644
--- a/bin/testfiles/OS_TESTS.csv
+++ b/bin/testfiles/OS_TESTS.csv
@@ -1,4 +1,4 @@
 label,responseCode,responseMessage,threadName,dataType,success,grpThreads,allThreads,SampleCount,ErrorCount
 OSPS-listDirectory,0,OK,Thread Group 1-1,text,true,1,1,1,0
 OSPS-listEnv,0,OK,Thread Group 1-1,text,true,1,1,1,0
-OSPS-sleep,500,System Sampler interrupted whilst executing system call: java.lang.InterruptedException: Process timeout out after 1000 milliseconds,Thread Group 1-1,text,true,1,1,1,0
+OSPS-sleep,500,Process timeout reached after 1000 milliseconds,Thread Group 1-1,text,true,1,1,1,0
diff --git a/bin/testfiles/OS_TESTS.jmx b/bin/testfiles/OS_TESTS.jmx
index 65f0c87..e032a3a 100644
--- a/bin/testfiles/OS_TESTS.jmx
+++ b/bin/testfiles/OS_TESTS.jmx
@@ -136,7 +136,7 @@
           <hashTree>
             <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true">
               <collectionProp name="Asserion.test_strings">
-                <stringProp name="-57279347">Process timeout out after 1000 milliseconds</stringProp>
+                <stringProp name="-57279347">Process timeout reached after 1000 milliseconds</stringProp>
               </collectionProp>
               <stringProp name="Assertion.test_field">Assertion.response_message</stringProp>
               <boolProp name="Assertion.assume_success">true</boolProp>
@@ -293,7 +293,7 @@
           <hashTree>
             <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true">
               <collectionProp name="Asserion.test_strings">
-                <stringProp name="-57279347">Process timeout out after 1000 milliseconds</stringProp>
+                <stringProp name="-57279347">Process timeout reached after 1000 milliseconds</stringProp>
               </collectionProp>
               <stringProp name="Assertion.test_field">Assertion.response_message</stringProp>
               <boolProp name="Assertion.assume_success">true</boolProp>
diff --git a/bin/testfiles/OS_TESTS.xml b/bin/testfiles/OS_TESTS.xml
index 49171ff..fecf861 100644
--- a/bin/testfiles/OS_TESTS.xml
+++ b/bin/testfiles/OS_TESTS.xml
@@ -19,7 +19,7 @@
     <error>false</error>
   </assertionResult>
 </sample>
-<sample s="true" lb="OSPS-sleep" rc="500" rm="System Sampler interrupted whilst executing system call: java.lang.InterruptedException: Process timeout out after 1000 milliseconds" tn="Thread Group 1-1" dt="text" sc="1" ec="0" ng="1" na="1">
+<sample s="true" lb="OSPS-sleep" rc="500" rm="Process timeout reached after 1000 milliseconds" tn="Thread Group 1-1" dt="text" sc="1" ec="0" ng="1" na="1">
   <assertionResult>
     <name>Response Assertion</name>
     <failure>false</failure>
diff --git a/src/core/src/main/java/org/apache/jmeter/JMeter.java b/src/core/src/main/java/org/apache/jmeter/JMeter.java
index 6113836..7fcf271 100644
--- a/src/core/src/main/java/org/apache/jmeter/JMeter.java
+++ b/src/core/src/main/java/org/apache/jmeter/JMeter.java
@@ -463,8 +463,8 @@ public class JMeter implements JMeterPlugin {
             Thread.setDefaultUncaughtExceptionHandler(
                     (Thread t, Throwable e) -> {
                     if (!(e instanceof ThreadDeath)) {
-                        log.error("Uncaught exception: ", e);
-                        System.err.println("Uncaught Exception " + e + ". See log file for details.");//NOSONAR
+                        log.error("Uncaught exception in thread " + t, e);
+                        System.err.println("Uncaught Exception " + e + " in thread " + t + ". See log file for details.");//NOSONAR
                     }
             });
 
diff --git a/src/core/src/main/java/org/apache/jmeter/gui/action/HtmlReportGenerator.java b/src/core/src/main/java/org/apache/jmeter/gui/action/HtmlReportGenerator.java
index b781270..6035e51 100644
--- a/src/core/src/main/java/org/apache/jmeter/gui/action/HtmlReportGenerator.java
+++ b/src/core/src/main/java/org/apache/jmeter/gui/action/HtmlReportGenerator.java
@@ -24,6 +24,7 @@ import java.io.IOException;
 import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.TimeoutException;
 
 import org.apache.jmeter.util.JMeterUtils;
 import org.apache.jorphan.exec.SystemCommand;
@@ -87,9 +88,12 @@ public class HtmlReportGenerator {
                 LOGGER.info("The HTML report generation failed and returned: {}", commandExecutionOutput);
                 return errorMessageList;
             }
-        } catch (InterruptedException | IOException e) {
+        } catch (InterruptedException | TimeoutException | IOException e) {
             errorMessageList.add(commandExecutionOutput.toString());
             LOGGER.error("Error during HTML report generation:", e);
+            if (e instanceof InterruptedException) {
+                Thread.currentThread().interrupt();
+            }
         }
         LOGGER.debug("SystemCommand ran: {}  returned: {}", generationCommand, resultCode);
         return errorMessageList;
diff --git a/src/jorphan/src/main/java/org/apache/jorphan/exec/KeyToolUtils.java b/src/jorphan/src/main/java/org/apache/jorphan/exec/KeyToolUtils.java
index df8d63c..679e72d 100644
--- a/src/jorphan/src/main/java/org/apache/jorphan/exec/KeyToolUtils.java
+++ b/src/jorphan/src/main/java/org/apache/jorphan/exec/KeyToolUtils.java
@@ -27,6 +27,7 @@ import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.TimeoutException;
 
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.lang3.SystemUtils;
@@ -173,7 +174,10 @@ public class KeyToolUtils {
                     + "\n'" + formatCommand(arguments)+"'");
             }
         } catch (InterruptedException e) { // NOSONAR
+            Thread.currentThread().interrupt();
             throw new IOException("Command was interrupted\n" + nativeCommand.getOutResult(), e);
+        } catch (TimeoutException e) { // NOSONAR
+            throw new IOException("Timeout reached while executing\n" + nativeCommand.getOutResult(), e);
         }
     }
 
@@ -369,7 +373,10 @@ public class KeyToolUtils {
                 throw new IOException("Command failed, code: " + exitVal + "\n" + nativeCommand.getOutResult());
             }
         } catch (InterruptedException e) { // NOSONAR
+            Thread.currentThread().interrupt();
             throw new IOException("Command was interrupted\n" + nativeCommand.getOutResult(), e);
+        } catch (TimeoutException e) { // NOSONAR
+            throw new IOException("Timeout reached while executing\n" + nativeCommand.getOutResult(), e);
         }
     }
 
@@ -480,6 +487,10 @@ public class KeyToolUtils {
             return false;
         } catch (InterruptedException e) { // NOSONAR
             log.error("Command was interrupted\n" + nativeCommand.getOutResult(), e);
+            Thread.currentThread().interrupt();
+            return false;
+        } catch (TimeoutException e) { // NOSONAR
+            log.info("Timeout reached while checking for keytool existence, will return false, try another way.", e);
             return false;
         }
     }
diff --git a/src/jorphan/src/main/java/org/apache/jorphan/exec/SystemCommand.java b/src/jorphan/src/main/java/org/apache/jorphan/exec/SystemCommand.java
index ca58c00..1814edc 100644
--- a/src/jorphan/src/main/java/org/apache/jorphan/exec/SystemCommand.java
+++ b/src/jorphan/src/main/java/org/apache/jorphan/exec/SystemCommand.java
@@ -30,6 +30,8 @@ import java.io.OutputStream;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
 import org.apache.jorphan.util.JOrphanUtils;
 
@@ -37,6 +39,10 @@ import org.apache.jorphan.util.JOrphanUtils;
  * Utility class for invoking native system applications
  */
 public class SystemCommand {
+    /**
+     * @deprecated does not make sense anymore. Superceded by {@link Process#wait(long)}
+     */
+    @Deprecated
     public static final int POLL_INTERVAL = 100;
     private final File directory;
     private final Map<String, String> env;
@@ -53,7 +59,7 @@ public class SystemCommand {
      * @param directory File working directory (may be null)
      */
     public SystemCommand(File directory, Map<String, String> env) {
-        this(directory, 0L, POLL_INTERVAL, env, (InputStream) null, (OutputStream) null, (OutputStream) null);
+        this(directory, 0L, 0, env, (InputStream) null, (OutputStream) null, (OutputStream) null);
     }
 
     /**
@@ -123,13 +129,13 @@ public class SystemCommand {
      * @throws InterruptedException when execution was interrupted
      * @throws IOException when I/O error occurs while execution
      */
-    public int run(List<String> arguments) throws InterruptedException, IOException {
+    public int run(List<String> arguments) throws InterruptedException, IOException, TimeoutException {
         return run(arguments, stdin, stdout, stderr);
     }
 
     // helper method to allow input and output to be changed for chaining
     private int run(List<String> arguments, InputStream in,
-            OutputStream out,OutputStream err) throws InterruptedException, IOException {
+            OutputStream out,OutputStream err) throws InterruptedException, IOException, TimeoutException {
         Process proc = null;
         final ProcessBuilder procBuild = new ProcessBuilder(arguments);
         if (env != null) {
@@ -199,13 +205,14 @@ public class SystemCommand {
      * @param arguments2 second command to run
      * @return exit status
      * @throws InterruptedException when execution gets interrupted
+     * @throws TimeoutException when timeout is reached while execution
      * @throws IOException when I/O error occurs while execution
      */
-    public int run(List<String> arguments1, List<String> arguments2) throws InterruptedException, IOException {
+    public int run(List<String> arguments1, List<String> arguments2) throws InterruptedException, IOException, TimeoutException {
         ByteArrayOutputStream out = new ByteArrayOutputStream(); // capture the intermediate output
-        int exitCode=run(arguments1,stdin,out, stderr);
+        int exitCode = run(arguments1, stdin, out, stderr);
         if (exitCode == 0) {
-            exitCode = run(arguments2,new ByteArrayInputStream(out.toByteArray()),stdout,stderr);
+            exitCode = run(arguments2, new ByteArrayInputStream(out.toByteArray()), stdout, stderr);
         }
         return exitCode;
     }
@@ -215,28 +222,16 @@ public class SystemCommand {
      * @param proc Process
      * @param timeoutInMillis long timeout in ms
      * @return proc exit value
-     * @throws InterruptedException
+     * @throws TimeoutException when timeout is reached while execution
      */
-    private int waitForEndWithTimeout(Process proc, long timeoutInMillis) throws InterruptedException {
+    private int waitForEndWithTimeout(Process proc, long timeoutInMillis) throws InterruptedException, TimeoutException {
         if (timeoutInMillis <= 0L) {
             return proc.waitFor();
-        } else {
-            long now = System.currentTimeMillis();
-            long finish = now + timeoutInMillis;
-            while(System.currentTimeMillis() < finish) {
-                try {
-                    return proc.exitValue();
-                } catch (IllegalThreadStateException e) { // not yet terminated
-                    Thread.sleep(pollInterval);
-                }
-            }
-            try {
-                return proc.exitValue();
-            } catch (IllegalThreadStateException e) { // not yet terminated
-                // N.B. proc.destroy() is called by the finally clause in the run() method
-                throw new InterruptedException( "Process timeout out after " + timeoutInMillis + " milliseconds" );
-            }
         }
+        if (proc.waitFor(timeoutInMillis, TimeUnit.MILLISECONDS)) {
+            return proc.exitValue();
+        }
+        throw new TimeoutException("Process timeout reached after " + timeoutInMillis + " milliseconds");
     }
 
     /**
diff --git a/src/protocol/native/src/main/java/org/apache/jmeter/protocol/system/SystemSampler.java b/src/protocol/native/src/main/java/org/apache/jmeter/protocol/system/SystemSampler.java
index 1cd55de..5062537 100644
--- a/src/protocol/native/src/main/java/org/apache/jmeter/protocol/system/SystemSampler.java
+++ b/src/protocol/native/src/main/java/org/apache/jmeter/protocol/system/SystemSampler.java
@@ -27,6 +27,7 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.TimeoutException;
 
 import org.apache.commons.lang3.StringUtils;
 import org.apache.jmeter.config.Argument;
@@ -38,7 +39,6 @@ import org.apache.jmeter.samplers.SampleResult;
 import org.apache.jmeter.services.FileServer;
 import org.apache.jmeter.testelement.TestElement;
 import org.apache.jmeter.testelement.property.TestElementProperty;
-import org.apache.jmeter.util.JMeterUtils;
 import org.apache.jorphan.exec.SystemCommand;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -48,8 +48,6 @@ import org.slf4j.LoggerFactory;
  */
 public class SystemSampler extends AbstractSampler {
 
-    private static final int POLL_INTERVAL = JMeterUtils.getPropDefault("os_sampler.poll_for_timeout", SystemCommand.POLL_INTERVAL);
-
     private static final long serialVersionUID = 1;
 
     // + JMX names, do not change their values
@@ -151,7 +149,7 @@ public class SystemSampler extends AbstractSampler {
 
         SystemCommand nativeCommand = null;
         try {
-            nativeCommand = new SystemCommand(directory, getTimeout(), POLL_INTERVAL, env, getStdin(), getStdout(), getStderr());
+            nativeCommand = new SystemCommand(directory, getTimeout(), 0, env, getStdin(), getStdout(), getStderr());
             results.sampleStart();
             int returnCode = nativeCommand.run(cmds);
             results.sampleEnd();
@@ -179,6 +177,11 @@ public class SystemSampler extends AbstractSampler {
             results.setResponseCode("500"); //$NON-NLS-1$
             results.setResponseMessage("System Sampler interrupted whilst executing system call: " + ie);
             Thread.currentThread().interrupt();
+        } catch (TimeoutException e) {
+            results.sampleEnd();
+            results.setSuccessful(false);
+            results.setResponseCode("500");
+            results.setResponseMessage(e.getMessage());
         }
 
         if (nativeCommand != null) {
diff --git a/xdocs/usermanual/properties_reference.xml b/xdocs/usermanual/properties_reference.xml
index 87994c8..e65a85f 100644
--- a/xdocs/usermanual/properties_reference.xml
+++ b/xdocs/usermanual/properties_reference.xml
@@ -1031,13 +1031,6 @@ JMETER-SERVER</source>
 </properties>
 </section>
 <section name="&sect-num;.24 OS Process Sampler configuration" anchor="os_sampler">
-<properties>
-<property name="os_sampler.poll_for_timeout">
-    Polling to see if process has finished its work, used when a timeout
-    is configured on sampler. Specified in milliseconds.<br/>
-    Defaults to: <code>100</code>
-</property>
-</properties>
 </section>
 <section name="&sect-num;.25 TCP Sampler configuration" anchor="tcp_sampler">
 <properties>