Source code: hk/hku/cecid/phoenix/common/util/Property.java
1 /*
2 * Academic Free License
3 * Version 1.0
4 *
5 * This Academic Free License applies to any software and associated
6 * documentation (the "Software") whose owner (the "Licensor") has placed the
7 * statement "Licensed under the Academic Free License Version 1.0" immediately
8 * after the copyright notice that applies to the Software.
9 *
10 * Permission is hereby granted, free of charge, to any person obtaining a copy
11 * of the Software (1) to use, copy, modify, merge, publish, perform,
12 * distribute, sublicense, and/or sell copies of the Software, and to permit
13 * persons to whom the Software is furnished to do so, and (2) under patent
14 * claims owned or controlled by the Licensor that are embodied in the Software
15 * as furnished by the Licensor, to make, use, sell and offer for sale the
16 * Software and derivative works thereof, subject to the following conditions:
17 *
18 * - Redistributions of the Software in source code form must retain all
19 * copyright notices in the Software as furnished by the Licensor, this list
20 * of conditions, and the following disclaimers.
21 * - Redistributions of the Software in executable form must reproduce all
22 * copyright notices in the Software as furnished by the Licensor, this list
23 * of conditions, and the following disclaimers in the documentation and/or
24 * other materials provided with the distribution.
25 * - Neither the names of Licensor, nor the names of any contributors to the
26 * Software, nor any of their trademarks or service marks, may be used to
27 * endorse or promote products derived from this Software without express
28 * prior written permission of the Licensor.
29 *
30 * DISCLAIMERS: LICENSOR WARRANTS THAT THE COPYRIGHT IN AND TO THE SOFTWARE IS
31 * OWNED BY THE LICENSOR OR THAT THE SOFTWARE IS DISTRIBUTED BY LICENSOR UNDER
32 * A VALID CURRENT LICENSE. EXCEPT AS EXPRESSLY STATED IN THE IMMEDIATELY
33 * PRECEDING SENTENCE, THE SOFTWARE IS PROVIDED BY THE LICENSOR, CONTRIBUTORS
34 * AND COPYRIGHT OWNERS "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
37 * LICENSOR, CONTRIBUTORS OR COPYRIGHT OWNERS BE LIABLE FOR ANY CLAIM, DAMAGES
38 * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
39 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE.
40 *
41 * This license is Copyright (C) 2002 Lawrence E. Rosen. All rights reserved.
42 * Permission is hereby granted to copy and distribute this license without
43 * modification. This license may not be modified without the express written
44 * permission of its copyright owner.
45 */
46
47 /* =====
48 *
49 * $Header: /ebxml/staff/cecid/cvs_repository/common/src/hk/hku/cecid/phoenix/common/util/Property.java,v 1.14 2002/12/13 03:59:03 kcyee Exp $
50 *
51 * Code authored by:
52 *
53 * kcyee [2002-05-27]
54 *
55 * Code reviewed by:
56 *
57 * username [YYYY-MM-DD]
58 *
59 * Remarks:
60 *
61 * =====
62 */
63
64 package hk.hku.cecid.phoenix.common.util;
65
66 import java.io.File;
67 import java.io.IOException;
68 import java.net.URL;
69 import java.util.Hashtable;
70 import java.util.Vector;
71
72 /**
73 * This is an abstract class for property file reader. We may implement
74 * XML based, or plain text based, or encrypted property file reader out
75 * of this abstract class. This interface is defining some common methods
76 * needed by all kinds of property readers.
77 *
78 * @author kcyee
79 * @version $Revision: 1.14 $
80 */
81 public abstract class Property {
82
83 /**
84 * Singleton instance of text property object for each location
85 */
86 private static Hashtable loadedProps = new Hashtable();
87
88 /**
89 * Cache of property values
90 */
91 protected Hashtable cache;
92
93 /**
94 * System property name for location property file
95 */
96 protected final static String PROP_HOME = "prop.home";
97
98 /**
99 * Default constructor
100 */
101 Property() {
102 cache = new Hashtable();
103 }
104
105 /**
106 * Gets the property value from cache. Try to see if the property value
107 * has been retrieved previously.
108 *
109 * @param path the path (key) of the property
110 * @return the property value given the path (key), null if not found
111 */
112 protected String getFromCache(String path) {
113 if (path == null) {
114 return null;
115 }
116 return (String) cache.get(path);
117 }
118
119 /**
120 * Saves the property value to cache.
121 *
122 * @param path the path (key) of the property
123 * @param value the property value of the specified property key
124 */
125 protected void saveToCache(String path, String value) {
126 if (path == null) {
127 return;
128 }
129
130 if (value == null) {
131 cache.remove(path);
132 }
133 else {
134 cache.put(path, value);
135 }
136 }
137
138 /**
139 * Removes the property value from cache.
140 *
141 * @param path the path (key) of the property
142 */
143 protected void removeFromCache(String path) {
144 saveToCache(path, null);
145 }
146
147 /**
148 * Reloads the property file.
149 */
150 public void reload() {
151 cache.clear();
152 }
153
154 /**
155 * Gets the property value given the path (key). Multiple values are
156 * referenced by the same path will be returned using a String array.
157 *
158 * @param path the path (key) of the property
159 * @return the property values given the path (key)
160 */
161 public abstract String[] getMultiple(String path);
162
163 /**
164 * Gets the property value given the path (key).
165 *
166 * @param path the path (key) of the property
167 * @return the property value given the path (key)
168 */
169 public abstract String get(String path);
170
171 /**
172 * Gets the property value given the path (key). If the property value
173 * is not found, the default value passed in is returned.
174 *
175 * @param path the path (key) of the property
176 * @param defaultValue the value to be returned if the property value
177 * is not found
178 * @return the property value given the path (key)
179 */
180 public abstract String get(String path, String defaultValue);
181
182 /**
183 * Sets the property value of the given path (key).
184 *
185 * @param path the path (key) of the property to be set
186 * @param value the property value to be set
187 */
188 public abstract void set(String path, String value);
189
190 /**
191 * Saves the property object to the same location when loading.
192 *
193 * @throws IOException when error occurs when saving the property object
194 */
195 public abstract void save() throws IOException;
196
197 /**
198 * Saves the property object to the specified location.
199 *
200 * @param location the location for saving the property object
201 * @throws IOException when error occurs when saving the property object
202 */
203 public abstract void save(String location) throws IOException;
204
205 /**
206 * Default file name of property file
207 */
208 protected static final String DEFAULT_FILENAME = "phoenix.properties.xml";
209
210 /**
211 * Loads the default property object out of the default location.
212 * The default filename is phoenix.properties.xml. We search for the
213 * default properties file in the following locations in that order:
214 * 1. The directory pointed by the system property prop.home
215 * 2. The directories in classpath
216 * 3. The current working directory
217 * 4. The current user's home directory
218 *
219 * @return the property object loaded
220 * @throws IOException when error occurs when loading the property object
221 */
222 public static synchronized Property load() throws IOException {
223 return load(DEFAULT_FILENAME);
224 }
225
226 /**
227 * Reloads the property object out of a given location. The property
228 * object will be reloaded even if it has been cached before. Currently
229 * only 2 types of property objects are supported: text-based and
230 * XML-based. The location is interpreted as file path in both
231 * cases. We search for the properties file in the following locations
232 * in that order:
233 * 1. The directory pointed by the system property prop.home
234 * 2. The directories in classpath
235 * 3. The current working directory
236 * 4. See if the location is a full path
237 * 5. The current user's home directory
238 *
239 * @param location the location for loading the property object
240 * @return the property object loaded
241 * @throws IOException when error occurs when loading the property object
242 */
243 public static synchronized Property reload(String location)
244 throws IOException {
245 loadedProps.remove(location);
246 return load(location);
247 }
248
249 /**
250 * Loads the property object out of a given location. Currently
251 * only 2 types of property objects are supported: text-based and
252 * XML-based. The location is interpreted as file path in both
253 * cases. We search for the properties file in the following locations
254 * in that order:
255 * 1. The directory pointed by the system property prop.home
256 * 2. The directories in classpath
257 * 3. The current working directory
258 * 4. See if the location is a full path
259 * 5. The current user's home directory
260 *
261 * @param location the location for loading the property object
262 * @return the property object loaded
263 * @throws IOException when error occurs when loading the property object
264 */
265 public static synchronized Property load(String location)
266 throws IOException {
267 if (location == null) {
268 throw new IOException("Cannot load <null>!");
269 }
270
271 if (loadedProps.get(location) != null) {
272 return (Property) loadedProps.get(location);
273 }
274
275 Vector locationTried = new Vector();
276
277 String fileSep = System.getProperty("file.separator");
278 String propDir = System.getProperty(PROP_HOME);
279 String curDir = (new File(".")).getCanonicalPath();
280 String homeDir = System.getProperty("user.home");
281
282 // try user-defined system property "prop.home"
283 try {
284 if (propDir != null) {
285 Property prop = loadFromFullPath(
286 propDir + fileSep + location);
287 loadedProps.put(location, prop);
288 System.err.println("Info: using property file in "
289 + propDir + fileSep + location);
290 return prop;
291 }
292 else {
293 locationTried.add("<prop.home> environment variable");
294 }
295 }
296 catch (IOException e) {
297 System.err.println(e.getMessage());
298 }
299 // try classpath...
300 try {
301 URL url = Property.class.getClassLoader()
302 .getResource(location);
303 if (url != null) {
304 Property prop = loadFromFullPath(url.getFile());
305 loadedProps.put(location, prop);
306 System.err.println("Info: using property file in "
307 + url.getFile());
308 return prop;
309 }
310 else {
311 locationTried.add("Classpath");
312 }
313 }
314 catch (IOException e) {
315 System.err.println(e.getMessage());
316 }
317 // try current directory...
318 try {
319 File file = new File(curDir + fileSep + location);
320 if (!locationTried.contains(file.getCanonicalPath())) {
321 if (file.exists()) {
322 Property prop = loadFromFullPath(file.getCanonicalPath());
323 loadedProps.put(location, prop);
324 System.err.println("Info: using property file in "
325 + file.getCanonicalPath());
326 return prop;
327 }
328 else {
329 locationTried.add(file.getCanonicalPath());
330 }
331 }
332 }
333 catch (IOException e) {
334 System.err.println(e.getMessage());
335 }
336 // try if the location is full path...
337 try {
338 File file = new File(location);
339 if (!locationTried.contains(file.getCanonicalPath())) {
340 if (file.exists()) {
341 Property prop = loadFromFullPath(file.getCanonicalPath());
342 loadedProps.put(location, prop);
343 System.err.println("Info: using property file in "
344 + file.getCanonicalPath());
345 return prop;
346 }
347 else {
348 locationTried.add(file.getCanonicalPath());
349 }
350 }
351 }
352 catch (IOException e) {
353 System.err.println(e.getMessage());
354 }
355 // try user's home directory...
356 try {
357 File file = new File(homeDir + fileSep + location);
358 if (!locationTried.contains(file.getCanonicalPath())) {
359 if (file.exists()) {
360 Property prop = loadFromFullPath(file.getCanonicalPath());
361 loadedProps.put(location, prop);
362 System.err.println("Info: using property file in "
363 + file.getCanonicalPath());
364 return prop;
365 }
366 else {
367 locationTried.add(file.getCanonicalPath());
368 }
369 }
370 }
371 catch (IOException e) {
372 System.err.println(e.getMessage());
373 }
374
375 String message =
376 "Cannot find property file in the following locations:";
377 for (int i=0 ; i<locationTried.size() ; i++) {
378 message = message + "\n " + (i+1) + ". " + locationTried.get(i);
379 }
380 message = message + "\n\n";
381 throw new IOException(message);
382 }
383
384 /**
385 * Loads the property file based on a full path. This function will
386 * dispatch the Property file type by the extension of the specified
387 * path.
388 *
389 * @param location the full path of the location of the file
390 * @return the property object loaded
391 * @throws IOException when error occurs when loading the property object
392 */
393 protected static synchronized Property loadFromFullPath(String location)
394 throws IOException {
395 if (location.toUpperCase().endsWith(".XML")) {
396 return new XMLProperty(location);
397 }
398 else {
399 return new TextProperty(location);
400 }
401 }
402 }