View Javadoc

1   /*
2    * $Id: Config.java,v 1.28 2004/10/08 16:33:57 mch Exp $
3    *
4    * (C) Copyright Astrogrid...
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  //   protected static Logger log = LogManager.getLogger(Config.class);
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     //public abstract Object[] getProperties(String key) ;
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);  //doing toString() can cause problems if null is the default...
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; //so we preserve the original
281 
282       //resolve included environment variables
283       filename = resolveEnvironmentVariables(filename);
284 
285       //for error messages
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          //not absolute so look in classpath
301          URL url = Config.class.getClassLoader().getResource(filename);
302    
303          if (url != null) {
304             return url;
305          }
306          else {
307             //not found so look in working directory
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; //preserve given for error messages
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             //special case - get the PAL-6dF part of:
335             // http://grendel12.roe.ac.uk/PAL-6dF/etc
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