[Info-vax] configuration of Java programs on VMS - a little trick (slightly long post)

Arne Vajhøj arne at vajhoej.dk
Wed Feb 2 19:26:12 EST 2022


Properties files are pretty easy.

Let us say that the program looks like:

import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;

public class PropsDemo {
     public static void main(String[] args) throws IOException {
         Properties p = new Properties();
         FileInputStream fis = new FileInputStream("props.properties");
         p.load(fis);
         fis.close();
         int a = Integer.parseInt(p.getProperty("demo.a"));
         System.out.println("a = " + a);
         String b = p.getProperty("demo.b");
         System.out.println("b = " + b);
         int c = Integer.parseInt(p.getProperty("demo.sub.c"));
         System.out.println("c = " + c);
         String d = p.getProperty("demo.sub.d");
         System.out.println("d = " + d);
         System.out.println(p.getProperty("demo.src", "**** problem ****"));
     }
}

just put a props.properties file in current dir with:

demo.a = 123
demo.b = ABC
demo.sub.c = 456
demo.sub.d = DEF
demo.src = Properties-file

It cannot be much easier than that.

Let us say that the program use an XML properties file like:

import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;

public class PropsDemoX {
     public static void main(String[] args) throws IOException {
         Properties p = new Properties();
         FileInputStream fis = new FileInputStream("props.xml");
         p.loadFromXML(fis);
         fis.close();
         int a = Integer.parseInt(p.getProperty("demo.a"));
         System.out.println("a = " + a);
         String b = p.getProperty("demo.b");
         System.out.println("b = " + b);
         int c = Integer.parseInt(p.getProperty("demo.sub.c"));
         System.out.println("c = " + c);
         String d = p.getProperty("demo.sub.d");
         System.out.println("d = " + d);
         System.out.println(p.getProperty("demo.src", "**** problem ****"));
     }
}

just put a props.xml file in current dir with:

<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
     <entry key="demo.a">123</entry>
     <entry key="demo.b">ABC</entry>
     <entry key="demo.sub.c">456</entry>
     <entry key="demo.sub.d">DEF</entry>
     <entry key="demo.src">XML-properties-file</entry>
</properties>

It is a bit more verbose but still OK.

But what if the Java program has decided to use the preferences API?

That API is a creation in the same category as the triangular wheel.

Code in same style as above:

import java.util.prefs.Preferences;

public class PrefsDemo {
     public static void main(String[] args) throws Exception {
         Preferences prefs = Preferences.userRoot();
         int a = prefs.node("demo").getInt("a", 0);
         System.out.println("a = " + a);
         String b = prefs.node("demo").get("b", "");
         System.out.println("b = " + b);
         int c = prefs.node("demo/sub").getInt("c", 0);
         System.out.println("c = " + c);
         String d = prefs.node("demo/sub").get("d", "");
         System.out.println("d = " + d);
         System.out.println(prefs.node("demo").get("src", "**** problem 
****") + " / " + prefs.getClass().getName());
     }
}


that requires a [username._java._userprefs.demo]prefs.xml with:

<!DOCTYPE map SYSTEM "http://java.sun.com/dtd/preferences.dtd">
<map>
     <entry key="a" value="123"/>
     <entry key="b" value="ABC"/>
     <entry key="src" value="XML-preference-file"/>
</map>

and a [username._java._userprefs.demo.sub]prefs.xml with:

<!DOCTYPE map SYSTEM "http://java.sun.com/dtd/preferences.dtd">
<map>
     <entry key="c" value="456"/>
     <entry key="d" value="DEF"/>
</map>

I don't like that way to store preferences.

The only positive is that it is even worse on Windows where it uses the
Windows registry.

Talk about hiding the configuration.

Probably the only good thing about the Preferences API is that
it comes with a SPI so one can easily change the implementation.

There are implementations using ini-file in same dir, XML-fil in
same dir, database etc..

But I wanted an implementation using VMS logicals.

That is a common way to control stuff on VMS. Despite some
not liking it.

EnvPreferencesFactory.java:

import java.util.prefs.Preferences;
import java.util.prefs.PreferencesFactory;

public class EnvPreferencesFactory implements PreferencesFactory {
     public Preferences systemRoot() {
         return new EnvPreferences(null, "", "systemprefs");
     }
     public Preferences userRoot() {
         return new EnvPreferences(null, "", "userprefs_" + 
System.getProperty("user.name"));
     }
}

EnvPreferences.java:

import java.util.prefs.AbstractPreferences;
import java.util.prefs.BackingStoreException;

public class EnvPreferences extends AbstractPreferences {
     private String tree;
     private String name;
     public EnvPreferences(AbstractPreferences parent, String name, 
String tree) {
         super(parent, name);
         this.tree = tree;
         this.name = name;
     }
     @Override
     protected void putSpi(String key, String value) {
         throw new RuntimeException("Definition of properties not 
supported");
     }
     @Override
     protected String getSpi(String key) {
         String lognam = tree + name + "_" + key;
         return System.getenv(lognam.toLowerCase());
     }
     @Override
     protected void removeSpi(String key) {
         throw new RuntimeException("Remove of properties not supported");
     }
     @Override
     protected void removeNodeSpi() throws BackingStoreException {
         throw new RuntimeException("Remove of properties not supported");
     }
     @Override
     protected String[] keysSpi() throws BackingStoreException {
         throw new RuntimeException("Wildcard lookup of properties not 
supported");
     }
     @Override
     protected String[] childrenNamesSpi() throws BackingStoreException {
         throw new RuntimeException("Wildcard lookup of properties not 
supported");
     }
     @Override
     protected AbstractPreferences childSpi(String name) {
         return new EnvPreferences(this, this.name + "_" + name, tree);
     }
     @Override
     protected void syncSpi() throws BackingStoreException {
         // nothing
     }
     @Override
     protected void flushSpi() throws BackingStoreException {
         // nothing
     }
}

And now we can:

$ define/nolog userprefs_arne_demo_a 123
$ define/nolog userprefs_arne_demo_b "ABC"
$ define/nolog userprefs_arne_demo_sub_c 456
$ define/nolog userprefs_arne_demo_sub_d "DEF"
$ define/nolog userprefs_arne_demo_src "VMS-logicals"
$ define/nolog java$getenv_process_list 
userprefs_arne_demo_a,userprefs_arne_demo_b,userprefs_arne_demo_sub_c,userprefs_arne_demo_su
b_d,userprefs_arne_demo_src
$ define/nolog decc$efs_case_preserve enable
$ java "-Djava.util.prefs.PreferencesFactory=EnvPreferencesFactory" -cp 
. "PrefsDemo"

And voila - that preference API using program is using VMS logicals.

A few notes:
* this trick does not require any changes to the program so it can
   be done with a program only available as binary
* this preferences implementation only allows simple get, not listing of
   all or setting, but most configuration usage is just getting
* the java$getenv_process_list thing is because apparently some
   HP engineers was concerned about allowing Java applications to
   call getenv on anything for security reasons - it does not make
   any sense to me, but ...
* the jdecc$efs_case_preserve is necessary because otherwise
   all values get converted to lowercase - even though the
   logical may contain something that is not a file or directory name

Arne






More information about the Info-vax mailing list