1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. 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 java.util.prefs;
18
19 import java.io.File;
20 import java.io.FilenameFilter;
21 import java.security.AccessController;
22 import java.security.PrivilegedAction;
23 import java.util.HashSet;
24 import java.util.Iterator;
25 import java.util.Properties;
26 import java.util.Set;
27
28 import org.apache.harmony.prefs.internal.nls.Messages;
29
30 /**
31 * The default implementation of {@code AbstractPreferences} for the Linux
32 * platform, using the file system as its back end.
33 *
34 * TODO some sync mechanism with backend, Performance - check file edit date
35 *
36 * @since 1.4
37 */
38 class FilePreferencesImpl extends AbstractPreferences {
39
40 // prefs file name
41 private static final String PREFS_FILE_NAME = "prefs.xml"; //$NON-NLS-1$
42
43 // home directory for user prefs
44 private static String USER_HOME;
45
46 // home directory for system prefs
47 private static String SYSTEM_HOME;
48
49 static {
50 AccessController.doPrivileged(new PrivilegedAction<Void>() {
51 @SuppressWarnings("nls")
52 public Void run() {
53 USER_HOME = System.getProperty("user.home") + "/.java/.userPrefs";
54 SYSTEM_HOME = System.getProperty("java.home") + "/.systemPrefs";
55 return null;
56 }
57 });
58 }
59
60 // file path for this preferences node
61 private String path;
62
63 // internal cache for prefs key-value pair
64 private Properties prefs;
65
66 // file represents this preferences node
67 private File prefsFile;
68
69 // parent dir for this preferences node
70 private File dir;
71
72 // cache for removed prefs key-value pair
73 private Set<String> removed = new HashSet<String>();
74
75 // cache for updated prefs key-value pair
76 private Set<String> updated = new HashSet<String>();
77
78 /**
79 * Construct root {@code FilePreferencesImpl} instance, construct user
80 * root if userNode is true, system root otherwise
81 */
82 FilePreferencesImpl(boolean userNode) {
83 super(null, ""); //$NON-NLS-1$
84 this.userNode = userNode;
85 path = userNode ? USER_HOME : SYSTEM_HOME;
86 initPrefs();
87 }
88
89 /**
90 * Construct a prefs using given parent and given name
91 */
92 private FilePreferencesImpl(AbstractPreferences parent, String name) {
93 super(parent, name);
94 path = ((FilePreferencesImpl) parent).path + File.separator + name;
95 initPrefs();
96 }
97
98 private void initPrefs() {
99 dir = new File(path);
100 newNode = (AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
101 public Boolean run() {
102 return Boolean.valueOf(!dir.exists());
103 }
104 })).booleanValue();
105 prefsFile = new File(path + File.separator + PREFS_FILE_NAME);
106 prefs = XMLParser.loadFilePrefs(prefsFile);
107 }
108
109 @Override
110 protected String[] childrenNamesSpi() throws BackingStoreException {
111 String[] names = AccessController
112 .doPrivileged(new PrivilegedAction<String[]>() {
113 public String[] run() {
114 return dir.list(new FilenameFilter() {
115 public boolean accept(File parent, String name) {
116 return new File(path + File.separator + name).isDirectory();
117 }
118 });
119
120 }
121 });
122 if (null == names) {// file is not a directory, exception case
123 // prefs.3=Cannot get children names for {0}
124 throw new BackingStoreException(
125 Messages.getString("prefs.3", toString())); //$NON-NLS-1$
126 }
127 return names;
128 }
129
130 @Override
131 protected AbstractPreferences childSpi(String name) {
132 FilePreferencesImpl child = new FilePreferencesImpl(this, name);
133 return child;
134 }
135
136 @Override
137 protected void flushSpi() throws BackingStoreException {
138 try {
139 // if removed, return
140 if (isRemoved()) {
141 return;
142 }
143 // reload
144 Properties currentPrefs = XMLParser.loadFilePrefs(prefsFile);
145 // merge
146 Iterator<String> it = removed.iterator();
147 while (it.hasNext()) {
148 currentPrefs.remove(it.next());
149 }
150 removed.clear();
151 it = updated.iterator();
152 while (it.hasNext()) {
153 Object key = it.next();
154 currentPrefs.put(key, prefs.get(key));
155 }
156 updated.clear();
157 // flush
158 prefs = currentPrefs;
159 XMLParser.flushFilePrefs(prefsFile, prefs);
160 } catch (Exception e) {
161 throw new BackingStoreException(e);
162 }
163 }
164
165 @Override
166 protected String getSpi(String key) {
167 try {
168 if (null == prefs) {
169 prefs = XMLParser.loadFilePrefs(prefsFile);
170 }
171 return prefs.getProperty(key);
172 } catch (Exception e) {
173 // if Exception happened, return null
174 return null;
175 }
176 }
177
178 @Override
179 protected String[] keysSpi() throws BackingStoreException {
180 final Set<Object> ks = prefs.keySet();
181 return ks.toArray(new String[ks.size()]);
182 }
183
184 @Override
185 protected void putSpi(String name, String value) {
186 prefs.setProperty(name, value);
187 updated.add(name);
188 }
189
190 @Override
191 protected void removeNodeSpi() throws BackingStoreException {
192 boolean removeSucceed = (AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
193 public Boolean run() {
194 prefsFile.delete();
195 return Boolean.valueOf(dir.delete());
196 }
197 })).booleanValue();
198 if (!removeSucceed) {
199 // prefs.4=Cannot remove {0}
200 throw new BackingStoreException(Messages.getString("prefs.4", toString())); //$NON-NLS-1$
201 }
202 }
203
204 @Override
205 protected void removeSpi(String key) {
206 prefs.remove(key);
207 updated.remove(key);
208 removed.add(key);
209 }
210
211 @Override
212 protected void syncSpi() throws BackingStoreException {
213 flushSpi();
214 }
215 }