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 }