Understanding example code for processbuilder in Java - java

I have some example code where a process builder is used and given two commands to execute, but I can't fully understand what each line of code is doing.
Also the commands don't seem to be actually executing.
Code:
public static void main(String[] args) {
ArrayList<String> commands = new ArrayList(); // commands in a processbuilder is an Arraylist of of strings
commands.add("myfile.pdf"); // supposed to open the file?
commands.add("bash\", \"-c\", \"ls"); // supposed to use ls command in terminal
execute(commands); // should execute the two commands above
System.out.println("executed commands"); // only thing that actually happens
}
public static void execute(ArrayList<String> command) {
try {
ProcessBuilder builder = new ProcessBuilder(command); // a new builder which takes a command passed into the method
Map<String, String> environ = builder.environment(); // ???
Process p = builder.start(); // p is never used?
} catch (Exception e) {
e.printStackTrace();
}
}
I get no errors or warnings.
Tried reading the API on the processbuilder but I didn't really understand it

ProcessBuilder helps to start external processes.
First, the command line parts (executable, parameters) are taken as a list of String, which is very comfortable. ("command" is rather misleading here, since it consists of executable and parameters).
Second, you can edit the environment of the new process (environment variables like "$HOME", "$PATH", etc.).
Your p can be used, for example to check, if the process has finished yet or to retrieve the input/output of the new process. Since you only start the process (fire-and-forget), you don't need it here.
You may also use Runtime.exec(...) to start an external process, which is the historical way to do so, but I think it's more comfortable to use ProcessBuilder.

Related

How to return out of command pattern in controller so that application stops running (Java)?

I am designing a controller using the command pattern.
The controller has a while loop inside that is scanning for user input. If user input matches a specific String, then a command class is executed. Here is a code snippit:
public Controller(Readable in, Appendable out) {
this.out = out;
this.scan = new Scanner(in);
this.commandMap = this.generateCommands();
}
public void go(Model m) {
while (scan.hasNext()) {
String input = scan.next();
Command command = this.commandMap.get(input);
command.do(m);
}
}
I usually use return to stop the application. However, when I use return inside one of the Command classes, the application keeps running. I think it just goes back to this upper loop.
By the way, all my commands are public void.
Is there a way to exit/close the application from within the command classes? Like a "super" return? Or do I need to no longer make them void and if/else the return in the controller.
EDIT: system.exit(0) doesn't seem like the right solution for me because it doesn't preserve the appendable log? My JUnit tests no longer print out everything I have appended once system.exit(0) is called.
Use this to terminate the entire program:
System.exit(0);

asking user for input valid command in java

I am developing my own shell(command prompt). [When
a user enters a built-in command, the shell is required to search and execute the respective code accordingly.]
I have created my code using command split, and command parameters in order to store my commands. but one thing I'm confused about is that making a command that is not in the list.
I think of using if statement to print invalid comment (for example)
if (command!="exit")||(command!="about")||(command!="date")||(command!="time")||(command!="hist")||(command!="notepad")
||(command!="")||(command!="hist -h")||(command!="hist -l")||(command!="c"){
System.out.println("invalid command");
}
but this statement is way too much if there are tons of command line.. so is there an easy way of implementing it !?
If this java and all commands are Strings and input command is also a String you can simplify what you are trying to do by creating a list of valid commands and do a contains check.
List<String> validCommands = Arrays.asList("exit", "about", "date");
if (!validCommands.contains(command)) {
System.out.println("invalid command");
}
That being said there are better ways to maintain a list of valid command outside java program such as properties file and load list of valid commands from that file. This will make your program more maintainable.
Use a Map<String, Command>, to keep track of the available commands. If the command is not in the map then it's invalid. For example:
public class Shell {
private final Map<String, Command> supportedCommands;
public Shell(Map<String, Command> supportedCommands) {
this.supportedCommands = supportedCommands;
}
public void execute(String command, String[] args) {
Command c = supportedCommands.get(command);
if (c == null) {
System.out.println("invalid command");
} else {
c.execute(args);
}
}
public interface Command {
public void execute(String[] args);
}
}

How do you parse arguments for a java program?

I'm making a Selenium WebDriver java program. I have 25 application and 4 environments. I need to be able to pass something like -app app1 app2 app3 ... appn -env env1 env2 envn
I need to be able to pass either, neither or both arguments. Right now I have it being able to pass one app and one env in that order but I need to be able to do it in either order and with the either neither or both possibility. Here's what I have so far. With this I can either pass no arguments and runs every app for every environment (which is what I want) or I can pick app1 env1 in that order for that specific test.
public static Application chooseAppTest(String[] args)
{
Application application = null;
switch (Application.valueOf(args[0]))
{
case ACCOUNTINVENTORY:
new AccountInventory(Environment.valueOf(args[1]));
AccountInventory.accountInventoryDatabaseTests(testResults);
break;
if (args.length == 0)
{
LogIn.loginTest(testResults);
DatabaseTest.testResults(testResults);
LinkTest.linkTests(testResults);
}
else
{
// First choose application, then choose environment
Application.chooseAppTest(args);
}
I don't think recursion is needed. You can do something like this:
public static void main (String[] args)
{
List<String> apps = new LinkedList<>();
List<String> envs = new LinkedList<>();
List<String> current = null;
// parse arguments
for (String arg : args)
{
if (arg.equals("-app")) current = apps;
else if (arg.equals("-env")) current = envs;
else if (current != null) // add argument
current.add(arg);
}
// parsing finished
Application.doSomethingWith(apps, envs);
}
It is not necessary or advantagious to use recursion. You can read all the arguments into an array and process them from there. Other than that, I'm not sure how you would proceed. With the arguments arranged in this way, how do you know which environment goes with which application?
As Elliott commented, have you looked at Apache Commons CLI? It's a command line parser.

Get output from a process

This is a second part to my question here.
I now have a process but I want to know how to get the output from the process?
String filename = matlab.getfileName();
Process p = Runtime.getRuntime().exec("java -cp mediaProperty.java " + filename);
My mediaProperty.java:
public class mediaProperty {
public static Object main(String[] args) {
Object[] mediaProp = null;
java.util.List lstMedia = new ArrayList();
Media media = null;
try {
media = new Media();
lstMedia.add(args);
mediaProp = media.media(3, lstMedia);
} catch (Exception p) {
System.out.println("Exception: " + p.toString());
} finally {
MWArray.disposeArray(mediaProp);
if (media != null) {
media.dispose();
}
}
return mediaProp;
}
}
The mediaProperty.java will return an Object. Inside this is actually String array. How do I get the array? And is the way I'm calling exec() correct?
use public static void main (not Object as return type)
Serialize the object using ObjectOutputStream (all necessary examples are in the javadoc)
The only thing different from the example is the construction - construct it like
ObjectOutputStream oos = new ObjectOutputStream(System.out);
in the program calling exec(), get the output with process.getOutputStream()
Read in an ObjectInputStream based on the already retreived OutputStream (check this)
Deserialize the object (see the javadoc of ObjectInputStream)
Now, this is a weird way to do it, but as I don't know exactly what you are trying to achieve, it sounds reasonable.
You could do System.setOut(new PrintStream(p.getOutputStream())) if you'd like to have the process print its results directly to standard output. Of course, this will override the old standard output. But you could also do other things with the process's output stream, like have a thread that reads from it.
A problem with your code is that the main function of a class must be of type void, and will return nothing. You will not be able to pass Java objects between processes, as they are running in different JVMs. If you must do this you could serialize the object to disk, but I imagine you don't even need to run this in a separate process.
mediaProp is a local variable in your main() method. It's not accessible from the outside.
You'll have to redesign your mediaProperty class a bit.
First, you should use:
"java -cp . mediaProperty " + filename
for calling the java process. The "-cp ." defines the classpath and I have made the assumption that the java file is compiled and the generated class file is at the same path as the executing process.
Then, you need to print the result at the standard output and not just return it. Finally, read this article for reading the output.
Tip 1: Rename the class to MediaProperty
Tip 2: Why you don't call the MediaProperty class directly from your code? Is it necessary to start a new process?
There are a few gotcha's.
In exec you assume that java is on the path, and the filename should be fully qualified or you should know that the current working dir of the java process is OK.
main() should return void (nothing). If you want to pass the results out of your program use something like:
for (Object o : mediaProp) {
System.out.println(o);
}
and parse it again on the input stream (the calling software).
Better yet, include the MediaProperty class in the java path and call main(...) directly in stead of calling a separate java process.

How do I unit test a Java method which uses ProcessBuilder and Process?

I have a Java method which starts up a Process with ProcessBuilder, and pipes its output into a byte array, and then returns its byte array when the process is finished.
Pseudo-code:
ProcessBuilder b = new ProcessBuilder("my.exe")
Process p = b.start();
... // get output from process, close process
What would be the best way to go about unit testing this method? I haven't found a way to mock ProcessBuilder (it's final), even with the incredibly awesome JMockit, it gives me a NoClassDefFoundError:
java.lang.NoClassDefFoundError: test/MockProcessBuilder
at java.lang.ProcessBuilder.<init>(ProcessBuilder.java)
at mypackage.MyProcess.start(ReportReaderWrapperImpl.java:97)
at test.MyProcessTest.testStart(ReportReaderWrapperImplTest.java:28)
Any thoughts?
Answer - As Olaf recommended, I ended up refactoring those lines to an interface
Process start(String param) throws IOException;
I now pass an instance of this interface into the class I wanted to test (in its constructor), normally using a default implementation with the original lines. When I want to test I simply use a mock implementation of the interface. Works like a charm, though I do wonder if I'm over-interfacing here...
Shield yourself from the classes to be mocked. Create an interface either for doing what you really want (e.g. hiding the fact that external processes are involved at all) or only for Process and ProcessBuilder.
You don't want to test, that ProcessBuilder and Process work, only that you can work with their output. When you create an interface one trivial implementation (that can be inspected easily) delegates to ProcessBuilder and Process, another implementation mocks this behaviour. Later on you might even have another implementation that does what you need without starting another process.
With newer releases of JMockit (0.98+) you should be able to easily mock JRE classes like Process and ProcessBuilder. So, no need to create interfaces just for testing...
Full example (using JMockit 1.16):
public class MyProcessTest
{
public static class MyProcess {
public byte[] run() throws IOException, InterruptedException {
Process process = new ProcessBuilder("my.exe").start();
process.waitFor();
// Simplified example solution:
InputStream processOutput = process.getInputStream();
byte[] output = new byte[8192];
int bytesRead = processOutput.read(output);
return Arrays.copyOf(output, bytesRead);
}
}
#Test
public void runProcessReadingItsOutput(#Mocked final ProcessBuilder pb)
throws Exception
{
byte[] expectedOutput = "mocked output".getBytes();
final InputStream output = new ByteArrayInputStream(expectedOutput);
new Expectations() {{ pb.start().getInputStream(); result = output; }};
byte[] processOutput = new MyProcess().run();
assertArrayEquals(expectedOutput, processOutput);
}
}

Categories