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 }