I have a java program that fires off an executable using the Runtime.exec() method. I'm using the variant that takes in a set of command line params as one argument, and some environment variables as another argument.
The environment variable I'm tryign to set is path, so i'm passing in "PATH=C:\some\path". This does not work. Is there some trick to this or any alternatives. I am stuck to Java 1.4 unfortunately.
Use getenv to get the environment and fix it up then use a flavour of exec to do the exec.
This works with a batch file that has path in it.
package p;
import java.util.*;
public class Run {
static String[] mapToStringArray(Map<String, String> map) {
final String[] strings = new String[map.size()];
int i = 0;
for (Map.Entry<String, String> e : map.entrySet()) {
strings[i] = e.getKey() + '=' + e.getValue();
i++;
}
return strings;
}
public static void main(String[] arguments) throws Exception {
final Map<String, String> env = new HashMap<String, String>(System.getenv());
env.put("Path", env.get("Path") + ";foo");
final String[] strings=mapToStringArray(env);
Runtime.getRuntime().exec("cmd /C start foo.bat",strings);
}
}
If "PATH=C:\some\path" appears in your source code, it would be incorrect as it would be trying to escape the 's' and 'p' in that string, you'd use "PATH=C:\\some\\path" instead (escaping the slashes). Also, you don't want to pass it in as a string directly, but as an array of strings (likely with that as the only string in it).
If you want to change the Path variable on windows, you should take a look at JNI_Registry: http://www.trustice.com/java/jnireg/
It's a Java binding to the Windows Registry API and comes with a very small footprint.
I have used it for my current project and it works just fine.
One solution might be to add an additional command to "exec" where you set the path ... as in the example found here:
http://www.neowin.net/forum/topic/620450-java-runtimegetruntimeexec-help/
excerpt:
cmd = new String[7];
cmd[0] = "cmd";
cmd[1] = "/C";
cmd[2] = "set PATH=C:\\Program Files\\Java\\jdk1.6.0_04\bin";
cmd[3] = "copy " + "\"" +path + "\\" +name+ "\"" + " C:\\java";
cmd[4] = "chdir C:\\java";
cmd[5] = "javac *.java";
cmd[6] = "jar cmf mainClass.txt"+" name"+".jar *.class";
try{
Runtime.getRuntime().exec(cmd);
Related
I have several process objects from which I'd like to get their names. Something like Thread-34 or Thread-74, etc.
I know I can build such with the pid, but is there something built in?
I know how to get their pid or info, but that doesn't help me to get their names.
ProcessBuilder pb = new ProcessBuilder(command);
p = pb.start();
p.pid()
p.info()
Is it possible (Java 15)?
Use p.info().command(). It returns the executable pathname of the process (reference). Note that it returns an Optional object: you need to check if it has a value before getting the pathname string out of it:
var pathname = p.info().command();
if (pathname.isPresent()) {
System.out.println(pathname.get());
}
If you would like to refer to your processes by custom names (like "Process-1", "Process-2", ...) then you can use a HashMap:
import java.util.HashMap;
public class App {
public static void main(String[] args) throws Exception {
// Create processes with names
var processes = new HashMap<String, Process>();
for (var i = 0; i < 5; ++i) {
var processBuilder = new ProcessBuilder("cmd");
var process = processBuilder.start();
var processName = "Process-" + (i + 1); // Name can be anything you want
processes.put(processName, process);
}
// List process names
for (var processName : processes.keySet()) {
System.out.println(processName);
}
// Refer to a process by name
System.out.println(processes.get("Process-3").pid());
}
}
Example output:
Process-1
Process-3
Process-2
Process-5
Process-4
12780
I am trying to build out Integration Tests (IT) for an application. The application has at its centre a Server, written in Java, that set-ups a message queue from which it polls for messages sent to it on a particular port-number. I would like to write an Integration Test which fires some messages at this server/port-number and tests the response.
Below is the full list of VM arguments that I run when I start the server from within Intellij manually. I can start the server this way and then fire my test messages at it but I would like to convert this into IT tests so that I can start/stop the server programmatically at the start and end of my tests.
The problem I am having is that I dont know how to start the server application from within my test class. So to ask it more plainly, how to start the Java main() of a class in its own process thread. I am working within Intellij (2019.1) and Java 8. Should I be using the ProcessBuilder or ExecutorService maybe ?
I think I can use System.setProperty for some of the VM arguments but not sure how to specify the -XX ones...so that would be a second part to this question.
-Djava.endorsed.dirs=/Users/xxx/dev/src/repo/xxx/myapp/target/classes/lib/endorsed
-Dmyapp.home=/private/var/tmp/myapp
-Dmyapp.log.dir=/private/var/tmp
-Dmyapp.env=/Users/xxx/dev/src/repo/xxx/myapp/target/classes/etc/examples/environment-apa.sh
-Dsimplelogger.properties=/private/var/tmp/myapp/etc/simplelogger.properties
-server
-XX:CompileThreshold=2500
-XX:+UseFastAccessorMethods
-Xss256k
-Xmx1g
-Xms512m
I've tried implementing this using the ExecutorService
public class ServerTest {
#Test
public void shouldStartServerOk() {
try{
startServer();
}catch(Exception e){
e.printStackTrace();
fail();
}
}
private void startServer(){
ExecutorService executor = Executors.newFixedThreadPool(1);
Runnable runnableTask = new Runnable() {
#Override
public void run() {
String [] args = new String[0];
try {
System.setProperty("java.endorsed.dirs", "/Users/xxx/dev/src/gitlab/xxx/myapp/target/classes/lib/endorsed");
System.setProperty("myapp.home", "/private/var/tmp/myapp");
System.setProperty("myapp.log.dir", "/private/var/tmp");
System.setProperty("myapp.env", "/Users/xxx/dev/src/gitlab/xxx/myapp/target/classes/etc/examples/environment-apa.sh");
System.setProperty("simplelogger.properties", "/private/var/tmp/myapp/etc/simplelogger.properties");
System.setProperty("-server", "TRUE");
MyApp.main(args);
} catch (Exception e) {
e.printStackTrace();
}
}
};
executor.execute(runnableTask);
// shut down the executor manually
//executor.shutdown();
}
But this doesn't seem to work although the test does complete green. When I debug the process, the flow doesn't Step-Into MyApp.main(args). Strangely when I just try running MyApp.main(args) on its own outside of the ExecutorService then it starts and runs fine until I hit Stop in my IDE. This is behaviour I would like just the additional ability to Start/Stop the process.
UPDATE-1:
following the comments from #dmitrievanthony and #RealSkeptic I have tried to implement something along those lines based on SO question, Executing a Java application in a separate process/636367,
public final class JavaProcess {
private JavaProcess() {}
public static int exec(Class klass) throws IOException,
InterruptedException {
String javaHome = System.getProperty("java.home");
String javaBin = javaHome +
File.separator + "bin" +
File.separator + "java";
String classpath = System.getProperty("java.class.path");
String className = klass.getName();
ProcessBuilder processBuilder = new ProcessBuilder(
javaBin,
"-cp",
classpath,
className
);
Map<String, String> env = processBuilder.environment();
env.put("java.endorsed.dirs", "/Users/xxx/dev/src/gitlab/xxx/myapp/target/classes/lib/endorsed");
env.put("myapp.home", "/private/var/tmp/myapp");
env.put("myapp.log.dir", "/private/var/tmp");
env.put("myapp.env", "/Users/xxx/dev/src/gitlab/xxx/myapp/target/classes/etc/examples/environment-apa.sh");
env.put("simplelogger.properties", "/private/var/tmp/myapp/etc/simplelogger.properties");
env.put("-XX:CompileThreshold", "2500");
env.put("-XX:+UseFastAccessorMethods", "");
env.put("-Xss256k", "");
env.put("-Xmx1g", "");
env.put("-Xms512m", "");
Process process = processBuilder.inheritIO().start();
process.waitFor();
return process.exitValue();
}
and calling it in my myAppIT test class as int status = JavaProcess.exec(MyAapp.class);
I can now see my class "MyApp" starting - and can confirm that the process flow is running into my MyApp.main() class. The problem now is that the System.env variables that I am setting in my ProcessBuilder do not appear to be available in the called programme ie. when I print to log System.getProperty("myapp.home") its returning null even though I can confirm that it is being set as shown in the code - does anyone have any ideas on this one please ?
UPDATE-2: I am trying to implement suggestion by #RealSkeptic and passing in the arguments in a similar way as passing commandline arguments as shown in the code snippet below. Now I am getting an exception
Error: Could not find or load main class xxx.xxx.xxx.xxx.MyApp -Djava.endorsed.dirs=.Users.xxx.dev.src.gitlab.myapp.myapp.target.classes.lib.endorsed
one problem I see is that the forward slashes of the path have been translated to ".". The path should read, Djava.endorsed.dirs=/Users/xxx/dev/src/gitlab/myapp/myapp/target/classes/lib/endorsed
ProcessBuilder processBuilder = new ProcessBuilder(
javaBin,
"-cp",
classpath,
className + " " +
"-Djava.endorsed.dirs=" + "/Users/xxx/dev/src/gitlab/xxx/myapp/target/classes/lib/endorsed " +
"-Dmyapp.home=/private/var/tmp/myapp " +
"-Dmyapp.log.dir=/private/var/tmp" +
"-Dmyapp.env=/Users/xxx/dev/src/gitlab/xxx/myapp/target/classes/etc/examples/environment-apa.sh " +
"-Dsimplelogger.properties=/private/var/tmp/myapp/etc/simplelogger.properties " +
"-server " +
"-XX:CompileThreshold=2500 " +
"-XX:+UseFastAccessorMethods " +
"-Xss256k " +
"-Xmx1g " +
"-Xms512m"
);
Update-3 following the last comment from #RealSkeptic I've modified my code (see below) and this now works.
ProcessBuilder processBuilder = new ProcessBuilder(
javaBin,
"-cp",
classpath,
"-Djava.endorsed.dirs=" + "/Users/xxx/dev/src/gitlab/xxx/myapp/target/classes/lib/endorsed",
"-Dmyapp.home=/private/var/tmp/myapp",
"-Dmyapp.log.dir=/private/var/tmp",
"-Dmyapp.env=/Users/xxx/dev/src/gitlab/xxx/myapp/target/classes/etc/examples/environment-apa.sh",
"-Dsimplelogger.properties=/private/var/tmp/myapp/etc/simplelogger.properties ",
"-server",
"-XX:CompileThreshold=2500",
"-XX:+UseFastAccessorMethods",
"-Xss256k",
"-Xmx1g",
"-Xms512m",
className
);
The below is copied from UPDATE-3 which I am posting as the answer. Thank you to those who responded and especially #RealSkeptic.
ProcessBuilder processBuilder = new ProcessBuilder(
javaBin,
"-cp",
classpath,
"-Djava.endorsed.dirs=" + "/Users/xxx/dev/src/gitlab/xxx/myapp/target/classes/lib/endorsed",
"-Drrc.home=/private/var/tmp/myapp",
"-Drrc.log.dir=/private/var/tmp",
"-Drrc.env=/Users/xxx/dev/src/gitlab/xxx/myapp/target/classes/etc/examples/environment-apa.sh",
"-Dsimplelogger.properties=/private/var/tmp/myapp/etc/simplelogger.properties ",
"-server",
"-XX:CompileThreshold=2500",
"-XX:+UseFastAccessorMethods",
"-Xss256k",
"-Xmx1g",
"-Xms512m",
className
);
I have refactored the above to put each of the arguments into a List so the call to ProcessBuilder reduces to,
ProcessBuilder processBuilder = new ProcessBuilder(arguments);
Process process = processBuilder.inheritIO().start();
To Stop the process you just need to call
process.destroy();
Hello working on a small program that just needs to run a python script I have. This python script will play a given .wav file, and draw a shape on the turtle screen. As such, I'm not looking for an output to be returned to java. Here is my java code:
public class Driver {
public static void main(String[] args){
try {
Process p = Runtime.getRuntime().exec("python " +
" D:/Coding Files/Python/MusicColors.py" +" teenagers.wav");
}
catch (Exception e){
System.out.println(e);
}
}
}
The exception I get is:
java.io.IOException: Cannot run program "python":
CreateProcess error=2, The system cannot find the file specified
I probably am making a very stupid mistake as I have limited knowledge in the subject of processes and such. I added python to my system path, so whenever I put "python" into command line, it returns with
Python 3.5.2 (v3.5.2:4def2a2901a5, Jun 25 2016, 22:01:18) [MSC v.1900 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
And makes it the python shell.
Here is the exact line I added to my environment path:
C:\Users\Joe\AppData\Local\Programs\Python\Python35-32
If anyone can figure out where I went wrong I'd really appreciate it!
The $PATH variable you've set is not inherited in Java's execution context. Try passing the Python's bin path to exec()'s execution environment.
To do this, the code below first retrieve all the environment variables and create an array of ENV_KEY=ENV_VALUE pairs.
Then, the path to your Python's bin is appended to the PATH value.
Finally, we pass the array of all environment variables to exec() (via the second parameter).
import java.util.HashMap;
import java.util.Map;
public class Driver {
public static void main(String[] args){
try {
String[] commands = {"python D:/Coding Files/Python/MusicColors.py teenagers.wav"};
// Get a list of all environment variables
final Map<String, String> envMap = new HashMap<String, String>(System.getenv());
// Append Python bin path to Path
envMap.put("Path", envMap.get("Path") + ";C:/Users/Joe/AppData/Local/Programs/Python/Python35-32");
// Convert to an array of ENV_KEY=ENV_VALUE format strings
final String[] envs = new String[envMap.size()];
int i = 0;
for (Map.Entry<String, String> e : envMap.entrySet()) {
envs[i] = e.getKey() + '=' + e.getValue();
i++;
}
// Exec with the environment variables
Process p = Runtime.getRuntime().exec(commands, envs);
}
catch (Exception e){
System.out.println(e);
}
}
}
I have two Java classes that are running commands on the local system. My dev system is a Mac, my QA system is Windows and the Prod system is UNIX. So there are different commands for each one, at the moment I have to go in and comment/uncomment the differences. Both classes are structured the same with executable and command. Here is what I have.
// Linux (QA/Prod)
final String executable = "/user1/Project/Manufacturer/CommandCLI";
// final String executable = "cat"; // Mac Dev
// final String executable = "cmd"; // Windows QA
final String command = "getarray model=" + model + " serialnum=" + serialnum;
// Windows QA(local laptop)
//final String command = "/C c:/Manufacturer/CommandCLI.bat getarray model=" + model + " serialnum=" + serialnum;
//Mac Dev
// final String command = "/TestData/" + computer.getId() + ".xml"
So, as you can see -- I am commenting and uncommenting depending on the environment. One of my main concerns is that I am relying on the model and serialnum variable -- and I don't know if that can somehow be inserted into a property (model and serialnum are given in the method call).
We are using Maven so during "mvn clean package" we are adding the -P flag to specify a properties file.
What is an elegant way to handle this?
I suggest to create 3 different method: one for each os that contains os-specific commands. And you can determine current os using system properties: check this question. And call appropriate method based on this property. Example:
private runOnLinux(int model, int serialNum) { ... }
private runOnWindows(int model, int serialNum) { ... }
private runOnMac(int model, int serialNum) { ... }
// Somewhere in source code...
String os = System.getProperty("os.name").toLowerCase();
if (os.contains("windows")) {
runOnWindows(model, serialNum);
} else if (os.contains("linux") || os.contains("unix")) {
runOnLinux(model, serialNum);
} else {
// Mac!
runOnMac(model, serialNum);
}
Of course I not sure all this checks are correct. Better check answers to the question I mentioned at the beginning. It contains much more useful information.
I am baffled....
I am using jdk1.6.0_24, Eclipse 3.6.2 on Windows7. Everything is 64bit.
The problem I am having is, the property (such as -Dmyki=helloDumbo) that I pass from Eclipse via the Run->Run Configuration->Arguments does not seems to get passed to my program.
As an example I have the following:
public static void main(String[] args)
{
String s = System.getProperty("myki");
System.out.println("myki = " + s);
System.out.println("Arg = " + args[0]);
}
And my output is:
myki = null
Arg = -Dmyki=helloDumbo
From the above it seems that Eclipse passed down the arguments I put into the JVM but why System.getProperty() returned null?
Make sure you put it in as a VM argument not a Program argument: