Are Java system properties always non-null? - java

There are a couple of Java system properties, among them things like java.home and path.separator, user.home. The spec does not mention any formal promises on the existence of those values.
Especially I am interested in user.home. Does it always point to some existing path?

I think you can safely assume that all the properties in that list is always available in any recent (Oracle-provided) JVM.
However, a null check is more defensive, and not expensive in this case.
I've never seen user.home to be null or be incorrectly specified by default. However, keep in mind that users can override with -Duser.home=..., so you can't rely on it to point to an existing path.

The documentation you pointed out states
The getProperty method returns a string containing the value of the property. If the property does not exist, this version of getProperty returns null.
So if the property does not exist you get a null value

The spec says that user.home contains user home directory, it does not say it may contain null. I have no doubt that JVM guarantees that it is always set.

The default properties will differ depending on OS. There will be some keys for which no values are defined. On my machine I found user.variant and user.timezone without any values! Following is the code which will list down all the key value pairs:
Properties prop = System.getProperties();
Set<Object> set = prop.keySet();
Iterator<Object> itr = set.iterator();
while(itr.hasNext()){
Object obj = itr.next();
String propVal = System.getProperty(obj.toString());
System.out.println(obj.toString()+" = "+propVal);
}
}
Regarding your specific reference about user.home, it seems it defined most of the time. Check out this interesting post where people have posted the list of system properties on different machines.

Related

Migration from JDK8 to JDK11 causing java.lang.UnsupportedOperationException

You you can see from the class name properties.stringPropertyNames() returns an Collections$UnmodifiableCollection. Unmodifiable means you can't add, insert, remove or change something on this collection
Now that you have explained what the type of properties is java.util.Properties the answer is clear. In Java 8, Properties.stringPropertyNames() returned a set whose modifiability was unspecified:
"Returns a set of keys in this property list where the key and its corresponding value are strings, including distinct keys in the default property list if a key of the same name has not already been found from the main properties list. Properties whose key or value is not of type String are omitted."
"The returned set is not backed by the Properties object. Changes to this Properties are not reflected in the set, or vice versa.".
In Java 11, it is specified to be unmodifiable.
"Returns an unmodifiable set of keys from this property list where the key and its corresponding value are strings, including distinct keys in the default property list if a key of the same name has not already been found from the main properties list. Properties whose key or value is not of type String are omitted."
"The returned set is not backed by this Properties object. Changes to this Properties object are not reflected in the returned set."
The change happened in Java 9. This is related to bug 8059361.
(This is was an incompatible change in the sense that it could break customers' code if they relied on being able to modify the returned set. However, that code was relying on unspecified behavior, so you could argue that it was broken already.)
As i understand correctly, you have unmodifiable set and you are tying to change it here
parameterNames.removeIf(s -> !s.startsWith(seq));
that's why you getting this error/exception.
Set<String> parameterNames = new HashSet<>(properties.stringPropertyNames());
This will create a new Set with same content which can be altered.

Using Groovy for variable expansion in Java properties

I frequently use standard Java property files for configuring my Groovy applications. One feature I have been missing is the ability to use variables as part of the property value so they can be expand dynamically during use. I thought I could provide this functionality using the following design:
Use a special format to annotate the properties that should be expanded. I have chosen to enclose such templates in double exclamation marks (!!). These property values are essentially a template to be expanded with the local variables
Before using the properties in the application, use the groovy 'evaluate' method to expand application variables in the template
Re-assign the original property key to the new value before use
So, if I have a property file config.properties with properties like:
version=2.3
local_lib=!!${env['GROOVY_HOME']}/${configProps.getProperty('version')}/lib!!
The local_lib property will be expanded from the GROOVY_HOME environment variable and the version property value.
In my application, I have coded this as follows:
//Load the environment variables and configuration file
env=System.getenv()
configFile=new File('config.properties')
configProps= new Properties()
configProps.load(configFile.newDataInputStream())
//Replace configuration property values with their expanded equivalent
configProps.each{
//if a property value is a template we evaluate it
if (it.value.startsWith('!!')){
valTemplate=it.value.replace('!!','"')
it.value=evaluate(valTemplate)
}
}
//then we use the expanded property values
This seems to work. When I do
println configProps
I see that the value is expanded and not null
However, the getProperty method for the expanded property returns null.
assert configProps.getProperty('local_lib')=='C:\\DEVTOOLS\\groovy-2.4.7/2.3/lib'
| | |
| null false
[local_lib:C:\DEVTOOLS\groovy-2.4.7/2.3/lib, version:2.3]
What is causing this discrepancy? I would have expected to return the value shown in the property map.
Your local_lib value looks like a String, but it isn't. It is a GString, only lazily coerced to String as needed (like when printing out the configProps map value).
Thus, a little known effect of Properties.getProperty() takes effect here. When the actual map value is not a String, Properties.getProperty() returns null.
So, in order to get the desired behavior, you need to coerce the GString to String before you store the value in the property map. Like so:
it.value=evaluate(valTemplate).toString()
or
it.value=evaluate(valTemplate) as String
Then you should see the desired results downstream.

How do I pass an array or list of values to Java via system properties - and how do I access it?

Is there a way to pass a list of values into Java, using only one system property?
I'm thinking of something along the lines -DMY_LIST=val1,val2,val3 or -DMY_LIST={val1, val2}
Any ideas?
How would I access that in Java?
Update: I initially asked for environment variables, but actually meant system properties. Similar principle, but quite different... I changed title and text accordingly now. Thanks, #serge-ballesta
From Oracle Javase tutorial
On the Java platform, an application uses System.getenv to retrieve environment variable values. Without an argument, getenv returns a read-only instance of java.util.Map, where the map keys are the environment variable names, and the map values are the environment variable values.
So if you have an environment variable MY_LIST=val1,val2,val3, you can use it as simple as
String strlist = System.getenv().get("MY_LIST");
List<String> list = Arrays.asList(strlist.split(","));
Edit: fixed call of getenv (forgot parentheses - thanks to Christian)
I must precise that my anwer concerns environment variable which is the title of the post. But java -D MY_LIST=a,b,c ... sets system properties and it in not the same.
To access system properties set by -D option, I should write instead :
String strlist = System.getProperty("MY_LIST");
List<String> list = Arrays.asList(strlist.split(","));

Difference between "get' VS "getProperty"

Properties myProp = new Properties();
myProp.put("material", "steel");
Properties prop1 = new Properties(myProp);
System.out.println(prop1.get("material") + ", " + prop1.getProperty("material"));
// outputs "null, steel"
Isn't get similar to getProperty in the sense that it returns the entries/attributes of
an Object? Why is it not returning 'steel' when using get?
get is inherited from Hashtable, and is declared to return Object.
getProperty is introduced by Properties, and is declared to return String.
Note thatgetProperty will consult the "defaults" properties which you can pass into the constructor for Properties; get won't. In most cases they'll return the same value though. In the example you've given, you are using a default backing properties:
prop1 doesn't directly contain an entry for "material", hence why get is returning null.
myProp does contain an entry for "material", so when you call prop1.getProperty("material"), it will find that it doesn't have it directly, and check in myProp instead, and find "steel" there.
A look at the docs shows that get is inherited, and returns Object whereas getProperty is a member of Properties and returns the String.
Seemingly they should return the same, however from the docs again:
If the key is not found in this property list, the default property list, and its defaults, recursively, are then checked.
So it is best to use getProperty as it will return the default if it is not found.
Your second Properties object (props or prop1?), has no properties added directly to it. It only uses myProp as defaults. So those values never get added to the backing HashMap. Properties.getProperty() doesn't find "material" in the backing HashMap so it can look in the defaults. But the inherited HashMap.get() only looks in the backing HashMap and not in the defaults that you passed into the constructor.

Empty list in App Engine Datastore: Java vs Python

I have the following java model class in App Engine:
public class Xyz ... {
#Persistent
private Set<Long> uvw;
}
When saving an object Xyz with an empty set uvw in Java, I get a "null" field (as listed in the appengine datastore viewer).
When I try to load the same object in Python (through remote_api), as defined by the following python model class:
class Xyz(db.Model):
uvw = db.ListProperty(int)
I get a "BadValueError: Property uvw is required".
When saving another object of the same class in Python with an empty uvw list, the Datastore viewer prints a "missing" field.
Apparently empty lists storage handling differs between Java and Python and lead to "incompatible" objects.
Thus my question: Is there a way to, either:
force Java to store an empty list as a "missing" field,
force Python to gracefully accept a "null" list as an empty list when loading the object?
Or any other suggestion on how to handle empty list field in both languages.
Thanks for your answers!
It should work if you assign a default value to your Python property:
uvw = db.ListProperty(int, default=[])
I use the low-level java api, so perhaps what I am doing would be different. But before I save a collection-type data structure to the datastore, I convert it into something that the datastore naturally handles. This would include mainly Strings and ByteArrays.
It sounds like java app engine is interpreting the empty set as a null value. And python is not reading this null value correctly. You might try saving an empty set as the String value "empty set". And then have python check to see if the datastore holds that string value. If it does, it could allocate a new empty set, if not, it could read the property as a set.
The Java Set behavior is because Java's Collections are reference types, which default to being null.
To actually create an empty Set, declare it like this:
#Persistent
private Set<Long> uvw = new HashSet<Long>();
or using some other implementation of Set on the right side. HashSet is the most commonly used Set type, though. Other interesting set types are the two thread-safe Sets CopyOnWriteArraySet and ConcurrentSkipListSet; also the Ordered Set type LinkedHashSet and the Sorted Set type TreeSet.
It may work to you
uvw = db.ListProperty(int, default=[])
Its the most comment way to short it out...

Categories

Resources