View Javadoc
1   /* ***************************************************************************
2    * Copyright (c) 2011 Brabenetz Harald, Austria.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   * 
16   *****************************************************************************/
17  package org.settings4j.connector;
18  
19  import java.util.prefs.BackingStoreException;
20  import java.util.prefs.Preferences;
21  
22  
23  /**
24   * Connector which uses the {@link Preferences} feature of Java.
25   * <p>
26   * Locations:
27   * <ul>
28   * <li>In W98/Me/NT/W2K/XP/W2003/Vista/W7-32/W7-64 this information is stored in the fragile, hard-to-back-up registry
29   * in HKEY_LOCAL_MACHINE\JavaSoft\Prefs for system Preferences and HKEY_CURRENT_USER\JavaSoft\Prefs for user Preferences
30   * in a very fluffy format. Every capital letter is preceded with a / and any fields containing accented letters are
31   * encoded in Base64.</li>
32   * <li>In Windows, user Preferences show up at HKEY_CURRENT_USER\Software\JavaSoft\Prefs\com\mindprod\replicator and
33   * HKEY_USERS\ usernamexxx\Software\JavaSoft\Prefs\com\mindprod\replicator where the package name is
34   * com.mindprod.replicator.</li>
35   * <li>In Windows, system Preferences show up at HKEY_LOCAL_MACHINE\Software\JavaSoft\Prefs\com\mindprod\replicator,
36   * where the package name is com.mindprod.replicator</li>
37   * <li>In Linux, preferences are stored in ordinary xml files. System Preferences are stored in etc/.java.</li>
38   * <li>In Linux, user preferences are stored in ~/.java. The file for user preferences may have a goofy base64-encoded
39   * name something like this:<br/>
40   * /home/username/.java/.userPrefs/ com/mindprod/replicator/_!':!bw
41   * "t!#4!b@"p!'4!~!"w!()!bw"k!#4!cg"l!(!!b!"p!'}@"0!'8!cg==</li>
42   * </ul>
43   * 
44   * @author Harald.Brabenetz
45   */
46  public class PreferencesConnector extends AbstractPropertyConnector {
47  
48      private final Preferences systemPrefs;
49  
50      private final Preferences userPrefs;
51  
52  
53      /**
54       * Default Constructor initialize the User and System {@link Preferences}.
55       */
56      public PreferencesConnector() {
57          super();
58          this.systemPrefs = Preferences.systemRoot();
59          this.userPrefs = Preferences.userRoot();
60      }
61  
62  
63      /** {@inheritDoc} */
64      @Override
65      protected String getProperty(final String keyPath, final String defaultValue) {
66          final String normalizedKey = normalizeKey(keyPath);
67          final String path = getPath(normalizedKey);
68          final String key = getKey(normalizedKey);
69          String value = getPreferenceValue(path, key, defaultValue, this.userPrefs);
70          if (value == null) {
71              value = getPreferenceValue(path, key, defaultValue, this.systemPrefs);
72          }
73          return value;
74      }
75  
76  
77  
78      private String getPath(final String keyPath) {
79          String path = null;
80          final int endOfPath = keyPath.lastIndexOf('/');
81          if (endOfPath != -1) {
82              path = keyPath.substring(0, endOfPath);
83          }
84          return path;
85      }
86  
87      /**
88       * Resolve the given path and key against the given Preferences.
89       * 
90       * @param path the preferences path (placeholder part before '/')
91       * @param key the preferences key (placeholder part after '/')
92       * @param defaultValue the default Value.
93       * @param preferences the Preferences to resolve against
94       * @return the value for the placeholder, or <code>null</code> if none found
95       */
96      protected String getPreferenceValue(final String path, final String key, final String defaultValue,
97              final Preferences preferences) {
98          if (path != null) {
99              // Do not create the node if it does not exist...
100             try {
101                 if (preferences.nodeExists(path)) {
102                     return preferences.node(path).get(key, defaultValue);
103                 }
104                 return defaultValue;
105             } catch (final BackingStoreException e) {
106                 throw new RuntimeException("Cannot access specified node path [" + path + "]", e);
107             }
108         }
109         return preferences.get(key, defaultValue);
110     }
111 
112 
113     /**
114      * Stores the given Value into the User-Preferences.
115      * 
116      * @param keyPath - The full KeyPath
117      * @param value - The new Value
118      */
119     public void setString(final String keyPath, final String value) {
120         final String normalizedKey = normalizeKey(keyPath);
121         final String path = getPath(normalizedKey);
122         final String key = getKey(normalizedKey);
123         setPreferenceValue(path, key, value, this.userPrefs);
124     }
125 
126     /**
127      * Stores the given Value into the System-Preferences.
128      * 
129      * @param keyPath - The full KeyPath
130      * @param value - The new Value
131      */
132     public void setSystemString(final String keyPath, final String value) {
133         final String normalizedKey = normalizeKey(keyPath);
134         final String path = getPath(normalizedKey);
135         final String key = getKey(normalizedKey);
136         setPreferenceValue(path, key, value, this.systemPrefs);
137     }
138 
139     /**
140      * Resolve the given path and key against the given Preferences.
141      * 
142      * @param path the preferences path (placeholder part before '/')
143      * @param key the preferences key (placeholder part after '/')
144      * @param value the Value to store.
145      * @param preferences the Preferences to resolve against
146      */
147     protected void setPreferenceValue(final String path, final String key, final String value,
148             final Preferences preferences) {
149         if (path != null) {
150             preferences.node(path).put(key, value);
151         } else {
152             preferences.put(key, value);
153         }
154         try {
155             preferences.flush();
156         } catch (BackingStoreException e) {
157             throw new RuntimeException("Cannot access specified node path [" + path + "]", e);
158         }
159     }
160 
161     private String getKey(final String keyPath) {
162         String key = keyPath;
163         final int endOfPath = keyPath.lastIndexOf('/');
164         if (endOfPath != -1) {
165             key = keyPath.substring(endOfPath + 1);
166         }
167         return key;
168     }
169 
170 
171     private String normalizeKey(final String key) {
172         if (key == null) {
173             return null;
174         }
175         String normalizeKey = key;
176 
177         normalizeKey = normalizeKey.replace('\\', '/');
178 
179         if (normalizeKey.startsWith("/")) {
180             normalizeKey = normalizeKey.substring(1);
181         }
182         return normalizeKey;
183     }
184 }