Welcome to Newsletter Volume 3

Here the goal is to Ensure preferences works equally well for developers, testers, and Web Start users.

Previous|Next

Deploying with Java Web Start - Part 2: Saving Configuration Data

In Part 1 of this series we discussed minimizing the differences from development time through to deployment time. The goal is to provide an infrastructure that works for many platforms and that works for everyone including developers, build testers, and end users. The assumption is that the deployment will be a signed Java Web Start application running in the "all-permissions" mode. In this part we will present code (Listing 3) that builds on the JnlpServices class from Part 1 and provides a mechanism for storing a small amount of configuration data.

If you plan to use this preferences implementation in a signed Java Web Start application, then please be aware that when your user uninstalls your application the muffin and the data will remain. For the most part this should be the desired behavior, but this is a series about deployment and one of the challenges that will be discussed in Part 3 will examine an exception to this rule.

Listing 3: JnlpPreferencesFactory class

package ca.ansir.jnlp;

import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Properties;
import java.util.prefs.AbstractPreferences;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;
import java.util.prefs.PreferencesFactory;

/**
* The
<code>JnlpPreferencesFactory</code> provides a muffin implementation of
* a
<code>PreferencesFactory</code> for the storage of a small amount of
* configuration data. This class can be compiled without referencing the
* javaws.jar.
*
<p>
* <b>Usage</b>
* </p>
* <p>
*
*
<pre><code>
* boolean jnlpPresent = true;
* try {
*     Class.forName(&quot;javax.jnlp.ServiceManager&quot;);
*     System.setProperty(&quot;java.util.prefs.PreferencesFactory&quot;,
*             JnlpPreferencesFactory.class.getName());
* } catch (Exception e) {
*     jnlpPresent = false;
* }
* Preferences prefs = Preferences.userRoot();
*
</code></pre>
*
*
</p>
* <p>
* In the above code example the a muffin Preference implementation will be used
* only when running in the Java Web Start environment, otherwise, the default
* Preference implementation will be used.
*
</p>
* <p>
* Please feel free to use this code snippet (<code>JnlpPreferencesFactory</code>)
* in developing your own commercial or non-commercial applications, but please
* credit the author, Dan Andrews, for any portion of this code that you use,
* and provide a reference to <a href="http://www.ansir.ca">Ansir
</a>. Thank
* you.
*
</p>
*
*
@author Dan Andrews
*/
public class JnlpPreferencesFactory implements PreferencesFactory {

   
/** Not supported message. */
   
private static final String SYSTEM_ROOT_NOT_SUPPORTED = "System root not supported";

   
/** Persistence service delete method name. */
   
private static final String PERSISTENCE_DELETE_METHOD = "delete";

   
/** Persistence service getNames method name. */
   
private static final String PERSISTENCE_GET_NAMES_METHOD = "getNames";

   
/** Persistence service get method name. */
   
private static final String PERSISTENCE_GET_METHOD = "get";

   
/** Persistence service create method name. */
   
private static final String PERSISTENCE_CREATE_METHOD = "create";

   
/** File contents getOutputStream method name. */
   
private static final String CONTENTS_GET_OUTPUT_STREAM_METHOD = "getOutputStream";

   
/** File contents getInputStream method name. */
   
private static final String CONTENTS_GET_INPUT_STREAM_METHOD = "getInputStream";

   
/** The <code>PersistenceService</code> object. */
   
private Object persistenceService = null;

   
/** The user root. */
   
private Preferences userRoot;

   
/**
     * Constructor
     *
     *
@throws Exception
     *             if invoked outside of a web start application.
     */
   
public JnlpPreferencesFactory() throws Exception {
       
persistenceService = JnlpServices.getPersistenceService();

        URL codeBase = JnlpServices.getCodeBase
();

        userRoot =
new JnlpPreferences(codeBase);
   
}

   
/**
     * Not allowed.
     */
   
public Preferences systemRoot() {
       
throw new IllegalAccessError(SYSTEM_ROOT_NOT_SUPPORTED);
   
}

   
/**
     * Gets the user root.
     *
     *
@return the user root <code>Preferences</code> object
     */
   
public Preferences userRoot() {
       
return userRoot;
   
}

   
/**
     * Preferences implementation modified from code posted by Dale King
     * (http://www.javakb.com/Uwe/Forum.aspx/java-programmer/2979/
     * Where-is-javaws-exe)
     */
   
private class JnlpPreferences extends AbstractPreferences {

       
private Object contents = null;

       
private Properties props = null;

       
private final URL url;

       
public JnlpPreferences(URL url) {
           
super(null, "");
           
this.url = url;
       
}

       
private JnlpPreferences(JnlpPreferences parent, String name)
               
throws MalformedURLException {
           
super(parent, name);
           
this.url = new URL(parent.url, name);
       
}

       
private Properties getProperties() {
           
if (props == null) {
               
props = new Properties();
               
try {
                   
Method createMethod = persistenceService.getClass()
                           
.getMethod(PERSISTENCE_CREATE_METHOD,
                                   
new Class[] { URL.class, Long.TYPE });
                    createMethod.invoke
(persistenceService, new Object[] { url,
                           
new Long(8096) });
                   
// persistenceService.create(url, 8096);
               
} catch (Exception e) {
                   
// will throw if already exists
               
}
               
try {
                   
Method getMethod = persistenceService.getClass().getMethod(
                           
PERSISTENCE_GET_METHOD, new Class[] { URL.class });
                    contents = getMethod.invoke
(persistenceService,
                           
new Object[] { url });
                   
// contents = persistenceService.get(url);

                   
Method getInputStreamMethod = contents.getClass()
                           
.getMethod(CONTENTS_GET_INPUT_STREAM_METHOD,
                                   
new Class[0]);
                    InputStream inputStream =
(InputStream) getInputStreamMethod
                            .invoke
(contents, new Object[0]);
                    props.load
(inputStream);
                   
// props.load(contents.getInputStream());
               
} catch (Exception e) {
                   
e.printStackTrace();
               
}
            }
           
return props;
       
}

       
protected String[] childrenNamesSpi() throws BackingStoreException {
           
String[] names = null;
           
try {

               
Method namesMethod = persistenceService.getClass()
                       
.getMethod(PERSISTENCE_GET_NAMES_METHOD,
                               
new Class[] { URL.class });
                names =
(String[]) namesMethod.invoke(persistenceService,
                       
new Object[] { url });
               
// return persistenceService.getNames(url);
           
} catch (Exception e) {
               
throw new BackingStoreException(e);
           
}
           
return names;
       
}

       
protected AbstractPreferences childSpi(String name) {
           
try {
               
return new JnlpPreferences(this, name);
           
} catch (MalformedURLException e) {
               
throw new RuntimeException(e);
           
}
        }

       
protected void flushSpi() throws BackingStoreException {
           
if (props != null) {
               
try {
                   
Method getInputStreamMethod = contents.getClass()
                           
.getMethod(CONTENTS_GET_OUTPUT_STREAM_METHOD,
                                   
new Class[] { Boolean.TYPE });
                    OutputStream outputStream =
(OutputStream) getInputStreamMethod
                            .invoke
(contents, new Object[] { Boolean.TRUE });
                    props.store
(outputStream, "");
                   
// props.store(contents.getOutputStream(true), "");
               
} catch (Exception e) {
                   
throw new BackingStoreException(e);
               
}
            }
        }

       
protected String getSpi(String key) {
           
return getProperties().getProperty(key);
       
}

       
protected String[] keysSpi() throws BackingStoreException {
           
return (String[]) getProperties().keySet().toArray(new String[0]);
       
}

       
protected void putSpi(String key, String value) {
           
getProperties().put(key, value);

           
try {
               
flushSpi();
           
} catch (Exception e) {
            }
        }

       
protected void removeNodeSpi() throws BackingStoreException {
           
try {
               
Method deleteMethod = persistenceService.getClass().getMethod(
                       
PERSISTENCE_DELETE_METHOD, new Class[] { URL.class });
                deleteMethod.invoke
(persistenceService, new Object[] { url });
               
// persistenceService.delete(url);
           
} catch (Exception e) {
               
throw new BackingStoreException(e);
           
}
        }

       
protected void removeSpi(String key) {
           
getProperties().remove(key);
       
}

       
protected void syncSpi() throws BackingStoreException {
           
flushSpi();
       
}
    }

}