Is it possible to initialize Java system properties using some sort of configuration file?
(ie: can you set java.library.path using a file inside your jar)
EDIT: Clarification: I am asking specifically about initializing the system properties to a value in a file, not setting them later from inside the virtual machine. Yes, you can change system properties to whatever you want very easily after the machine starts up, but the Java system classes will not use the new values.
Practically speaking, this means System.setProperty and System.setProperties are useless for loading native libraries, as JNI will always use the original value of java.library.path to load libraries with. I'm trying to figure out if there's a cleaner alternative to just putting -Djava.library.path=whatever in start up scripts everywhere.
There is a way to set java.library.path programatically, see this.
The code is a hack to set the sys_path field on the ClassLoader,
System.setProperty( "java.library.path", "/path/to/libs" );
Field fieldSysPath = ClassLoader.class.getDeclaredField( "sys_paths" );
fieldSysPath.setAccessible( true );
fieldSysPath.set( null, null );
It would be pretty simple to do yourself:
public static void main(String[] args) {
Properties p = new Properties();
p.load(...); // Load the properties from a file in your jar
for (String name : p.stringPropertyNames()) {
String value = p.getProperty(name);
System.setProperty(name, value);
}
}
Use JAVA_TOOL_OPTIONS environment variable. _JAVA_OPTIONS may also work, but it's not documented at all. The JAVA_TOOL_OPTIONS is weakly documented, but here are some links:
http://www.oracle.com/technetwork/java/javase/envvars-138887.html#gbmsy
https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/envvars002.html
https://bugs.openjdk.java.net/browse/JDK-4971166
An example of use:
set JAVA_TOOL_OPTIONS=-Dmy.property=sth
When launching JVM you should see a message:
Picked up JAVA_TOOL_OPTIONS: ...
You cannot initialize them as far as I know, but you can definitely override their values to anything you want, using any source you wish.
I think the reason why System.setProperty(key,value) is useless for java.library.path in your program is your application is started, you need set it before your program is running.
Check your native library, if the library have any dependency that not included in the java.library.path, System.load will fail, as if System.setProperty(key, value) does not work as expected.
You can parse properties file with additional code (another instance of JVM), redirect its output to var and use this variable as parameter to your java invocation. For example:
public class SystemPropertyLoader {
public static void main(String[] args) throws Exception {
String file = args[0];//TODO check args
Properties properties = new Properties();
InputStream is = new FileInputStream(file);
properties.load(is);
is.close();
StringBuilder builder = new StringBuilder();
for (Entry e : properties.entrySet()){
builder.append("-D");
builder.append(e.getKey());
builder.append('=');
builder.append(e.getValue());
builder.append(' ');
}
System.out.println(builder.toString());
}
}
and
#!/bin/ksh
properties_variable=$(java SystemPropertyLoader input.properties)
Related
I'm currently trying to test the UI of an Eclipse RCP application. When executed manually, the application starts fine and can be used correctly. However, when QF-Test launches the application, I get a ClassCastException in a 3pp module:
java.lang.ClassCastException: java.io.File cannot be cast to java.lang.String
at com.solarmetric.conf.ConfigurationImpl.fromProperties(ConfigurationImpl.java:560)
at com.solarmetric.conf.ConfigurationImpl.loadDefaults(ConfigurationImpl.java:186)
After analyzing the code of the 3pp library I see that the exception occurs when trying to cast a System's property value to a String. This shouldn't be a problem because all properties values should be String (see this answer). However, QF-Test is adding 3 properties which their values are File (java.io.File) objects. More precisely:
jython.home = C:\Program Files\qfs\qftest\qftest-4.2.0\jython
groovy.home = C:\Program Files\qfs\qftest\qftest-4.2.0\groovy
javascript.home = C:\Program Files\qfs\qftest\qftest-4.2.0\javascript
I would like to remove those wrong property values. I've already tried to define them manually as parameters of the QF-Test command line call without success.
Some help would be very appreciated.
This behaviour of QF-Test was fixed with QF-Test 4.2.1 (released February 26, 2018), see https://www.qfs.de/en/qf-test-manual/lc/manual-en-history.html#sec_N1D715:
Bug fixed:
In a few cases a broken system property set by QF-Test could interfere with SUT startup.
So the answer is to simply update your QF-Test!
Unfortunately, I do not know a fix for QF-Test.
If possible, I recommend the workaround to correct the properties before use.
Properties sysProps = System.getProperties();
Properties copyProps = new Properties();
synchronized (sysProps) {
copyProps.putAll(sysProps);
}
Set<Entry<Object, Object>> entrySet = copyProps.entrySet();
for (Entry<Object, Object> entry : entrySet) {
if (!(entry.getKey() instanceof String) || !(entry.getValue() instanceof String)) {
sysProps.remove(entry.getKey());
sysProps.put(String.valueOf(entry.getKey()), String.valueOf(entry.getValue()));
}
}
Is there an existing Java solution to resolve file paths containing environment variables?
I need to convert this (simplified) method to handle the case where the provided path contains an environment variable (like %PROGRAMFILES(X86)%\SomeProgram\SomeFile.ini).
I know how to write code to extract the variable names, fetch their values and substitute them in the provided string but I can't help but feel I'm reinventing the wheel...
boolean isValidPath(String pathToValidate) {
File f = new File(pathToValidate);
if(f.exists()) {
return true;
}
return false;
}
Almost sure it does not exist in standard library simply because any environment variable is very specific in its usage. Some allow lists delimited by semicolons like PATH, some - only one single value. Also relying on values of env vars means that code becomes more platform-specific, and jre seems to avoid it at any reasonable cost.
There are already a couple of implementations of such substitutors on github, for example https://github.com/Haufe-Lexware/java-env-replacer.
You can actually get the environment variables details by calling System.getenv() and then you can write some logic (sample shown below) to achieve what you wanted:
public boolean isValidPath(String pathToValidate) {
String envValue = "";
for (String envName : env.keySet()) {
if(pathToValidate.contains(envName)) {
envValue= env.get(envName);
break;
}
}
//replace the envValue in pathToValidate
//Then check file exists or not & return true/false
}
steps I do:
I do in code
System.setProperty("myproperty", 1);
and then I set in a shell script the property "myProperty" to 3.
like this:
# setprop "myproperty" 3
and then in the code I try to read the property like this:
System.getProperty("myproperty");
I get the value of 1. which means that the set from the shell didn't actually work.
but when I print all props from shell with
# getprop
I see in the list that myproperty equals 3.
in shorter words: I want to change the value of a property from a script, and I see that this scripts actually changes the property but in the java code I get the old value.
any ideas?
Java code in Android provides System.getProperty and System.setProperty functions in java library but it's important to note that although these java APIs are semantically equal to native version, java version store data in a totally different place. Actually, a hashtable is employed by dalvik VM to store properties. So, java properties are separated, it can't get or set native properties, and neither vice versa.
You can use android.os.SystemProperties class can manipulate native properties, though it's intended for internal usage only. It calls through jni into native property library to get/set properties.
getprop/setprop work on android.os.SystemProperties, not on java.lang.System.
Unfortunately, this class is not available to third party application. Apparently you have rooted your device, so you may still access it.
You can use that snippet to run getProp as shell command and get the value of any property:
private String getSystemProperty(String propertyName) {
String propertyValue = "[UNKNOWN]";
try {
Process getPropProcess = Runtime.getRuntime().exec("getprop " + propertyName);
BufferedReader osRes =
new BufferedReader(new InputStreamReader(getPropProcess.getInputStream()));
propertyValue = osRes.readLine();
osRes.close();
} catch (Exception e) {
// Do nothing - can't get property value
}
return propertyValue;
}
I made a program in Java that uses two external databases. The paths to these databases are hard-coded inside my program code.
In order to make this program usable for other users on other computers (who should also install these two databases on their computers), I think that the path for these two databases should be added as environmental variables ? How could this be done ?
I am not a professional when it comes to environmental variables, so can you please advise what should be done in this case?
Thanks in advance
To get the value of an environment variable in Java, you write something like this:
String pathToDatabase = System.getenv().get("PATH_TO_DATABASE");
(where PATH_TO_DATABASE is the name of the environment variable). This uses System.getenv() to get a map of all environment variables.
To set the value of an environment variable in Linux, your users can write something like this:
export PATH_TO_DATABASE=/this/is/the/path/to/the/database
before running the program.
Environment vars are usually not be the best way to handle app config, but if you must, the specific OS docs are needed to learn how to set them and from Java use:
Map map = System.getenv();
Rather than environment variables, a properties file would be useful and more portable. For example, in your properties file you could have the following:
db.url = jdbc://foo/bar?whatever
db.user = username
db.password = password
Then your code could read that in using the follow:
Properties properties = new Properties();
try {
properties.load(new FileInputStream("path/filename"));
} catch (IOException e) {
System.err.println( "Eeeek!" );
}
System.out.println( properties.getProperty( "db.url" ) );
Handily, properties objects allow you to specify defaults, so you could still have the hardcoded values if you want and then override them with the external file.
Can I set java.library.path programmatically from java code itself?
The following doesn't work.
System.setProperty("java.library.path", "/blah");
Maybe this will help: Setting "java.library.path" programmatically
When messing around with JNI, one has to set the java.library.path accordingly.
Unfortunately the only way is to add a system property before the application is started:
java -Djava.library.path=/path/to/libs
Changing the system property later doesn’t have any effect, since the property is evaluated very early and cached. But the guys over at jdic discovered a way how to work around it. It is a little bit dirty – but hey, those hacks are the reason we all love Java…
System.setProperty( "java.library.path", "/path/to/libs" );
Field fieldSysPath = ClassLoader.class.getDeclaredField( "sys_paths" );
fieldSysPath.setAccessible( true );
fieldSysPath.set( null, null );
Explanation
At first the system property is updated with the new value. This might be a relative path – or maybe you want to create that path dynamically.
The Classloader has a static field (sys_paths) that contains the paths. If that field is set to null, it is initialized automatically. Therefore forcing that field to null will result into the reevaluation of the library path as soon as loadLibrary() is called…
No you can't. This property is a read only value. You can change it at JVM launchin time with:
-Djava.library.path=your_path
If you want to load a library from a specific location, you can use System.load(libraryPath) instead with the full path to the library.
I'm just quoting from the link provided by secmask (https://cedarsoft.com/blog.html), in case the link goes dead:
Changing the system property java.library.path later doesn’t have
any effect, since the property is evaluated very early and cached. But
the guys over at jdic discovered a way how to work around it. It is a
little bit dirty – but hey, those hacks are the reason we all love
Java.
System.setProperty("java.library.path", "/path/to/libs");
Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths");
fieldSysPath.setAccessible(true);
fieldSysPath.set(null, null);
Explanation:
At first the system property is updated with the new value. This might be a relative path – or maybe you want to create that path dynamically. The Classloader has a static field (sys_paths) that contains the paths. If that field is set to null, it is initialized automatically.Therefore forcing that field to null will result into the reevaluation of the library path as soon as loadLibrary() is called.
Yes it will read the Environment Variables.
Following is the code for setting the Environment variable using the ini4j.
import java.io.IOException;
import org.ini4j.Reg;
public class SettingWinEnvironmentUsing_ini4j {
public static void main(String args[])
{
System.out.println("Setting System Environment Variables");
Reg reg = new Reg();
Reg.Key env = reg.add("HKEY_CURRENT_USER\\Environment");
env.put("RR_PROPERTY_PATH", "c:\\path");
try {
reg.write();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(env.get("RR_PROPERTY_PATH"));
}
}
You can find the ini4j jar at
http://cropforge.org/plugins/scmsvn/viewcvs.php/IAPlugin/lib/ini4j-0.5.2-SNAPSHOT.jar?rev=656&root=icisjavatools&view=log
import java.util.Map;
public class ReadingEnvironment {
public static void main(String[] args) {
System.out.println("Reading System Environment Variables:\n");
// System.out.println(System.getenv());
Map<String, String> env = System.getenv();
for (String envName : env.keySet()) {
System.out.format("%s=%s%n", envName, env.get(envName));
}
}
}