Use isEmpty().
Add ShutdownHookProcessDestroyer.isEmpty().
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 2e5af39..06429ab 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -46,6 +46,9 @@
<action dev="ggregory" type="update" due-to="nullptr">
Java-style Array declaration and remove empty finally block #26.
</action>
+ <action dev="ggregory" type="add" due-to="Gary Gregory">
+ Add ShutdownHookProcessDestroyer.isEmpty().
+ </action>
</release>
<release version="1.3" date="2014-11-02" description="Maintenance and feature Release">
<action issue="EXEC-69" dev="ggregory" type="add" due-to="Richard Atkins, Michael Vorburger">
diff --git a/src/main/java/org/apache/commons/exec/LogOutputStream.java b/src/main/java/org/apache/commons/exec/LogOutputStream.java
index eb606d0..80f78b8 100644
--- a/src/main/java/org/apache/commons/exec/LogOutputStream.java
+++ b/src/main/java/org/apache/commons/exec/LogOutputStream.java
@@ -21,6 +21,7 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
+import java.nio.charset.Charset;
/**
* Base class to connect a logging system to the output and/or
@@ -32,6 +33,16 @@
public abstract class LogOutputStream
extends OutputStream {
+ private static final class ByteArrayOutputStreamX extends ByteArrayOutputStream {
+ private ByteArrayOutputStreamX(final int size) {
+ super(size);
+ }
+
+ public synchronized String toString(final Charset charset) {
+ return new String(buf, 0, count, charset);
+ }
+ }
+
/** Initial buffer size. */
private static final int INTIAL_SIZE = 132;
@@ -42,13 +53,14 @@
private static final int LF = 0x0a;
/** the internal buffer */
- private final ByteArrayOutputStream buffer = new ByteArrayOutputStream(
- INTIAL_SIZE);
+ private final ByteArrayOutputStreamX buffer = new ByteArrayOutputStreamX(INTIAL_SIZE);
private boolean skip = false;
private final int level;
+ private final Charset charset;
+
/**
* Creates a new instance of this class.
* Uses the default level of 999.
@@ -63,7 +75,19 @@
* @param level loglevel used to log data written to this stream.
*/
public LogOutputStream(final int level) {
+ this(level, null);
+ }
+
+ /**
+ * Creates a new instance of this class, specifying the character set that should be used for
+ * outputting the string for each line
+ *
+ * @param level loglevel used to log data written to this stream
+ * @param charset Character Set to use when processing lines
+ */
+ public LogOutputStream(final int level, final Charset charset) {
this.level = level;
+ this.charset = charset == null ? Charset.defaultCharset() : charset;
}
/**
@@ -157,7 +181,7 @@
* Converts the buffer to a string and sends it to {@code processLine}.
*/
protected void processBuffer() {
- processLine(buffer.toString());
+ processLine(buffer.toString(charset));
buffer.reset();
}
diff --git a/src/main/java/org/apache/commons/exec/ShutdownHookProcessDestroyer.java b/src/main/java/org/apache/commons/exec/ShutdownHookProcessDestroyer.java
index 13d94a7..c47d807 100644
--- a/src/main/java/org/apache/commons/exec/ShutdownHookProcessDestroyer.java
+++ b/src/main/java/org/apache/commons/exec/ShutdownHookProcessDestroyer.java
@@ -27,20 +27,6 @@
*/
public class ShutdownHookProcessDestroyer implements ProcessDestroyer, Runnable {
- /** the list of currently running processes */
- private final Vector<Process> processes = new Vector<>();
-
- /** The thread registered at the JVM to execute the shutdown handler */
- private ProcessDestroyerImpl destroyProcessThread = null;
-
- /** Whether or not this ProcessDestroyer has been registered as a shutdown hook */
- private boolean added = false;
-
- /**
- * Whether or not this ProcessDestroyer is currently running as shutdown hook
- */
- private volatile boolean running = false;
-
private class ProcessDestroyerImpl extends Thread {
private boolean shouldDestroy = true;
@@ -61,22 +47,51 @@
}
}
+ /** the list of currently running processes */
+ private final Vector<Process> processes = new Vector<>();
+
+ /** The thread registered at the JVM to execute the shutdown handler */
+ private ProcessDestroyerImpl destroyProcessThread = null;
+
+ /** Whether or not this ProcessDestroyer has been registered as a shutdown hook */
+ private boolean added = false;
+
/**
- * Constructs a {@code ProcessDestroyer} and obtains
- * {@code Runtime.addShutdownHook()} and
- * {@code Runtime.removeShutdownHook()} through reflection. The
- * ProcessDestroyer manages a list of processes to be destroyed when the VM
- * exits. If a process is added when the list is empty, this
- * {@code ProcessDestroyer} is registered as a shutdown hook. If
- * removing a process results in an empty list, the
- * {@code ProcessDestroyer} is removed as a shutdown hook.
+ * Whether or not this ProcessDestroyer is currently running as shutdown hook
+ */
+ private volatile boolean running = false;
+
+ /**
+ * Constructs a {@code ProcessDestroyer} and obtains {@code Runtime.addShutdownHook()} and
+ * {@code Runtime.removeShutdownHook()} through reflection. The ProcessDestroyer manages a list of processes to be
+ * destroyed when the VM exits. If a process is added when the list is empty, this {@code ProcessDestroyer} is
+ * registered as a shutdown hook. If removing a process results in an empty list, the {@code ProcessDestroyer} is
+ * removed as a shutdown hook.
*/
public ShutdownHookProcessDestroyer() {
}
/**
- * Registers this {@code ProcessDestroyer} as a shutdown hook, uses
- * reflection to ensure pre-JDK 1.3 compatibility.
+ * Returns {@code true} if the specified {@code Process} was successfully added to the list of processes to destroy
+ * upon VM exit.
+ *
+ * @param process the process to add
+ * @return {@code true} if the specified {@code Process} was successfully added
+ */
+ @Override
+ public boolean add(final Process process) {
+ synchronized (processes) {
+ // if this list is empty, register the shutdown hook
+ if (processes.isEmpty()) {
+ addShutdownHook();
+ }
+ processes.addElement(process);
+ return processes.contains(process);
+ }
+ }
+
+ /**
+ * Registers this {@code ProcessDestroyer} as a shutdown hook, uses reflection to ensure pre-JDK 1.3 compatibility.
*/
private void addShutdownHook() {
if (!running) {
@@ -87,20 +102,54 @@
}
/**
- * Removes this {@code ProcessDestroyer} as a shutdown hook, uses
- * reflection to ensure pre-JDK 1.3 compatibility
+ * Returns whether or not the ProcessDestroyer is registered as as shutdown hook
+ *
+ * @return true if this is currently added as shutdown hook
+ */
+ public boolean isAddedAsShutdownHook() {
+ return added;
+ }
+
+ /**
+ * Tests emptiness (size == 0).
+ *
+ * @return Whether or not this is empty.
+ * @since 1.4.0
+ */
+ public boolean isEmpty() {
+ return size() == 0;
+ }
+
+ /**
+ * Returns {@code true} if the specified {@code Process} was successfully removed from the list of processes to
+ * destroy upon VM exit.
+ *
+ * @param process the process to remove
+ * @return {@code true} if the specified {@code Process} was successfully removed
+ */
+ @Override
+ public boolean remove(final Process process) {
+ synchronized (processes) {
+ final boolean processRemoved = processes.removeElement(process);
+ if (processRemoved && processes.isEmpty()) {
+ removeShutdownHook();
+ }
+ return processRemoved;
+ }
+ }
+
+ /**
+ * Removes this {@code ProcessDestroyer} as a shutdown hook, uses reflection to ensure pre-JDK 1.3 compatibility
*/
private void removeShutdownHook() {
if (added && !running) {
- final boolean removed = Runtime.getRuntime().removeShutdownHook(
- destroyProcessThread);
+ final boolean removed = Runtime.getRuntime().removeShutdownHook(destroyProcessThread);
if (!removed) {
System.err.println("Could not remove shutdown hook");
}
/*
- * start the hook thread, a unstarted thread may not be eligible for
- * garbage collection Cf.: http://developer.java.sun.com/developer/
- * bugParade/bugs/4533087.html
+ * start the hook thread, a unstarted thread may not be eligible for garbage collection Cf.:
+ * http://developer.java.sun.com/developer/ bugParade/bugs/4533087.html
*/
destroyProcessThread.setShouldDestroy(false);
@@ -118,83 +167,31 @@
}
/**
- * Returns whether or not the ProcessDestroyer is registered as as shutdown
- * hook
- *
- * @return true if this is currently added as shutdown hook
- */
- public boolean isAddedAsShutdownHook() {
- return added;
- }
-
- /**
- * Returns {@code true} if the specified {@code Process} was
- * successfully added to the list of processes to destroy upon VM exit.
- *
- * @param process
- * the process to add
- * @return {@code true} if the specified {@code Process} was
- * successfully added
- */
- @Override
- public boolean add(final Process process) {
- synchronized (processes) {
- // if this list is empty, register the shutdown hook
- if (processes.size() == 0) {
- addShutdownHook();
- }
- processes.addElement(process);
- return processes.contains(process);
- }
- }
-
- /**
- * Returns {@code true} if the specified {@code Process} was
- * successfully removed from the list of processes to destroy upon VM exit.
- *
- * @param process
- * the process to remove
- * @return {@code true} if the specified {@code Process} was
- * successfully removed
- */
- @Override
- public boolean remove(final Process process) {
- synchronized (processes) {
- final boolean processRemoved = processes.removeElement(process);
- if (processRemoved && processes.size() == 0) {
- removeShutdownHook();
- }
- return processRemoved;
- }
- }
-
- /**
- * Returns the number of registered processes.
- *
- * @return the number of register process
- */
- @Override
-public int size() {
- return processes.size();
- }
-
- /**
* Invoked by the VM when it is exiting.
*/
- @Override
-public void run() {
- synchronized (processes) {
- running = true;
- final Enumeration<Process> e = processes.elements();
- while (e.hasMoreElements()) {
- final Process process = e.nextElement();
- try {
- process.destroy();
- }
- catch (final Throwable t) {
- System.err.println("Unable to terminate process during process shutdown");
- }
- }
- }
- }
+ @Override
+ public void run() {
+ synchronized (processes) {
+ running = true;
+ final Enumeration<Process> e = processes.elements();
+ while (e.hasMoreElements()) {
+ final Process process = e.nextElement();
+ try {
+ process.destroy();
+ } catch (final Throwable t) {
+ System.err.println("Unable to terminate process during process shutdown");
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the number of registered processes.
+ *
+ * @return the number of register process
+ */
+ @Override
+ public int size() {
+ return processes.size();
+ }
}
diff --git a/src/main/java/org/apache/commons/exec/util/MapUtils.java b/src/main/java/org/apache/commons/exec/util/MapUtils.java
index 57901c8..dbe3547 100644
--- a/src/main/java/org/apache/commons/exec/util/MapUtils.java
+++ b/src/main/java/org/apache/commons/exec/util/MapUtils.java
@@ -98,10 +98,10 @@
Map<K, V> result = null;
- if (lhs == null || lhs.size() == 0) {
+ if (lhs == null || lhs.isEmpty()) {
result = copy(rhs);
}
- else if (rhs == null || rhs.size() == 0) {
+ else if (rhs == null || rhs.isEmpty()) {
result = copy(lhs);
}
else {
diff --git a/src/main/java/org/apache/commons/exec/util/StringUtils.java b/src/main/java/org/apache/commons/exec/util/StringUtils.java
index 7eaeb7d..b8fb1e9 100644
--- a/src/main/java/org/apache/commons/exec/util/StringUtils.java
+++ b/src/main/java/org/apache/commons/exec/util/StringUtils.java
@@ -74,7 +74,7 @@
return argBuf;
}
- if (vars == null || vars.size() == 0) {
+ if (vars == null || vars.isEmpty()) {
return argBuf.append(argStr);
}
diff --git a/src/site/apt/tutorial.apt b/src/site/apt/tutorial.apt
index c059ada..6038063 100644
--- a/src/site/apt/tutorial.apt
+++ b/src/site/apt/tutorial.apt
@@ -54,7 +54,7 @@
You successfully printed your first PDF document but at the end an exception is thrown - what
happend? Oops, Acrobat Reader returned an exit value of '1' on success which is usually
- considered as an execution failure. So we have to tweak our code to fix this odd behaviour -
+ considered as an execution failure. So we have to tweak our code to fix this odd behavior -
we define the exit value of "1" to be considered as successful execution.
+----------------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/exec/DefaultExecutorTest.java b/src/test/java/org/apache/commons/exec/DefaultExecutorTest.java
index 787393f..896de1b 100644
--- a/src/test/java/org/apache/commons/exec/DefaultExecutorTest.java
+++ b/src/test/java/org/apache/commons/exec/DefaultExecutorTest.java
@@ -472,14 +472,14 @@
final ShutdownHookProcessDestroyer processDestroyer = new ShutdownHookProcessDestroyer();
exec.setProcessDestroyer(processDestroyer);
- assertTrue(processDestroyer.size() == 0);
+ assertTrue(processDestroyer.isEmpty());
assertTrue(processDestroyer.isAddedAsShutdownHook() == false);
final int exitValue = exec.execute(cl);
assertEquals("FOO..", baos.toString().trim());
assertFalse(exec.isFailure(exitValue));
- assertTrue(processDestroyer.size() == 0);
+ assertTrue(processDestroyer.isEmpty());
assertTrue(processDestroyer.isAddedAsShutdownHook() == false);
}
@@ -499,7 +499,7 @@
final ExecuteWatchdog watchdog = new ExecuteWatchdog(Integer.MAX_VALUE);
assertTrue(exec.getProcessDestroyer() == null);
- assertTrue(processDestroyer.size() == 0);
+ assertTrue(processDestroyer.isEmpty());
assertTrue(processDestroyer.isAddedAsShutdownHook() == false);
exec.setWatchdog(watchdog);
diff --git a/src/test/java/org/apache/commons/exec/LogOutputStreamTest.java b/src/test/java/org/apache/commons/exec/LogOutputStreamTest.java
index 8e7403e..8f578d2 100644
--- a/src/test/java/org/apache/commons/exec/LogOutputStreamTest.java
+++ b/src/test/java/org/apache/commons/exec/LogOutputStreamTest.java
@@ -17,10 +17,12 @@
*/
package org.apache.commons.exec;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import java.io.File;
import java.io.OutputStream;
+import java.nio.charset.Charset;
import org.junit.After;
import org.junit.Before;
@@ -37,6 +39,7 @@
private final File testDir = new File("src/test/scripts");
private OutputStream systemOut;
private final File environmentScript = TestUtil.resolveScriptForOS(testDir + "/environment");
+ private final File utf8CharacterScript = TestUtil.resolveScriptForOS(testDir + "/utf8Characters");
@BeforeClass
public static void classSetUp() {
@@ -47,13 +50,14 @@
@Before
public void setUp() throws Exception {
- this.systemOut = new SystemLogOutputStream(1);
- this.exec.setStreamHandler(new PumpStreamHandler(systemOut, systemOut));
+
}
@After
public void tearDown() throws Exception {
- this.systemOut.close();
+ if (this.systemOut != null) {
+ this.systemOut.close();
+ }
}
// ======================================================================
@@ -62,24 +66,49 @@
@Test
public void testStdout() throws Exception {
+ this.systemOut = new SystemLogOutputStream(1);
+ this.exec.setStreamHandler(new PumpStreamHandler(systemOut, systemOut));
+
final CommandLine cl = new CommandLine(environmentScript);
final int exitValue = exec.execute(cl);
assertFalse(exec.isFailure(exitValue));
}
+ @Test
+ public void testStdoutWithUTF8Characters() throws Exception {
+ this.systemOut = new SystemLogOutputStream(1, Charset.forName("UTF-8"));
+ this.exec.setStreamHandler(new PumpStreamHandler(systemOut, systemOut));
+
+ final CommandLine cl = new CommandLine(utf8CharacterScript);
+ final int exitValue = exec.execute(cl);
+ assertFalse(exec.isFailure(exitValue));
+ assertEquals("This string contains UTF-8 characters like the see no evil monkey \uD83D\uDE48 and the right single quotation mark \u2019", ((SystemLogOutputStream) systemOut).getOutput());
+ }
+
// ======================================================================
// Helper classes
// ======================================================================
private class SystemLogOutputStream extends LogOutputStream {
+ StringBuffer output = new StringBuffer();
+
private SystemLogOutputStream(final int level) {
super(level);
}
+ private SystemLogOutputStream(final int level, final Charset charset) {
+ super(level, charset);
+ }
+
@Override
protected void processLine(final String line, final int level) {
System.out.println(line);
+ output.append(line);
+ }
+
+ private String getOutput() {
+ return output.toString();
}
}
diff --git a/src/test/java/org/apache/commons/exec/util/MapUtilTest.java b/src/test/java/org/apache/commons/exec/util/MapUtilTest.java
index af1a54d..053e7b6 100644
--- a/src/test/java/org/apache/commons/exec/util/MapUtilTest.java
+++ b/src/test/java/org/apache/commons/exec/util/MapUtilTest.java
@@ -45,7 +45,7 @@
assertEquals("/usr/opt/java", result.get("JAVA_HOME"));
result.remove("JAVA_HOME");
- assertTrue(result.size() == 0);
+ assertTrue(result.isEmpty());
assertTrue(procEnvironment.size() == 1);
}