View Javadoc

1   package org.e2etrace.config;
2   
3   /*
4    * Copyright 2006 Gunther Popp
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *      http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  import java.io.FileInputStream;
20  import java.io.IOException;
21  import java.io.InputStream;
22  import java.util.Enumeration;
23  import java.util.HashSet;
24  import java.util.Properties;
25  
26  import org.apache.commons.logging.Log;
27  import org.apache.commons.logging.LogFactory;
28  import org.e2etrace.trace.ITraceStepId;
29  
30  
31  /**
32   * Properties file implementation of the e2etrace configuration.
33   * <p>
34   * <p>
35   *
36   * The property file must be structured as follows:
37   * <p>
38   *
39   * <pre>
40   *            global.enabletrace=true/false
41   *            global.reloadinterval=&lt;n&gt;
42   *            id.&lt;trace step id&gt;=true/false
43   * </pre>
44   *
45   * If a <code>reloadinterval</code> is defined in the configuration file, the
46   * property file will be automatically reloaded every n seconds. If no reload
47   * interval or an interval of 0 seconds is defined, the configuration will only
48   * be loaded once.
49   * <p>
50   *
51   * The trace step ids must match the string representation of the id (see
52   * {@link ITraceStepId#asString()}). The default value for trace ids is
53   * <code>true</code>. That is, if no ids are defined in the configuration
54   * file , tracing will be enabled for all trace steps.
55   * <p>
56   *
57   * @author Gunther Popp
58   *
59   */
60  public class PropertiesTraceConfig implements ITraceConfig {
61  
62    private static final Log log = LogFactory.getLog(PropertiesTraceConfig.class);
63  
64    private static final String KEY_ENABLETRACE = "global.enabletrace";
65    private static final String KEY_RELOADINTERVAL = "global.reloadinterval";
66    private static final String KEY_IDPREFIX = "id.";
67  
68    private String fileName;
69    private boolean enabled;
70    private boolean configIsDirty;
71    private HashSet disabledForId;
72    private long reloadInterval;
73    private long lastReload;
74  
75    /**
76     * Default constructor.
77     * <p>
78     *
79     */
80    public PropertiesTraceConfig() {
81  
82    }
83  
84    /** {@inheritDoc} */
85    public boolean isTraceEnabled() {
86      reloadifIntervalExpired();
87  
88      return this.enabled;
89    }
90  
91    /** {@inheritDoc} */
92    public boolean isTraceEnabledForId(ITraceStepId id) {
93      boolean ret = true;
94  
95      reloadifIntervalExpired();
96  
97      if (this.disabledForId.contains(id.asString())) {
98        ret = false;
99      }
100 
101     return ret;
102   }
103 
104   /**
105    * Loads the e2etrace configuration from the specified properties file.
106    * <p>
107    *
108    * The following search order is used:
109    * <p>
110    * <ul>
111    * <li>Load the file using the classloader of
112    * <code>PropertiesTraceConfig</code>. If e2etrace is deployed as part of
113    * an Java EE application, this loader should find all files that are part of
114    * the ear/war file.</li>
115    * <li>If the file has not been found, try again using the thread context
116    * class loader.</li>
117    * <li>If this fails again, load the file directly from the file system.
118    * </li>
119    * </ul>
120    * loaded as resource via the thread context class loader. Hence, the
121    * specified file must be accessible in the classpath of the application.
122    * <p>
123    *
124    * @param fileName path and name of the configuration file
125    * @throws IOException Error loading the specified file
126    */
127   public void loadConfigFile(String fileName) throws IOException {
128     this.fileName = fileName;
129     this.configIsDirty = true;
130 
131     reloadConfiguration();
132 
133   }
134 
135   /**
136    * Reloads the configuration file if the reload interval has expired.
137    * <p>
138    *
139    */
140   private void reloadifIntervalExpired() {
141     if (this.reloadInterval > 0) {
142       if (((System.currentTimeMillis() / 1000) - this.lastReload) >= this.reloadInterval) {
143         try {
144           // The following flag is used to prevent multiple reloads from
145           // parallel threads
146           this.configIsDirty = true;
147 
148           reloadConfiguration();
149 
150         } catch (IOException e) {
151           log
152               .error(
153                   "Cannot reload configuration file. Will use the already loaded values instead.",
154                   e);
155         }
156       }
157     }
158   }
159 
160   /**
161    * Reloads the configuration file.
162    * <p>
163    *
164    * @throws IOException Error loading the configuration file
165    */
166   private synchronized void reloadConfiguration() throws IOException {
167     // If a parallel thread has reloaded the config just before us, simply
168     // return at this point
169     if (!this.configIsDirty) {
170       return;
171     }
172 
173     Properties loadedConfig;
174     loadedConfig = locateAndLoadProperties();
175 
176     this.enabled = getPropertyAsBoolean(loadedConfig,
177         PropertiesTraceConfig.KEY_ENABLETRACE, true);
178     this.reloadInterval = getPropertyAsLong(loadedConfig,
179         PropertiesTraceConfig.KEY_RELOADINTERVAL, 0);
180 
181     // Load the on/off settings per trace id
182     Enumeration e = loadedConfig.keys();
183     String key;
184     String id;
185     boolean idEnabled;
186 
187     this.disabledForId = new HashSet();
188 
189     while (e.hasMoreElements()) {
190       key = (String) e.nextElement();
191 
192       if (key.startsWith(KEY_IDPREFIX)) {
193         id = key.substring(key.indexOf(".") + 1);
194         idEnabled = getPropertyAsBoolean(loadedConfig, key, true);
195 
196         if (!idEnabled) {
197           this.disabledForId.add(id);
198         }
199       }
200     }
201 
202     // Memorize the reload time
203     this.lastReload = System.currentTimeMillis() / 1000;
204 
205     // Config has been refreshed
206     this.configIsDirty = false;
207 
208     if (log.isInfoEnabled()) {
209       log.info("Loaded configuration file " + this.fileName);
210       log.info("Tracing is now " + (this.enabled ? "enabled" : "disabled"));
211       log.info("Reload interval is now " + this.reloadInterval + " seconds");
212       log.info("Tracing has been disabled for " + this.disabledForId.size() + " ids");
213     }
214 
215   }
216 
217   /**
218    * Searches the config file as described in <code>loadConfiguration</code>
219    * and loads the properties.
220    * <p>
221    *
222    * @return loaded properties
223    * @throws IOException File not found or error while loading its content
224    */
225   private Properties locateAndLoadProperties() throws IOException {
226     InputStream is = null;
227     Properties loadedConfig = new Properties();
228 
229     try {
230       // 1. Load configuration using the current class loader
231       is = PropertiesTraceConfig.class.getResourceAsStream(this.fileName);
232 
233       if (is == null) {
234 
235         // 2. Load configuration using the context class loader
236         ClassLoader ctxLoader = Thread.currentThread().getContextClassLoader();
237         is = ctxLoader.getResourceAsStream(this.fileName);
238 
239         if (is == null) {
240           // 3. Load configuration from the file system
241           is = new FileInputStream(this.fileName);
242         }
243       }
244 
245       if (is != null) {
246         loadedConfig.load(is);
247       } else {
248         throw new IOException("Cannot find or open file " + this.fileName);
249       }
250     } finally {
251       if (is != null) {
252         is.close();
253       }
254     }
255 
256     return loadedConfig;
257   }
258 
259   /**
260    * Returns the config value for a given property key as boolean.
261    * <p>
262    *
263    * @param loadedConfig loaded configuration file
264    * @param key key of the required config value
265    * @param def default value, if the key is not existent in the configuration
266    *
267    * @return config value as boolean
268    */
269   private boolean getPropertyAsBoolean(Properties loadedConfig, String key, boolean def) {
270     boolean ret = def;
271     String retValue;
272 
273     retValue = loadedConfig.getProperty(key);
274 
275     if (retValue != null) {
276       ret = new Boolean(retValue).booleanValue();
277 
278     }
279 
280     return ret;
281 
282   }
283 
284   /**
285    * Returns the config value for a given property key as long.
286    * <p>
287    *
288    * @param loadedConfig loaded configuration file
289    * @param key key of the required config value
290    * @param def default value, if the key is not existent in the configuration
291    *
292    * @return config value as long
293    */
294   private long getPropertyAsLong(Properties loadedConfig, String key, long def) {
295     long ret = def;
296     String retValue;
297 
298     retValue = loadedConfig.getProperty(key);
299 
300     if (retValue != null) {
301       ret = new Long(retValue).longValue();
302 
303     }
304 
305     return ret;
306 
307   }
308 
309 }