View Javadoc

1   /*
2    * @(#)Configurator.java   1.0
3    *
4    * Copyright (C) AstroGrid. All rights reserved.
5    *
6    * This software is published under the terms of the AstroGrid
7    * Software License version 1.2, a copy of which has been included
8    * with this distribution in the LICENSE.txt file.
9    *
10   */
11  package org.astrogrid;
12  
13  import java.util.Hashtable;
14  
15  import javax.naming.Context;
16  import javax.naming.InitialContext;
17  import javax.naming.NamingException;
18  
19  import org.apache.commons.logging.LogFactory;
20  import org.jconfig.Configuration;
21  import org.jconfig.ConfigurationManager;
22  import org.jconfig.ConfigurationManagerException;
23  import org.jconfig.handler.ConfigurationHandler;
24  import org.jconfig.handler.InputStreamHandler;
25  import org.jconfig.handler.URLHandler;
26  import org.jconfig.handler.XMLFileHandler;
27  
28  import org.astrogrid.i18n.AstroGridMessage;
29  
30  /***
31   * A wrapper around jconfig to provide access to configuration files.
32   * Subclass this for each component and implement the methods giving the name of the config file.
33   * The class first searches JNDI for the location of the file, if that files it looks for config file
34   * on the classpath. 
35   * @author unknown
36   * @see http://www.jconfig.org
37   * @TODO factor out the logging and replace with commons logging http://jakarta.apache.org/commons/logging/api/index.html
38   */
39  public abstract class Configurator {
40  /*** error message */
41    private static final String ASTROGRIDERROR_COULD_NOT_READ_CONFIGFILE =
42      "AG{0}Z00001:{1}: Could not read my configuration file {2}. Missing file or malformed XML.",
43      ASTROGRIDERROR_COMPONENT_NOT_INITIALIZED =
44        "AG{0}Z00002:{1}: Not initialized. Perhaps my configuration file is missing or contains malformed XML.";
45  /*** keys for config file */
46    public static final String GENERAL_CATEGORY = "GENERAL",
47      GENERAL_VERSION_NUMBER = "VERSION";
48  
49  /*** key to indicate template file should be loaded */
50    private static final String TEMPLATE = "TEMPLATE.";
51  
52  /*** config files that have already been loaded*/
53    private static Hashtable loadedConfigurations = new Hashtable();
54  /*** ctor */
55    protected Configurator() {
56      this.init();
57    }
58  /***
59   * Initialisation of configurator
60   *
61   */
62    private void init() {
63      Log.trace("Configurator.init(): entry");
64  
65      try {
66        // Attempts to load the Subsystem's configuration details
67        // from an appropriate xml-based properties file...
68        if (Configurator
69          .getConfig(getSubsystemAcronym(), getConfigFileName(), getJNDIName())
70          == null) {
71  
72          // But it couldn't be found, so log a message...
73          AstroGridMessage message =
74            new AstroGridMessage(
75              ASTROGRIDERROR_COULD_NOT_READ_CONFIGFILE,
76              getSubsystemAcronym(),
77              Configurator.getClassName(Configurator.class),
78              getConfigFileName());
79          Log.logError(message.toString());
80          return;
81  
82        }
83  
84        try {
85          // Attempts to load the Subsystem's default installation messages
86          // from an appropriate properties' file...
87          AstroGridMessage.loadMessages(this.getSubsystemAcronym());
88        } catch (AstroGridException agex) {
89          Log.logError(agex.getAstroGridMessage().toString());
90        }
91  
92      } finally {
93        Log.trace("Configurator.init(): exit");
94      }
95  
96    } // end of init()
97  
98  
99    /***
100    * Verify that the configuration file has been located and loaded
101    * @throws AstroGridException if not
102    */
103   public final void checkPropertiesLoaded() throws AstroGridException {
104     Log.trace("checkPropertiesLoaded() entry");
105 
106     String check = "NOT LOADED";
107     Configuration config =
108       getConfig(
109         this.getSubsystemAcronym(),
110         this.getConfigFileName(),
111         this.getJNDIName());
112 
113     try {
114       if (config == null
115         || config.getProperty(
116           GENERAL_VERSION_NUMBER,
117           check,
118           GENERAL_CATEGORY).equals(
119           check)) {
120 
121         AstroGridMessage message =
122           new AstroGridMessage(
123             ASTROGRIDERROR_COMPONENT_NOT_INITIALIZED,
124             this.getSubsystemAcronym(),
125             Configurator.getClassName(Configurator.class));
126         Log.logError(message.toString());
127         throw new AstroGridException(message);
128       }
129     } finally {
130       Log.trace("checkPropertiesLoaded() exit");
131     }
132 
133   } // end checkPropertiesLoaded()
134 
135   /***
136     *
137     * Static getter for properties from the component's configuration.
138     * <p>
139     *
140     * @param subsystemAcronym TLA used to identify component
141     * @param key - the property key within category
142     * @param category - the category within the configuration
143     * @return the String value of the property, or the empty string if null
144     *
145     * @see org.jconfig.jConfig
146     **/
147   public static String getProperty(
148     final String subsystemAcronym,
149     final String key,
150     final String category) {
151     Log.trace("getProperty() entry");
152 
153     String targetProperty = null;
154 
155     try {
156 
157       Configuration config =
158         ConfigurationManager.getConfiguration(subsystemAcronym);
159 
160         targetProperty = config.getProperty(key // key within category
161     , "" // default value
162     , category) // category within config
163   .trim(); // ensure no surrounding spaces
164 
165       if (key.startsWith(TEMPLATE)) {
166         targetProperty =
167           TemplateManager.getInstance().getTemplate(
168             subsystemAcronym,
169             targetProperty);
170       }
171 
172     } finally {
173       Log.trace("getProperty() exit");
174     }
175 
176     return targetProperty;
177 
178   } // end of getProperty()
179 
180   /***
181    * Set a property
182    * @param subsystemAcronym e.g. JES
183    * @param key key
184    * @param category category
185    * @param value value
186    */
187   public static void setProperty(
188     final String subsystemAcronym,
189     final String key,
190     final String category,
191     final String value) {
192 
193     Log.trace("setProperty() entry");
194     try {
195 
196       Configuration config =
197         ConfigurationManager.getConfiguration(subsystemAcronym);
198 
199       config.setProperty(key, value, category);
200 
201       if (key.startsWith(TEMPLATE)) {
202         //following the philosophy of if you don't need it now don't write it...
203         throw new UnsupportedOperationException("This method isn't supported for TEMPLATEs");
204       }
205 
206     } finally {
207       Log.trace("setProperty() exit");
208     }
209   }
210 
211   /***
212    * Save the configuration to a file
213    * @param subsystemAcronym e.g.JES
214    * @param configFileName where do you want it?
215    * @throws AstroGridException probably an IOException
216    */
217   public static void save(
218     final String subsystemAcronym,
219     final String configFileName)
220     throws AstroGridException {
221 
222     Log.trace("save() entry");
223 
224     try {
225 
226       Configuration config =
227         ConfigurationManager.getConfiguration(subsystemAcronym);
228       ConfigurationHandler handler = new XMLFileHandler(configFileName);
229       ConfigurationManager.getInstance().save(handler, config);
230 
231     } catch (ConfigurationManagerException e) {
232       Log.logError("Error saving configuration file ", e);
233       throw new AstroGridException(e);
234     } finally {
235       Log.trace("save() exit");
236     }
237   }
238 
239   /***
240    * Save the configuration to a file
241    * @throws AstroGridException probably an IOException
242    */
243   public final void save() throws AstroGridException {
244     assert getSubsystemAcronym() != null;
245     assert getConfigFileName() != null;
246     this.save(getConfigFileName());
247   }
248 
249   /***
250    * Save the configuration to a given file
251    * @param fileName Name of file
252    * @throws AstroGridException probably an IOException
253    */
254   public final void save(final String fileName) throws AstroGridException {
255     assert getSubsystemAcronym() != null;
256     Configurator.save(getSubsystemAcronym(), fileName);
257   }
258 
259   /***
260    * Name of xml configuration file - implemented by subclass
261    * @return see above
262    */
263   protected abstract String getConfigFileName();
264   /***
265    * TLA for specific component - implemented by subclass
266    * @return see above
267    */
268   protected abstract String getSubsystemAcronym();
269   /***
270    * Returns the JNDI name of the URL for locating the config file.
271    * e.g. if you place
272    * <verbatim> 
273    * &lt;Environment description="URL giving location of the properties file to use for configuration" 
274    * name="jesConfigFileURL" override="false" type="java.lang.String" value="http://localhost:8080/ASTROGRID_jesconfig.xml"/&gt;
275    * </verbatim>
276    * in Tomcat's server.xml file (under a &lt;context&gt; element), then the name you return here would be
277    * <verbatim>
278    * java:comp/env/jesConfigFileURL
279    * </verbatim>
280    * @return To be implemented by subclasses.  This just returns null.
281    */
282   protected abstract String getJNDIName();
283 
284   /***
285    * Loads the configuration file.  Firstly an attempt is made to find a URL
286    * in the naming service.  If the subclass has not set a key, or that key
287    * is not set in the NamingService then an attempt is made to load the file
288    * from the classpath.  Otherwise the file is loaded from the URL.
289    * @param subsystemAcronym the TLA for the component - supplied by subclass
290    * @param configFileName the name of the config file on the classpath - supplied by subclass
291    * @param jndiName name to lookfor in JNDI, may be null
292    * @return The configuration
293    */
294   private static Configuration getConfig(
295     final String subsystemAcronym,
296     final String configFileName,
297     final String jndiName) {
298     Log.trace("Configurator.getConfig(): entry");
299 
300     assert subsystemAcronym != null;
301     assert !(
302       configFileName == null
303         && jndiName == null) : "Either configFileName or jndiName must be nonnull";
304 
305     Configuration configuration = null;
306 
307     try {
308 
309       if (!loadedConfigurations.containsKey(subsystemAcronym)) {
310         ConfigurationHandler handler = null;
311         if (jndiName != null) {
312           //  Try Obtaining the config file from a URL stored in JNDI
313           try {
314             Context ic = new InitialContext();
315             String url = (String) ic.lookup(jndiName);
316             URLHandler urlHandler = new URLHandler();
317             assert url != null;
318             urlHandler.setURL(url);
319             handler = urlHandler;
320           } catch (NamingException ne) {
321             Log.logDebug(
322               "No InitialContext in Configurator:getConfig "
323                 + "- unable to get log file URL from JNDI - switch to loading from classpath",
324               ne);
325           }
326         }
327 
328         if (handler == null) {
329           handler = new InputStreamHandler(configFileName);
330         }
331 
332         ConfigurationManager.getInstance().load(handler, subsystemAcronym);
333         loadedConfigurations.put(subsystemAcronym, configFileName);
334       }
335 
336       configuration = ConfigurationManager.getConfiguration(subsystemAcronym);
337     } catch (Exception cme) {
338       Log.logError(
339         "Could not get config subsystem '"
340           + subsystemAcronym
341           + "', filename '"
342           + configFileName
343           + "'",
344         cme);
345     } finally {
346       Log.trace("Configurator.getConfig(): exit");
347     }
348     return configuration;
349 
350   } // end of getConfig()
351 
352   /***
353    * Utility method to get the name of a class
354    * @param cls the class in question
355    * @return its name of course, minus the .class bit
356    */
357   public static String getClassName(final java.lang.Class cls) {
358 
359     String componentName = cls.getName();
360     int iLastPoint = componentName.lastIndexOf('.');
361     return componentName.substring(iLastPoint + 1);
362 
363   }
364 
365 } // end of class Configuration
366 
367 /***
368  * Delegates to commons logging
369  * This class used to use org.astrogrid.log.Log
370  * 
371  * @author jdt
372  */
373 final class Log {
374   /***
375    * Do nothing ctor
376    *
377    */
378   private Log() {
379   }
380   /***
381    * Logger for this class
382    */
383   private static org.apache.commons.logging.Log log =
384     LogFactory.getLog(Configurator.class);
385   /***
386    * delegates to commons logging
387    * @param string message
388    */
389   public static void trace(final String string) {
390     log.trace(string);
391   }
392 
393   /***
394    * * delegates to commons logging
395    * @param string message
396    * @param cme exception
397    */
398   public static void logError(final String string, final Throwable cme) {
399     log.error(string, cme);
400   }
401 
402   /***
403    * * delegates to commons logging
404    * @param string message
405    * @param ne exception
406    */
407   public static void logDebug(final String string, final Throwable ne) {
408     log.debug(string, ne);
409 
410   }
411 
412   /***
413    * * delegates to commons logging
414    * @param string message
415    */
416   public static void logError(final String string) {
417     log.error(string);
418   }
419 
420 }