Permalink
Browse files

Enhanced ProcessWrapperLiteImpl to run a command and return the results

This is a breaking change.
  • Loading branch information...
1 parent 39fed06 commit 19d83a702a60ad30df4ab97af00260bf25143afe @Raptor399 Raptor399 committed Nov 24, 2012
@@ -124,19 +124,12 @@ public void run() {
pipe_process.runInNewThread();
tsPipe.deleteLater();
+ // Execute the command and ignore the result
String[] cmd = new String[]{ts.executable(), f.getAbsolutePath(), tsPipe.getInputPipe()};
- ProcessBuilder pb = new ProcessBuilder(cmd);
- process = pb.start();
- ProcessWrapper pwi = new ProcessWrapperLiteImpl(process);
- attachedProcesses.add(pwi);
-
- // "Gob": a cryptic name for (e.g.) StreamGobbler - i.e. a stream
- // consumer that reads and discards the stream
- new Gob(process.getErrorStream()).start();
- new Gob(process.getInputStream()).start();
+ ProcessWrapperLiteImpl pwl = new ProcessWrapperLiteImpl(cmd);
+ pwl.getResult();
realIS = tsPipe.getInputStream();
- ProcessUtil.waitFor(process);
LOGGER.trace("tsMuxeR muxing finished");
} catch (IOException e) {
LOGGER.error(null, e);
@@ -18,49 +18,155 @@
*/
package net.pms.io;
-import net.pms.util.ProcessUtil;
-
+import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
+import java.io.InputStreamReader;
import java.util.ArrayList;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Utility class to execute the process of a short running command, for example
+ * "vlc -version". This in contrary to {@link ProcessWrapperImpl}, which is
+ * better suited for processes that take a long time to complete
+ * and that may need to be terminated half way.
+ * <p>
+ * Typical use: create an instance, then call {@link getResult()} to execute
+ * the command and get its output.
+ */
public class ProcessWrapperLiteImpl implements ProcessWrapper {
- private Process p;
+ /** Logger */
+ private static final Logger LOGGER = LoggerFactory.getLogger(ProcessWrapperLiteImpl.class);
+
+ /** System program and arguments to execute. */
+ private String[] command;
+
+ /**
+ * Instantiates a new process wrapper based on the specified operating
+ * system program and arguments. Instantiating the wrapper does not yet
+ * execute the command. A call to {@link getResult()} will execute it.
+ *
+ * @param command The operating system program and arguments to execute.
+ */
+ public ProcessWrapperLiteImpl(String[] command) {
+ this.command = command;
+ }
+
+ /**
+ * Executes the process and returns the combined output and error
+ * stream as a string. If the command fails to execute, an empty
+ * string is returned.
+ * <p>
+ * Note: this method assumes the command will finish by itself. Execution
+ * time is not limited in any way and the method will wait for the command
+ * to finish before returning the result.
+ *
+ * @return The combined output and error stream of the process. Individual
+ * lines will get a "\n" appended.
+ */
+ public String getResult() {
+ StringBuilder result = new StringBuilder();
+
+ if (command == null || command.length == 0) {
+ return "";
+ }
+
+ ProcessBuilder processBuilder = new ProcessBuilder(command);
+
+ // Combine the output and error stream
+ processBuilder.redirectErrorStream(true);
+
+ try {
+ LOGGER.trace("Executing command \"" + StringUtils.join(command, " ") + "\"");
+ Process process = processBuilder.start();
+ InputStream is = process.getInputStream();
+ InputStreamReader isr = new InputStreamReader(is);
+ BufferedReader br = new BufferedReader(isr);
+ String line;
+
+ while ((line = br.readLine()) != null) {
+ result.append(line);
+ result.append("\n");
+ }
+
+ br.close();
+ isr.close();
+ is.close();
+ process.waitFor();
+ process.destroy();
+ } catch (IOException e) {
+ LOGGER.debug("Error executing command \"" + StringUtils.join(command, " ") + "\"", e);
+ } catch (InterruptedException e) {
+ LOGGER.debug("Interrupted while waiting for command \"" + StringUtils.join(command, " ")
+ + "\" to finish.");
+ }
- public ProcessWrapperLiteImpl(Process p) {
- this.p = p;
+ return result.toString();
}
+ /**
+ * Unused interface method.
+ *
+ * @return Always returns <code>null</code>.
+ */
@Override
public InputStream getInputStream(long seek) throws IOException {
return null;
}
+ /**
+ * Unused interface method.
+ *
+ * @return Always returns <code>null</code>.
+ */
@Override
public ArrayList<String> getResults() {
return null;
}
+ /**
+ * Unused interface method.
+ *
+ * @return Always returns <code>false</code>.
+ */
@Override
public boolean isDestroyed() {
return false;
}
+ /**
+ * Unused interface method.
+ */
@Override
public void runInNewThread() {
}
+ /**
+ * Unused interface method.
+ *
+ * @return Always returns <code>false</code>.
+ */
@Override
public boolean isReadyToStop() {
return false;
}
+ /**
+ * Unused interface method.
+ *
+ * @param nullable Parameter is ignored.
+ */
@Override
public void setReadyToStop(boolean nullable) {
}
+ /**
+ * Unused interface method.
+ */
@Override
public void stopProcess() {
- ProcessUtil.destroy(p);
}
}
@@ -0,0 +1,90 @@
+/*
+ * PS3 Media Server, for streaming any medias to your PS3.
+ * Copyright (C) 2008 A.Brochard
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License only.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package net.pms.io;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.slf4j.LoggerFactory;
+
+import ch.qos.logback.classic.LoggerContext;
+
+import com.sun.jna.Platform;
+
+/**
+ * Tests for class ProcessWrapperLiteImpl.
+ */
+public class ProcessWrapperLiteImplTest {
+ @Before
+ public void setUp() {
+ // Silence all log messages from the PMS code that is being tested
+// LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
+// context.reset();
+ }
+
+ /**
+ * Test the basics of the ProcessWrapperLiteImpl class.
+ */
+ @Test
+ public void testBasics() {
+ // Null command should return empty string
+ ProcessWrapperLiteImpl pw = new ProcessWrapperLiteImpl(null);
+ assertEquals("Result for empty command is empty string", "", pw.getResult());
+
+ // Empty command should return empty string
+ String[] command = {};
+ pw = new ProcessWrapperLiteImpl(command);
+ assertEquals("Result for empty command is empty string", "", pw.getResult());
+
+ // Non-existing command should return empty string
+ command = new String[] { "non-existing-command" };
+ pw = new ProcessWrapperLiteImpl(command);
+ assertEquals("Result for non existing command is empty string", "", pw.getResult());
+
+ // "cd ." should return an empty string on all operating systems.
+ command = new String[] { "cd", "." };
+ pw = new ProcessWrapperLiteImpl(command);
+ assertEquals("Result for \"cd .\" is empty string", "", pw.getResult());
+ }
+
+ /**
+ * Test commands that return output.
+ */
+ @Test
+ public void testCommandsWithOutput() {
+ String[] command;
+
+ if (Platform.isWindows()) {
+ // Windows command that produces output
+ command = new String[] { "dir", "/s" };
+ } else {
+ // Mac OSX and Linux command that produces output
+ command = new String[] { "ls", "-a" };
+ }
+
+ ProcessWrapperLiteImpl pw = new ProcessWrapperLiteImpl(command);
+
+ // It would probably be wise to insert a test for the actual output,
+ // e.g. to see if it contains a particular string. For now, simply
+ // test to see whether the result is not empty.
+ assertFalse("Result for command with output is not empty", "".equals(pw.getResult()));
+ }
+}

0 comments on commit 19d83a7

Please sign in to comment.