I'm trying to run process builder to execute a file which is inside my bin/resources/ folder of my java project. For that, I don't want to hard code the complete path, so I'm trying to pass absolute path (full path I mean) using class loader and pass this value as a list argument to ProcessBuilder class. How ever I'm not able to succeed in doing so.
Below code samples (both Case 1 and 2) run fine but nothing happening in the end. I mean the file is not getting called.
Case 1:
String rCmd = "Rscript.exe";
String rScriptName = "resources/MyScript.R";
List list = new ArrayList<>();
list.add(rCmd);
list.add(this.getClass().getClassLoader().getResource(rScriptName).toURI().toString());
ProcessBuilder pb = new ProcessBuilder(list);
pb.start();
Case 2:
String rCmd = "Rscript.exe";
String rScriptName = "resources/MyScript.R";
List list = new ArrayList<>();
list.add(rCmd);
list.add(this.getClass().getClassLoader().getResource(rScriptName).toString());
ProcessBuilder pb = new ProcessBuilder(list);
pb.start();
Below piece of code throwing exception:
Case 3:
String rCmd = "Rscript.exe";
String rScriptName = "resources/MyScript.R";
List list = new ArrayList<>();
list.add(rCmd);
list.add(this.getClass().getClassLoader().getResource(rScriptName).toURI());
ProcessBuilder pb = new ProcessBuilder(list);
pb.start();
Case 4:
File file = new File(this.getClass().getClassLoader().getResource(rScriptName).toURI());
List list = new ArrayList<>();
list.add(rCmd);
list.add(file);
ProcessBuilder pb = new ProcessBuilder(list);
pb.start();
Output:
Exception in thread "main" java.lang.ArrayStoreException
at java.lang.System.arraycopy(Native Method)
at java.util.ArrayList.toArray(ArrayList.java:361)
at java.lang.ProcessBuilder.start(ProcessBuilder.java:1005)
Below code gives me expected output but don't want to hard code the path this way, as I need to run this code in linux box later.
String rCmd = "Rscript.exe";
String rScriptName = "D:/MyScript.R";
ProcessBuilder pb = new ProcessBuilder(rCmd, rScriptName);
pb.start();
Expecting your much needed help on this!
String rCmd = "Rscript.exe";
String rScriptName = "filename Without Resource";
List list = new ArrayList<>();
list.add(rCmd);
list.add([Class name].class.getClassLoader().getResource(rScriptName).toURI().getPath());
Was working.
Process builder Expect String Argument So try to parse the list and it will give the below exception unless they are strings.
Try
this.class.getClassLoader().getResource(rScriptName).toURI().getPath()
It will add the absolute path of the file and it will execute.
Class ProcessBuilder
Related
I would like to have a separate log file created for each process forked by a main process.
In log4j2.xml, a log file name is declared as:
fileName="${sys:loggingFileName}.log"
In an initial class, say class A, a log is created:
public class A {
System.setProperty ("loggingFileName", "MyLogA");
log = LogManager.getLogger (A.class);
...
log.info (...);
ProcessBuilder pb = new ProcessBuilder ();
Map<String, String> environment;
environment = pb.environment ();
environment.put ("CLASSPATH", System.getProperty ("java.class.path"));
pb.command (Arrays.asList ("/usr/bin/java", "class B"));
final Process process = pb.start ();
}
Class B could look exactly as above (with the appropriate substitution of B for A, and C for the creation of the new class).
When started separately (not through ProcessBuilder), Class A and class B each create a separate log as expected.
However, when class B is forked from class A using ProcessBuilder, a MyLogA.log file is created containing the specified log entry, but no MyLogB.log.
I don't understand why. Any guidance is appreciated.
Additionally: I have tried placing :
pb.redirectOutput (Redirect.INHERIT);
pb.redirectError (Redirect.INHERIT);
or
pb.redirectErrorStream (true);
prior to the pb.start, with no change.
Please use below lines of code before starting any processbuilder in order to redirect logs to particular log file.
Process p = null;
ProcessBuilder pb = new ProcessBuilder(......);
File logFile = new File("path to file/nameOfFile");
logFile.createNewFile();
pb.redirectErrorStream(Boolean.TRUE).redirectOutput(Redirect.appendTo(logFile));
p = pb.start();
This will create a start redirecting all the logs from process to this file as soon as pb.start is called.
Am going round in circles here trying to execute a bit of VBScript from java using processbuilder. I'm running my java code as part of a tomcat servlet. If I run the same command from a command line - its running fine. Have tried opening permissions right up (everyone=full perms)
Java Code (excerpt):
ProcessBuilder pb = new ProcessBuilder("cscript", "C:\\Users\\Public\\Documents\\office2pdf.vbs", "C:\\Users\\Public\\Documents\\temp\\22.doc");
Process pr = pb.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(pr.getErrorStream()));
String line = null;
StringBuilder sb = new StringBuilder("");
while ( (line = reader.readLine()) != null) {
sb.append(line);
}
int i = pr.waitFor() ;
getServletContext().log("pb response="+i+", er="+sb.toString());
VBScript (excerpt):
Sub SaveWordAsPDF(p_strFilePath)
'Save Word file as a PDF
'Initialise
Dim objWord, objDocument
Set objWord = CreateObject("Word.Application")
Wscript.Echo p_strFilePath
'Open the file
Set objDocument = objWord.Documents.Open(p_strFilePath)
'Save the PDF
objDocument.SaveAs PathOfPDF(p_strFilePath), WORD_PDF
'Close the file and exit the application
objDocument.Close FALSE
objWord.Quit
End Sub
I've traced it as far to see that the vbscript is infact loading; the line 'Wscript.Echo p_strFilePath' does in fact actually print the correct path to the word doc. According to the error stream returned from java the offending line from vbscript is: 'Set objDocument = objWord.Documents.Open(p_strFilePath)' for which its saying:
Microsoft VBScript runtime error: Object required: 'objWord.Documents.Open(...)'
not sure if this is only triggered from incomplete path; looked at lots of posts - haven't been able to find any that exactly match my situation. Any advice is greatly appreciated!
Runtime.getRuntime().exec("....")
and
ProcessBuilder pb = new ProcessBuilder("java", "-server", "-jar", "yourJar.jar");
Process p = pb.start();
The above 2 ways of executing a command create a new process for running the command.
Is there a way to execute the command in the same process, without creating a new one?
As #soong commented, you could manually load your JAR and the classes you need, and then call the main method by reflection. You can achieve this with something like this:
// load your JAR file as a File instance
String myJarPath = "C:\\somefolder\\someOtherFolder\\MyJar.jar";
File myJarFile = new File(myJarPath);
// create a new class loader based on your JAR's URL
URLClassLoader classLoader = new URLClassLoader(new URL[]{myJarFile.toURI().toURL()});
// load the class with the main method
Class<?> classToLoad = classLoader.loadClass("MyClass");
// get the main method
Method method = classToLoad.getMethod("main", String[].class);
// invoke it
String args[] = {"arg1", "arg2"}; // args to pass to the main method, it can be null
method.invoke(null, (Object) args); // first parameter is null because main is static
Maybe you could read classes into the Process with an ObjectInputStream
I'm writing a program that forms a new sub-process in a following pattern:
proc = java.lang.Runtime.getRuntime().exec("java -jar Xxx.jar");
Though the environment variables are automatically inherited to sub-processes, I think the system properties defined by -D<name of property>=<value of the property> are not.
My question is, if there is any way to transfer the system properties programmatically. Any comments or answers are welcomed. Thanks.
One solution that I've come up with is to define a set of properties to pass to subprocesses, and create a -D<key>=<value> strings from it.
static String[] properties_to_pass = {
"log4j.configuration"
};
Above is the set of system properties to pass. Then...
StringBuffer properties = new StringBuffer();
for ( String property : properties_to_pass ) {
String value = System.getProperty(property);
if ( value != null ) {
String r = String.format("-D%s=%s ", property, value);
properties.append( r );
}
}
And after the above ...
String command_arg = properties.toString();
String command = String.format("java %s -jar Torpedo.jar", command_arg);
java.lang.Runtime.getRuntime.exec( command );
Very naive solution, but works anyway. But still not sure that there might be a better solution. Any further comments are welcomed.
If you are using Java 1.5+ it is highly recommended to use ProcessBuilder to create processes as it allows a lot of convenience methods to achieve what you are trying.
You can achieve sharing of System Properties with ProcessBuilder as following.
Properties props = System.getProperties();
ProcessBuilder builder = new ProcessBuilder(new String[] {"java", "-jar","Xxx.jar"});
Map<String, String> env = builder.environment();
for(String prop:props.stringPropertyNames()) {
env.put(prop, props.getProperty(prop));
}
Process process = builder.start();
With this code from your Child Process you can access any System Properties you pass using -D in your Parent Process using System.getProperty method.
I would like to pass multiple parameters to a processBuilder and the parameters to be separated by a space.
Here is the command,
String[] command_ary = {dir+"library/crc"," -s ", fileName," ",addressRanges};
I need to provide a space after "fcrc" and after "-p" and in between "filename" and the "addressRange".
Thank you
You don't need to include spaces. The ProcessBuilder will deal with that for you. Just pass in your arguments one by one, without space:
ProcessBuilder pb = new ProcessBuilder(
dir + "library/crc",
"-s",
fileName,
addressRanges);
We need spaces between arguments in commandline because the commandline need to know which is the first argument, which is the second and so on. However when we use ProcessBuilder, we can pass an array to it, so we do not need to add those spaces to differentiate the arguments. The ProcessBuilder will directly pass the command array to the exec after some checking. For example,
private static final String JAVA_CMD = "java";
private static final String CP = "-cp";
private static final String CLASS_PATH = "../bin";
private static final String PROG = "yr12.m07.b.Test";
private static final String[] CMD_ARRAY = { JAVA_CMD, CP, CLASS_PATH, PROG };
ProcessBuilder processBuilder = new ProcessBuilder(CMD_ARRAY);
The above code will work perfectly.
Moreover, you can use
Runtime.getRuntime().exec("java -cp C:/testt Test");
But it is more convenient to use ProcessBuilder, one reason is that if our argument contains space we need to pass quote in Runtime.getRuntime().exec() like java -cp C:/testt \"argument with space\", but with ProcessBuilder we can get rid of it.
ProcessBuilder processBuilder = new ProcessBuilder("command", "The first argument", "TheSecondWithoutSpace");
Use it like this:
new java.lang.ProcessBuilder('netstat -an'.toString().split('\\s'))).start()