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