Is there any way to transfer system properties to sub-processes? - java

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.

Related

How can I get all errors when executing a command?

I'm developing a java program, at a certain point in the program I need to execute some commands and show all the errors returned by that command. But I can only show the first one.
This is my code:
String[] comando = {mql,"-c",cmd};
File errorsFile = new File("C:\\Users\\Administrator2\\Desktop\\errors.txt");
ProcessBuilder pb = new ProcessBuilder(comando);
pb.redirectError(errorsFile);
Process p = pb.start();
p.waitFor();
String r = errorsFile.getAbsolutePath();
Path ruta = Paths.get(r);
Charset charset = Charset.forName("ISO-8859-1");
List<String> fileContents = Files.readAllLines(ruta,charset);
if (fileContents.size()>0){
int cont = 1;
for(String str : fileContents){
System.out.println("Error"+cont);
System.out.println("\t"+str);
cont++;
}
}
else{
//other code
}
In this case I know that there are more than one errors, so I expect more than one output but as you can see in the photo I get only one.
I think the key here might be that ProcessBuilder's redirectError(File file) is actually redirectError (Redirect.to(file)) .
From Oracle's documentation of ProcessBuilder class:
This is a convenience method. An invocation of the form redirectError(file) behaves in exactly the same way as the invocation redirectError (Redirect.to(file)).
Most example's I have seen use Redirect.appendTo(File file) rather than Redirect.to(file). The documentation may explain why.
From Oracle's documentation of ProcessBuilder.Redirect :
public static ProcessBuilder.Redirect to(File file)
Returns a redirect to write to the specified file. If the specified file exists when the subprocess is started, its previous contents will be discarded.
public static ProcessBuilder.Redirect appendTo(File file)
Returns a redirect to append to the specified file. Each write operation first advances the position to the end of the file and then writes the requested data.
I would try replacing
pb.redirectError(errorsFile)
with
pb.redirectError(Redirect.appendTo(errorsFile))
and see if you get more lines that way.
Have you debugged and checked the contents of fileContents?
EDIT: Sorry, it should be a comment, but can't do it yet :(

Create a separate log4j2 log for each forked java process

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.

How to use Apache Commons CLI to parse the property file and --help option?

I have a property file which is like this -
hostName=machineA.domain.host.com
emailFrom=tester#host.com
emailTo=world#host.com
emailCc=hello#host.com
And now I am reading the above property file from my Java program as shown below. I am parsing the above property file manual way as of now -
public class FileReaderTask {
private static String hostName;
private static String emailFrom;
private static String emailTo;
private static String emailCc;
private static final String configFileName = "config.properties";
private static final Properties prop = new Properties();
public static void main(String[] args) {
readConfig(arguments);
// use the above variables here
System.out.println(hostName);
System.out.println(emailFrom);
System.out.println(emailTo);
System.out.println(emailCc);
}
private static void readConfig(String[] args) throws FileNotFoundException, IOException {
if (!TestUtils.isEmpty(args) && args.length != 0) {
prop.load(new FileInputStream(args[0]));
} else {
prop.load(FileReaderTask.class.getClassLoader().getResourceAsStream(configFileName));
}
StringBuilder sb = new StringBuilder();
for (String arg : args) {
sb.append(arg).append("\n");
}
String commandlineProperties = sb.toString();
if (!commandlineProperties.isEmpty()) {
// read, and overwrite, properties from the commandline...
prop.load(new StringReader(commandlineProperties));
}
hostName = prop.getProperty("hostName").trim();
emailFrom = prop.getProperty("emailFrom").trim();
emailTo = prop.getProperty("emailTo").trim();
emailCc = prop.getProperty("emailCc").trim();
}
}
Most of the time, I will be running my above program through command line as a runnable jar like this -
java -jar abc.jar config.properties
java -jar abc.jar config.properties hostName=machineB.domain.host.com
My question is-
Is there any way to add --help option while running the abc.jar that can tell us more about how to run the jar file and what does each property means and how to use them? I have seen --help while running most of the C++ executable or Unix stuff so not sure how we can do the same thing in Java?
Do I need to use CommandLine parser like Commons CLI for this in Java to achieve this and instead of doing manual parsing, I should use Commons CLI to parse the file as well? If yes, then can anyone provide an example how would I do that in my scenario?
In the long run if you plan to add other options in the future then commons-cli is surely a fairly good fit as it makes it easy to add new options and manual parsing quickly becomes complicated.
Take a look at the official examples, they provide a good overview of what the library can do.
Your specific case would probably lead to something like the following:
// create Options object
Options options = new Options();
Option help = new Option( "h", "help", false, "print this message" );
options.addOption(help);
CommandLineParser parser = new PosixParser();
CommandLine cmd = parser.parse( options, args);
if(cmd.hasOption("help") || cmd.getArgList().isEmpty()) {
// automatically generate the help statement
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp( "cli-test [options] <property-file>", options );
return;
}
// do your thing...
System.out.println("Had properties " + cmd.getArgList());

pass multiple parameters to ProcessBuilder with a space

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()

Execute a Java program from our Java program

I used
Runtime.getRuntime().exec("_____")
but it throws a IOException as below:
java.io.IOException: CreateProcess: c:/ error=5
at java.lang.Win32Process.create(Native Method)
at java.lang.Win32Process.<init>(Win32Process.java:63)
at java.lang.Runtime.execInternal(Native Method
I don't know whether I have the problem with specifying the path or something else. Can anyone please help me with the code.
You're trying to execute "C:/". You'll want to execute something like:
"javaw.exe d:\\somejavaprogram\\program.jar"
Notice the path separators.
I'm assuming this is for an ad-hoc project, rather than something large. However, for best practice running external programs from code:
Don't hardcode the executable location, unless you're certain it will never change
Look up directories like %windir% using System.getenv
Don't assume programs like javaw.exe are in the search path: check them first, or allow the user to specify a location
Make sure you're taking spaces into account: "cmd /c start " + myProg will not work if myProg is "my program.jar".
You can either launch another JVM (as described in detail in other answers).
But that is not a solution i would prefer.
Reasons are:
calling a native program from java is "dirty" (and sometimes crashes your own VM)
you need to know the path to the external JVM (modern JVMs don't set JAVA_HOME anymore)
you have no control on the other program
Main reason to do it anyway is, that the other application has no control over your part of the program either. And more importantly there's no trouble with unresponsive system threads like the AWT-Thread if the other application doesn't know its threading 101.
But! You can achieve more control and similar behaviour by using an elementary plugin technique. I.e. just call "a known interface method" the other application has to implement. (in this case the "main" method).
Only it's not quite as easy as it sounds to pull this off.
you have to dynamically include required jars at runtime (or include them in the classpath for your application)
you have to put the plugin in a sandbox that prevents compromising critical classes to the other application
And this calls for a customized classloader. But be warned - there are some well hidden pitfalls in implementing that. On the other hand it's a great exercise.
So, take your pick: either quick and dirty or hard but rewarding.
java.io.IOException: CreateProcess: c:/ error=5
at java.lang.Win32Process.create(Native Method)
at java.lang.Win32Process.<init>(Win32Process.java:63)
at java.lang.Runtime.execInternal(Native Method)
If I recall correctly, error code 5 means access denied. This could be because your path is incorrect (trying to execute "c:/") or you are bumping against your OS security (in which case, look at the permissions).
If you are having trouble locating the Java executable, you can usually find it using system properties:
public class LaunchJre {
private static boolean isWindows() {
String os = System.getProperty("os.name");
if (os == null) {
throw new IllegalStateException("os.name");
}
os = os.toLowerCase();
return os.startsWith("windows");
}
public static File getJreExecutable() throws FileNotFoundException {
String jreDirectory = System.getProperty("java.home");
if (jreDirectory == null) {
throw new IllegalStateException("java.home");
}
File exe;
if (isWindows()) {
exe = new File(jreDirectory, "bin/java.exe");
} else {
exe = new File(jreDirectory, "bin/java");
}
if (!exe.isFile()) {
throw new FileNotFoundException(exe.toString());
}
return exe;
}
public static int launch(List<String> cmdarray) throws IOException,
InterruptedException {
byte[] buffer = new byte[1024];
ProcessBuilder processBuilder = new ProcessBuilder(cmdarray);
processBuilder.redirectErrorStream(true);
Process process = processBuilder.start();
InputStream in = process.getInputStream();
while (true) {
int r = in.read(buffer);
if (r <= 0) {
break;
}
System.out.write(buffer, 0, r);
}
return process.waitFor();
}
public static void main(String[] args) {
try {
Runtime.getRuntime().exec("c:/");
List<String> cmdarray = new ArrayList<String>();
cmdarray.add(getJreExecutable().toString());
cmdarray.add("-version");
int retValue = launch(cmdarray);
if (retValue != 0) {
System.err.println("Error code " + retValue);
}
System.out.println("OK");
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
(Tested Windows XP, Sun JRE 1.6; Ubuntu 8.04, OpenJDK JRE 1.6)
This is the equivalent of running:
java -version
You may also want to look at the "java.library.path" system property (and "path.separator") when trying to locate the executable.
How about just calling the main from your java program?
Test.main(null);
This worked fine for me
Is there any reason you can't just call it directly in your Java code?
If there is a reason I've not tried it for executing a Java Program but you could try Jakarta Commons Exec works well for executing most programs.
I had to do this recently.
Here is how I did it, picking up only the relevant parts:
private static final String[] straJavaArgs =
{
"?i/j2re/bin/java",
"-ms64m",
"-mx64m",
"-Djava.ext.dirs=?i/lib;?i/jar/lib;?i/jar"
};
// ...
// AppDesc appToRun;
List<String> params = new ArrayList<String>();
// Java exe and parameters
params.addAll(ExpandStrings(straJavaArgs));
// Common VM arguments
params.addAll(Arrays.asList(AppDesc.GetCommonVMArgs()));
// Specific VM arguments
params.addAll(ExpandStrings(appToRun.GetVMArgs()));
// The program to run
params.add(appToRun.GetClass());
// Its arguments
params.addAll(ExpandStrings(appToRun.GetProgramArgs()));
// The common arguments
params.addAll(ExpandStrings(AppDesc.GetCommonProgramArgs()));
ProcessBuilder processBuilder = new ProcessBuilder(params);
process = processBuilder.start();
return CaptureProcessOutput(); // Uses a StreamGobbler class
protected ArrayList<String> ExpandStrings(String[] stra)
{
ArrayList<String> alResult = new ArrayList<String>();
for (int i = 0; i < stra.length; i++)
{
// Super flexible, eh? Ad hoc for the current task, at least...
alResult.add(stra[i]
.replaceAll("\\?i", strInstallDir)
.replaceAll("\\?c", strConfigDir)
);
}
return alResult;
}
public enum AppDesc
{
// Enumerate the applications to run, with their parameters
}
Incomplete, if you need more details, just ask.
public class Test {
public static void main(String[] args) throws Exception {
Process p = Runtime.getRuntime().exec("\"c:/program files/windows/notepad.exe\"");
p.waitFor();
}
}
The above works quite well, instead of passing \"c:/program files/windows/notepad.exe\" as the arguments for the executable, use the path to your program, I'm not sure if this solution is JVM version dependent, or if it can use relative paths.
You must pass the path of your executable at the exec method. Are you really trying to execute the "-" process?
Also, have a look at this for some useful tips.
Put ant lib in you classpath ( project lib ) and run this code :
import org.apache.tools.ant.taskdefs.Execute;
Execute exe = new Execute();
exe.setCommandline(new String[]{"java", "-version"});
exe.execute();
I can't remember the exact code that I used to get this to work, but you have to pass "java.exe" (or the equivalent) as the executable, and then the class or jar to run as the parameter, with the correct working directory. So it's not as simple as just calling one method.
I had a similiar problem. I needed to run a section of Java code in a seperate VM as it invoked native code via JNI that occasionally blew up taking out the entire VM.
I cheated a little though. I initially used Runtime to invoke a simple batch command file and put the work-in-progress java command in there. This enabled me to tweak it as needed and to run the command in a DOS prompt for easy testing. Once it was finished I simply copied the result into the Runtime invocation.
First you compile the prog-A code and convert to jar file(ie:In NetBeans Shift-F11)and the path is of netbeans(NetBeansProjects/prog-A/dist/prog-A.jar)
public class ProgA {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
System.out.println("Helllo print thr ProgA");
}
}
}
Second open the new project in prog-B and add the libraries, and select the jar and give to the prog-A.jar file and write the two line in your program
public class ProgB {
public static void main(String[] args) {
ProgA progA = new ProgA();
String arg[] = null;
progA.main(arg);
}
}
I agree with Ushsa Varghese, if you just want to run your jar file instead of compiling the .java file that is in the same directory you are executing your application from try the code below. This is the same as executing your java application from the command line so you have to invoke the jvm in order to run your application. Also make sure you have the complete path to your jar file the example below assumes that the jar file is in the same directory as the application that is executing the code below. keep in mind this is system dependent code.
try {
Runtime runTime = Runtime.getRuntime();
Process process = runTime.exec("java -jar deleteDriveC.jar");
} catch (IOException ex) {
//jar file doesnt exist
//Logger.getLogger(this.class.getName()).log(Level.SEVERE, null, ex);
}
The answer is simple all you have to do is put the code -
$ process p = Runtime.getRuntime().exec("javac factorial.java"); in the try catch block
The code would look like this -
try
{
process p = Runtime.getRuntime().exec("javac factorial.java");
}
catch(IOException e)
{
e.printStackTrace();
}
Hey I think this should work. Atleast for me it did work

Categories