1
2
3
4
5
6
7 package org.astrogrid.config;
8
9
10 import java.io.File;
11 import java.io.FileNotFoundException;
12 import java.io.IOException;
13 import java.io.Writer;
14 import java.net.MalformedURLException;
15 import java.net.URL;
16 import java.util.Set;
17 import javax.xml.parsers.ParserConfigurationException;
18 import org.apache.commons.logging.Log;
19 import org.apache.commons.logging.LogFactory;
20 import org.astrogrid.util.DomHelper;
21 import org.w3c.dom.Document;
22 import org.xml.sax.SAXException;
23
24 /***
25 * Defines the methods that a Configurator must implement.
26 * Also provides many of the convenience methods, such as getUrl().
27 * <p>
28 * There is no store yet but we could add this later
29 * <p>
30 * NB - Have deliberately NOT included a loadStream(Stream) method - although
31 * this might be generically useful, it makes it difficult to track through
32 * the Config package where properties have come from. If
33 * you need to do this, write your stream of properties to a file and then load
34 * from File.toURL()
35 *
36 */
37
38 public abstract class Config {
39
40 /*** The logging instance */
41 protected Log log = LogFactory.getLog(Config.class);
42
43
44 /*** String used to identify locations loaded from */
45 private String loadedFrom = null;
46
47 /*** Returns the property identified by the given key. If the property
48 * is not found, throws a PropertyNotFoundException */
49 public abstract Object getProperty(String key) throws PropertyNotFoundException;
50
51 /*** Set property. Stores in cache so it overrides all other properties
52 * with the same key. */
53 public abstract void setProperty(String key, Object value);
54
55 /*** Returns the list of properties identified by the given key. If no matching properties
56 * are found, throws a PropertyNotFoundException */
57 public abstract Object[] getProperties(String key) throws PropertyNotFoundException;
58
59 /*** Sets the property to the given list of values */
60 public abstract void setProperties(String key, Object[] values);
61
62 /*** Returns all the objects identified by the given key */
63
64
65 /*** Loads the properties from the file at the given URL */
66 public abstract void loadFromUrl(URL url) throws IOException;
67
68 /*** Writes out the configuration keys and values to the given Writer. Used
69 * for site debugging. Remember that passwords should be hidden... */
70 public abstract void dumpConfig(Writer out);
71
72 /*** Returns a list of the keys */
73 public abstract Set keySet();
74
75 /***
76 * Adds the given string to the list of places the values are being found
77 */
78 protected void addLoadedFrom(String s) {
79 if (loadedFrom == null) {
80 loadedFrom = s;
81 } else {
82 loadedFrom = loadedFrom + ", "+s;
83 }
84 }
85
86 /***
87 * Returns information about where the values are being found
88 */
89 public String loadedFrom() {
90 if (loadedFrom == null) {
91 return "(config not loaded yet)";
92 }
93 return loadedFrom;
94 }
95
96
97 /*** Returns the property identified by the given key. If the property
98 * is not found, returns the given defaultValue */
99 public Object getProperty(String key, Object defaultValue) {
100
101 try {
102 return getProperty(key);
103 }
104 catch (PropertyNotFoundException nnfe) {
105 return defaultValue;
106 }
107
108 }
109
110 /***
111 * Convenience string of getProperty
112 */
113 public String getString(String key) {
114 return getProperty(key).toString();
115 }
116
117 /***
118 * Convenience string of getProperty
119 */
120 public String getString(String key, String defaultValue) {
121 Object o = getProperty(key, defaultValue);
122 if (o == null) {
123 return null;
124 } else {
125 return o.toString();
126 }
127 }
128
129 /***
130 * Typed getProperty - returns URL. If property is not a valid url, throws
131 * a wrapping ConfigException as a runtime error
132 */
133 public URL getUrl(String key) {
134
135 try {
136 return new URL(getProperty(key).toString());
137 }
138 catch (MalformedURLException mue) {
139 throw new ConfigException("Key '"+key+"' returns invalid URL '"+getProperty(key)+"'", mue);
140 }
141 }
142
143 /***
144 * Typed getProperty - returns URL. If property is not a valid url, throws
145 * a wrapping ConfigException as a runtime error
146 */
147 public URL getUrl(String key, URL defaultValue) {
148
149 try {
150 return getUrl(key);
151 }
152 catch (PropertyNotFoundException nfe) {
153 return defaultValue;
154 }
155 }
156
157 /***
158 * Typed getProperty - returns integer. If property is not a valid int, throws
159 * a wrapping ConfigException as a runtime error
160 */
161 public int getInt(String key) {
162
163 try {
164 return Integer.parseInt(getProperty(key).toString().trim());
165 }
166 catch (NumberFormatException nfe) {
167 throw new ConfigException("Key '"+key+"' returns invalid integer '"+getProperty(key)+"'", nfe);
168 }
169 }
170
171 /***
172 * Typed getProperty - returns integer. If property is not a valid int, throws
173 * a wrapping ConfigException as a runtime error
174 */
175 public int getInt(String key, int defaultValue) {
176
177 try {
178 return getInt(key);
179 }
180 catch (PropertyNotFoundException nfe) {
181 return defaultValue;
182 }
183 }
184
185 /***
186 * Typed getProperty - returns boolean.
187 */
188 public boolean getBoolean(String key, boolean defaultValue) {
189
190 try {
191 return getBoolean(key);
192 }
193 catch (PropertyNotFoundException nfe) {
194 return defaultValue;
195 }
196 }
197
198 /***
199 * Typed getProperty - returns boolean.
200 */
201 public boolean getBoolean(String key) {
202 return Boolean.valueOf(getProperty(key).toString()).booleanValue();
203 }
204
205 /***
206 * Indirect typed getProperty - returns a DOM loaded from a file at the url
207 * speficied. ie, looks up the given key to get a url, then attempts to
208 * read a DOM from the file at that URL.
209 * @todo - This is a temporary holding point - Kevin will implement
210 */
211 public Document getDom(String key) {
212 return readDom(key, getUrl(key));
213 }
214
215 /***
216 * Indirect typed getProperty - returns a DOM loaded from a file at the url
217 * speficied. ie, looks up the given key to get a url, then attempts to
218 * read a DOM from the file at that URL.
219 */
220 public Document getDom(String key, Document defaultDom) {
221 try {
222 return getDom(key);
223 }
224 catch (PropertyNotFoundException nfe) {
225 return defaultDom;
226 }
227
228 }
229
230 /***
231 * Indirect typed getProperty - returns a DOM loaded from a file at the url
232 * speficied. ie, looks up the given key to get a url, then attempts to
233 * read a DOM from the file at that URL.
234 */
235 public Document getDom(String key, URL defaultUrl) {
236 try {
237 return getDom(key);
238 }
239 catch (PropertyNotFoundException nfe) {
240 return readDom("(default, key '"+key+"' not found)", defaultUrl);
241 }
242
243 }
244
245 /*** For DOM loading methods above. The key is given so that expcetions
246 * can report which key was being used */
247 private Document readDom(String key, URL source) {
248 try {
249 return DomHelper.newDocument(source.openStream());
250 }
251 catch (IOException ioe) {
252 throw new ConfigException("Could not read from '"+source+"' given by Config Key '"+key+"'" , ioe);
253 }
254 catch (ParserConfigurationException pce) {
255 throw new ConfigException("Application not configured correctly: ", pce);
256 }
257 catch (SAXException se) {
258 throw new ConfigException("Invalid XML in '"+source+"' from Config Key '"+key+"'");
259 }
260 }
261
262 /*** There are several occasions when an application needs a complete file. For
263 * example, a metadata file. This method provides a way of locating that file
264 * in different environments.
265 * If the filename includes a ${..} then the contents of the
266 * brackets are resolved using the system properties. This allows us to make
267 * use of Tomcat's locations for example.
268 * If the filename is then absolute, the file is resolved normally.
269 * If the filename is relative, the file is searched for first in the classpath
270 * then in the working directory.
271 * If the path is not found, a FileNotFoundException is thrown listing
272 * the places looked. If the path is found, a url to it is returned.
273 *
274 * Hmm not sure if this is really a Configuration thing....
275 *
276 * NB this resolves to a URL so it won't find files in jar files
277 */
278 public static URL resolveFilename(String givenFilename) throws IOException {
279
280 String filename = givenFilename;
281
282
283 filename = resolveEnvironmentVariables(filename);
284
285
286 String msg = givenFilename;
287 if (!givenFilename.equals(filename)) {
288 msg = msg + " (resolves to "+filename+")";
289 }
290
291 File f = new File(filename);
292 if (f.isAbsolute()) {
293 if (f.exists() && f.isFile()) {
294 return f.toURL();
295 }
296 throw new FileNotFoundException(msg);
297 }
298 else
299 {
300
301 URL url = Config.class.getClassLoader().getResource(filename);
302
303 if (url != null) {
304 return url;
305 }
306 else {
307
308 if (f.exists() && f.isFile()) {
309 return f.toURL();
310 }
311 }
312 throw new FileNotFoundException(msg+" not found in classpath or working directory");
313 }
314 }
315
316
317 /***
318 * Resolves environment variables. Looks for ${xxxx} strings and replace
319 * with whatever xxxx is set to in the system environment properties
320 */
321 public static String resolveEnvironmentVariables(String givenSource) {
322
323 String source = givenSource;
324
325 while (source.indexOf("${")>-1) {
326 int s = source.indexOf("${");
327 int e = source.indexOf("}");
328 if (e==-1) throw new IllegalArgumentException("String "+givenSource+" has mismatched brackets");
329
330 String sysEnvKey = source.substring(s+2,e);
331 String sysEnvValue = System.getProperty(sysEnvKey);
332
333 if ((sysEnvValue == null) && (sysEnvKey.equals("host.path"))) {
334
335
336 sysEnvValue = getDeploymentId();
337 }
338
339 if (sysEnvValue == null) throw new ConfigException("Sys Env '"+sysEnvKey+"' not found for filename "+givenSource);
340
341 source = source.substring(0, s)+ sysEnvValue + source.substring(e+1);
342 }
343
344 return source;
345 }
346
347 /***
348 * Attempt to resolve deployment-specific name. In this case it looks
349 * at the url of this file
350 */
351 public static String getDeploymentId() {
352 return null;
353 }
354
355 }
356
357