001: /**
002: * Copyright (C) 2001-2003 France Telecom R&D
003: *
004: * This library is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License as published by the Free Software Foundation; either
007: * version 2 of the License, or (at your option) any later version.
008: *
009: * This library is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: * Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public
015: * License along with this library; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: */package org.objectweb.util.monolog.file.monolog;
018:
019: import org.objectweb.util.monolog.api.Handler;
020: import org.objectweb.util.monolog.api.HandlerFactory;
021: import org.objectweb.util.monolog.api.Level;
022: import org.objectweb.util.monolog.api.LevelFactory;
023: import org.objectweb.util.monolog.api.Logger;
024: import org.objectweb.util.monolog.api.LoggerFactory;
025: import org.objectweb.util.monolog.api.TopicalLogger;
026: import org.objectweb.util.monolog.api.BasicLevel;
027: import org.objectweb.util.monolog.api.MonologFactory;
028: import org.objectweb.util.monolog.file.DottedStringTools;
029: import org.objectweb.util.monolog.wrapper.common.LevelImpl;
030:
031: import java.io.PrintStream;
032: import java.io.PrintWriter;
033: import java.io.Serializable;
034: import java.util.Enumeration;
035: import java.util.Properties;
036: import java.util.StringTokenizer;
037: import java.util.Vector;
038:
039: /**
040: * This class permits to load and store a monolog configuration. The chooseen
041: * format is java.util.Properties. It also easy to write or load a Properties
042: * from a file. The encoding format is the following:<br/>
043: * handler.<handler name>.<property name>=<property value><br/>
044: *<br/>
045: * level.<level name>=<integer value or string expression><br/>
046: *<br/>
047: * logger.<dotted logger name>.level=<level name><br/>
048: * logger.<dotted logger name>.additivity=<true | false><br/>
049: * logger.<dotted logger name>.handler.<number>=<handler name><br/>
050: * logger.<dotted logger name>.topic.<number>=<additionnal logger name><br/>
051: *<br/>
052: * @author Sebastien Chassande-Barrioz
053: */
054: public class PropertiesConfAccess implements Serializable {
055:
056: /**
057: *
058: */
059: private static final long serialVersionUID = -321110630195680214L;
060:
061: public final static String LOGGER_FIELD = "logger";
062: public final static String ACTIVATION = "activation";
063: public final static String ADDITIVITY_FIELD = "additivity";
064: public final static String USE_PARENT_FIELD = "useParent";
065: public final static String HANDLER_FIELD = "handler";
066: public final static String LEVEL_FIELD = "level";
067: public final static String TOPIC_FIELD = "topic";
068: public final static String CLEAN_HANDLERS_FIELD = "cleanHandlers";
069: public final static char DOT = '.';
070:
071: public final static String HANDLER_TYPE_ATTRIBUTE = "type";
072:
073: public final static String HANDLER_TYPE_ATTRIBUTE_FILE_VALUE = "file";
074: public final static String HANDLER_TYPE_ATTRIBUTE_CONSOLE_VALUE = "console";
075: public final static String HANDLER_TYPE_ATTRIBUTE_ROLLING_FILE_VALUE = "rollingfile";
076: public final static String HANDLER_TYPE_ATTRIBUTE_NTEVENT_VALUE = "ntevent";
077: public final static String HANDLER_TYPE_ATTRIBUTE_JMX_VALUE = "jmx";
078:
079: public static boolean debug = new Boolean(System
080: .getProperty("monolog.debug")).booleanValue();
081:
082: public static void load(Properties prop, LoggerFactory lof,
083: HandlerFactory hf, LevelFactory lef) throws Exception {
084: new PropertiesConfAccess().read(prop, lof, hf, lef);
085: }
086:
087: public static void load(Properties prop, MonologFactory mf)
088: throws Exception {
089: new PropertiesConfAccess().read(prop, mf, mf, mf);
090: }
091:
092: public static void store(Properties prop, LoggerFactory lof,
093: HandlerFactory hf, LevelFactory lef) throws Exception {
094: new PropertiesConfAccess().write(prop, lof, hf, lef);
095: }
096:
097: public static void store(Properties prop, MonologFactory mf)
098: throws Exception {
099: new PropertiesConfAccess().write(prop, mf, mf, mf);
100: }
101:
102: public void read(Properties prop, LoggerFactory lof,
103: HandlerFactory hf, LevelFactory lef) throws Exception {
104: read(prop, (MonologFactory) lof);
105: }
106:
107: public void read(Properties prop, MonologFactory mf)
108: throws Exception {
109: //Remove the handlers of the loggers which have the property
110: //CLEAN_HANDLERS_FIELD
111: for (Enumeration e = prop.keys(); e.hasMoreElements();) {
112: String key = ((String) e.nextElement());
113: if (key.startsWith(LOGGER_FIELD + DOT)
114: && key.endsWith(DOT + CLEAN_HANDLERS_FIELD)
115: && Boolean.getBoolean(prop.getProperty(key).trim())) {
116: String loggerName = key.substring(
117: LOGGER_FIELD.length() + 1, key.length() - 1
118: - CLEAN_HANDLERS_FIELD.length());
119: ((TopicalLogger) mf.getLogger(loggerName))
120: .removeAllHandlers();
121: }
122: }
123: //Parse logger and level definition
124: for (Enumeration en = prop.keys(); en.hasMoreElements();) {
125: String key = (String) en.nextElement();
126: if (key.startsWith(LOGGER_FIELD + DOT)) {
127: parseLoggerProp(prop, key, mf);
128: } else if (key.startsWith(LEVEL_FIELD + DOT)) {
129: parseLevelProp(prop, key, mf);
130: }
131: }
132: //Parse all handlers
133: Vector hs = new Vector(); //contains the list of handler to activate
134: for (Enumeration en = prop.keys(); en.hasMoreElements();) {
135: String key = (String) en.nextElement();
136: if (key.startsWith(HANDLER_FIELD + DOT)) {
137: String handlerName = DottedStringTools
138: .getBegin(DottedStringTools.getEnd(key));
139: Handler h = mf.getHandler(handlerName);
140: if (h != null && !hs.contains(h)) {
141: //Activate only used handler
142: hs.addElement(h);
143: } // do not activate unsued handler
144: parseHandlerProp(prop, key, mf);
145: }
146: }
147: //Activates used handlers only
148: for (int i = 0; i < hs.size(); i++) {
149: ((Handler) hs.elementAt(i)).setAttribute("activation", mf);
150: }
151: }
152:
153: public void write(Properties prop, LoggerFactory lof,
154: HandlerFactory hf, LevelFactory lef) throws Exception {
155:
156: // Set the level definitions
157: String key = null;
158: String val = null;
159: Level[] levels = lef.getLevels();
160: for (int i = 0; i < levels.length; i++) {
161: if (!isDefaultLevel(levels[i])) {
162: key = LEVEL_FIELD + DOT + levels[i].getName();
163: val = ((LevelImpl) levels[i]).getStringValue();
164: debug(key + " " + val);
165: prop.put(key, val.trim());
166: }
167: }
168:
169: // Set the handler definitions
170: Handler[] handlers = hf.getHandlers();
171: for (int i = 0; i < handlers.length; i++) {
172: key = HANDLER_FIELD + DOT + handlers[i].getName() + DOT
173: + HANDLER_TYPE_ATTRIBUTE;
174: val = handlers[i].getType();
175: debug(key + " " + val);
176: prop.put(key, val);
177: String[] ats = handlers[i].getAttributeNames();
178: for (int j = 0; j < ats.length; j++) {
179: key = HANDLER_FIELD + DOT + handlers[i].getName() + DOT
180: + ats[j];
181: Object o = handlers[i].getAttribute(ats[j]);
182: if (o instanceof String) {
183: val = (String) o;
184: debug(key + " " + val);
185: prop.put(key, val.trim());
186: }
187: }
188: }
189:
190: //Write the logger definition
191: TopicalLogger[] loggers = (TopicalLogger[]) lof.getLoggers();
192: final String topicPrefix = lof.getTopicPrefix();
193: for (int i = 0; i < loggers.length; i++) {
194: String[] topics = loggers[i].getTopic();
195: if (topics.length == 0) {
196: throw new Exception(
197: "Impossible to set the definition of a logger without name");
198: }
199: String topic = loggers[i].getTopic()[0];
200: if (topicPrefix != null && topic.startsWith(topicPrefix)) {
201: topic = topic.substring(topicPrefix.length());
202: }
203: String begin = LOGGER_FIELD + DOT + topic;
204:
205: if (!loggers[i].getAdditivity()) {
206: key = begin + DOT + ADDITIVITY_FIELD;
207: val = "false";
208: debug(key + " " + val);
209: prop.put(key, val.trim());
210: }
211:
212: Level level = loggers[i].getCurrentLevel();
213: if (level != null
214: && level.getIntValue() != BasicLevel.INHERIT) {
215: key = begin + DOT + LEVEL_FIELD;
216: val = " " + level.getName();
217: debug(key + " " + val);
218: prop.put(key, val.trim());
219: }
220:
221: handlers = loggers[i].getHandler();
222: if (handlers != null && handlers.length > 0) {
223: StringBuffer sb = new StringBuffer();
224: String sep = "";
225: for (int j = 0; j < handlers.length; j++) {
226: if (handlers[j].getName() != null) {
227: sb.append(sep);
228: sep = ", ";
229: sb.append(handlers[j].getName());
230: }
231: }
232: debug(begin + DOT + HANDLER_FIELD + " " + sb.toString());
233: prop.put(begin + DOT + HANDLER_FIELD, sb.toString());
234: }
235:
236: if (topics.length > 1) {
237: StringBuffer sb = new StringBuffer();
238: String sep = "";
239: for (int j = 1; j < topics.length; j++) {
240: sb.append(sep);
241: sep = ", ";
242: String ntopic = topics[j];
243: if (topicPrefix != null
244: && ntopic.startsWith(topicPrefix)) {
245: ntopic = ntopic.substring(topicPrefix.length());
246: }
247: sb.append(ntopic);
248: }
249: debug(begin + DOT + TOPIC_FIELD + " " + sb.toString());
250: prop.put(begin + DOT + TOPIC_FIELD, sb.toString());
251: }
252: }
253: }
254:
255: /**
256: * It checks if the level parameter is a default monolog level. The default
257: * monolog levels are the following:
258: * <ul>
259: * <li>FATAL</li>
260: * <li>ERROR</li>
261: * <li>WARN</li>
262: * <li>INFO</li>
263: * <li>DEBUG</li>
264: * </ul>
265: */
266: protected boolean isDefaultLevel(Level l) {
267: return (l.getName().equalsIgnoreCase("DEBUG")
268: || l.getName().equalsIgnoreCase("INFO")
269: || l.getName().equalsIgnoreCase("WARN")
270: || l.getName().equalsIgnoreCase("ERROR")
271: || l.getName().equalsIgnoreCase("FATAL") || l.getName()
272: .equalsIgnoreCase("INHERIT"));
273: }
274:
275: //------------ PARSING METHODS ------------//
276: //-----------------------------------------//
277:
278: /**
279: * It parses a property entry to build or configure a Logger instance.
280: * @param prop is the property where the entry is reachable.
281: * @param key is the entry key
282: * @param lof is the logger factory to use for building a new instance or
283: * fetching existent loggers.
284: * @param hf is the handler factory to use for building a new instance or
285: * fetching existent handlers.
286: * @param lef is the level factory to use for building a new instance or
287: * fetching existent levels.
288: * @return the level instance which has been built or configured
289: * @throws Exception when a parameter is null or the entry is malformed.
290: */
291: protected Logger parseLoggerProp(Properties prop, String key,
292: LoggerFactory lof, HandlerFactory hf, LevelFactory lef)
293: throws Exception {
294: return parseLoggerProp(prop, key, (MonologFactory) lof);
295: }
296:
297: /**
298: * It parses a property entry to build or configure a Logger instance.
299: * @param prop is the property where the entry is reachable.
300: * @param key is the entry key
301: * @param mf is the monolog factory to use for building a new instance or
302: * fetching existent loggers, handlers or levels.
303: * @return the level instance which has been built or configured
304: * @throws Exception when a parameter is null or the entry is malformed.
305: */
306: protected Logger parseLoggerProp(Properties prop, String key,
307: MonologFactory mf) throws Exception {
308: if (prop == null || key == null || mf == null) {
309: throw new Exception("The null parameters are not allowed");
310: }
311:
312: String temp = DottedStringTools.getFirst(key);
313: if (temp == null || !temp.equalsIgnoreCase(LOGGER_FIELD)) {
314: throw new Exception("This key is not a Logger property:"
315: + key);
316: }
317:
318: TopicalLogger logger = null;
319: temp = DottedStringTools.getEnd(key);
320: String last = DottedStringTools.getLast(temp);
321: if (last.equalsIgnoreCase(LEVEL_FIELD)) {
322: // Assigns the level of the logger
323: logger = (TopicalLogger) mf.getLogger(DottedStringTools
324: .getBegin(temp));
325: String levelName = prop.getProperty(key).trim();
326: Level l = mf.getLevel(levelName);
327: if (l == null) {
328: l = parseLevelProp(prop, LEVEL_FIELD + DOT + levelName,
329: mf);
330: }
331: if (debug) {
332: debug("set level to " + l.getName() + " to the logger "
333: + logger.getName());
334: }
335: logger.setLevel(l);
336: } else if (ADDITIVITY_FIELD.equalsIgnoreCase(last)
337: || USE_PARENT_FIELD.equalsIgnoreCase(last)) {
338: logger = (TopicalLogger) mf.getLogger(DottedStringTools
339: .getBegin(temp));
340: boolean a = Boolean
341: .getBoolean(prop.getProperty(key).trim());
342: if (debug) {
343: debug("set additivity to " + a + " to the logger "
344: + logger.getName());
345: }
346: logger.setAdditivity(a);
347: } else if (CLEAN_HANDLERS_FIELD.equalsIgnoreCase(last)) {
348: //Already managed iin the read method
349: } else if (HANDLER_FIELD.equalsIgnoreCase(last)) {
350: String value = prop.getProperty(key).trim();
351: StringTokenizer st = new StringTokenizer(value, ", ;:",
352: false);
353: logger = (TopicalLogger) mf.getLogger(DottedStringTools
354: .getBegin(temp));
355: cleanOldHandler(logger);
356: while (st.hasMoreTokens()) {
357: String hn = st.nextToken();
358: Handler h = mf.getHandler(hn);
359: if (h == null) {
360: h = parseHandlerProp(prop, HANDLER_FIELD + DOT
361: + value + DOT + HANDLER_TYPE_ATTRIBUTE, mf);
362: }
363: if (debug) {
364: debug("add handler " + h.getName()
365: + " to the logger " + logger.getName());
366: }
367: logger.addHandler(h);
368: }
369: } else if (TOPIC_FIELD.equalsIgnoreCase(last)) {
370: logger = (TopicalLogger) mf.getLogger(DottedStringTools
371: .getBegin(temp));
372: String value = prop.getProperty(key).trim();
373: StringTokenizer st = new StringTokenizer(value, ", ;:",
374: false);
375: cleanOldHandler(logger);
376: while (st.hasMoreTokens()) {
377: String topic = st.nextToken();
378: if (debug) {
379: debug("add topic " + topic + " to the logger "
380: + logger.getName());
381: }
382: logger.addTopic(topic);
383: }
384: } else { //old declaration
385: temp = DottedStringTools.getBegin(temp); // remove the number
386: last = DottedStringTools.getLast(temp); // attribute name
387: temp = DottedStringTools.getBegin(temp); // logger name
388: logger = (TopicalLogger) mf.getLogger(temp);
389: if (last.equalsIgnoreCase(HANDLER_FIELD)) {
390: // Assign an handler to a logger
391: String value = prop.getProperty(key).trim();
392: Handler h = mf.getHandler(value);
393: if (h == null) {
394: h = parseHandlerProp(prop, HANDLER_FIELD + DOT
395: + value + DOT + HANDLER_TYPE_ATTRIBUTE, mf);
396: }
397: cleanOldHandler(logger);
398: if (debug) {
399: debug("add handler " + h.getName()
400: + " to the logger " + logger.getName());
401: }
402: logger.addHandler(h);
403: } else if (last.equalsIgnoreCase(TOPIC_FIELD)) {
404: // Assign a topic to a logger
405: String topic = prop.getProperty(key).trim();
406: if (debug) {
407: debug("add topic " + topic + " to the logger "
408: + logger.getName());
409: }
410: logger.addTopic(topic);
411: } else {
412: throw new Exception("Unknown definition" + key + " "
413: + prop.getProperty(key));
414: }
415: }
416: return logger;
417: }
418:
419: /**
420: * It parses a property entry to build or configure a Handler instance.
421: * @param prop is the property where the entry is reachable.
422: * @param key is the entry key
423: * @param hf is the handler factory to use for building a new instance or
424: * fetching existent handlers.
425: * @return the handler instance which has been built or configured
426: * @throws Exception when a parameter is null or the entry is malformed.
427: */
428: protected Handler parseHandlerProp(Properties prop, String key,
429: HandlerFactory hf) throws Exception {
430:
431: if (prop == null || key == null || hf == null) {
432: throw new Exception("The null parameters are not allowed");
433: }
434: String temp = DottedStringTools.getFirst(key);
435: if (temp == null || !temp.equalsIgnoreCase(HANDLER_FIELD)) {
436: throw new Exception("This key is not a Handler field:"
437: + key);
438: }
439: temp = DottedStringTools.getEnd(key);
440: String attribute = DottedStringTools.getLast(temp);
441: String handlerName = DottedStringTools.getBegin(temp);
442: Handler hc = hf.getHandler(handlerName);
443: if (hc == null) {
444: String stringType = prop.getProperty(HANDLER_FIELD + DOT
445: + handlerName + DOT + HANDLER_TYPE_ATTRIBUTE, null);
446: if (stringType == null) {
447: throw new Exception("Impossible to define the handler "
448: + temp + ": the type is not defined");
449: }
450: hc = hf.createHandler(handlerName, stringType);
451: if (hc == null) {
452: throw new Exception(
453: "The HandlerFactory does not create the Handler: name="
454: + handlerName + " / type=" + stringType);
455: }
456: }
457: if (hc != null
458: && !HANDLER_TYPE_ATTRIBUTE.equalsIgnoreCase(attribute)) {
459: String v = prop.getProperty(key).trim();
460: if (debug) {
461: debug("assign property (" + attribute + ", " + v
462: + ") to the handler '" + handlerName);
463: }
464: hc.setAttribute(attribute, v);
465: }
466: return hc;
467: }
468:
469: /**
470: * It parses a property entry to build or configure a Level instance.
471: * @param prop is the property where the entry is reachable.
472: * @param key is the entry key
473: * @param lef is the level factory to use for building a new instance or
474: * fetching existent levels.
475: * @return the level instance which has been built or configured
476: * @throws Exception when a circular level definition is found or when a
477: * parameter is null or the entry is malformed.
478: */
479: protected Level parseLevelProp(Properties prop, String key,
480: LevelFactory lef) throws Exception {
481: return parseLevelProp(prop, key, lef, new Vector());
482: }
483:
484: /**
485: * It parses a property entry to build or configure a Level instance.
486: * @param prop is the property where the entry is reachable.
487: * @param key is the entry key
488: * @param lef is the level factory to use for building a new instance or
489: * fetching existent levels.
490: * @param currentLevelParse the list of current level name which are
491: * searched.
492: * @throws Exception when a circular level definition is found or when a
493: * parameter is null.
494: */
495: protected Level parseLevelProp(Properties prop, String key,
496: LevelFactory lef, Vector currentLevelParse)
497: throws Exception {
498: if (prop == null || key == null || lef == null) {
499: throw new Exception("The null parameters are not allowed");
500: }
501: String temp = DottedStringTools.getFirst(key);
502: if (temp == null || !temp.equalsIgnoreCase(LEVEL_FIELD))
503: throw new Exception("This key is not a level field:" + key);
504:
505: temp = DottedStringTools.getEnd(key);
506: Level l = lef.getLevel(temp);
507: if (l == null) {
508: if (!prop.containsKey(key)) {
509: throw new Exception("Level definition not found: "
510: + key);
511: }
512: if (currentLevelParse.contains(temp)) {
513: throw new Exception("Circular level definition: "
514: + temp);
515: }
516: String levelValue = prop.getProperty(key).trim();
517: String[] depends = getDependsLevel(levelValue);
518: if (depends.length > 0) {
519: currentLevelParse.addElement(temp);
520: for (int i = 0; i < depends.length; i++) {
521: parseLevelProp(prop,
522: LEVEL_FIELD + DOT + depends[i], lef,
523: currentLevelParse);
524: }
525: currentLevelParse.removeElement(temp);
526: }
527: l = lef.defineLevel(temp, levelValue);
528: }
529: return l;
530: }
531:
532: /**
533: * It retrieves the list of the referenced levels in the String parameter.
534: * @param expr is a string which can contain level identifier. The string is
535: * an arithmetic expression which contains number, operator and levels
536: * identifier.
537: * @return the list of the referenced levels.
538: */
539: protected String[] getDependsLevel(String expr) {
540: Vector res = new Vector();
541: for (StringTokenizer st = new StringTokenizer(expr, " +-", true); st
542: .hasMoreTokens();) {
543: String elem = st.nextToken();
544: if (Character.isLetter(elem.charAt(0))) {
545: res.addElement(elem);
546: }
547: }
548: String[] r = new String[res.size()];
549: res.copyInto(r);
550: return r;
551: }
552:
553: protected Vector loggerCleaned = null;
554:
555: /**
556: * Clear all the handlers associated to a logger which are not defined into
557: * this file
558: */
559: protected void cleanOldHandler(TopicalLogger l) throws Exception {
560: if (loggerCleaned == null) {
561: loggerCleaned = new Vector();
562: }
563: if (!loggerCleaned.contains(l)) {
564: loggerCleaned.addElement(l);
565: l.removeAllHandlers();
566: }
567: }
568:
569: /**
570: * This exception encapsulates an other exception.
571: */
572: class NestedException extends Exception {
573:
574: /**
575: *
576: */
577: private static final long serialVersionUID = -1218233947556357710L;
578:
579: Exception nestedException = null;
580:
581: public NestedException() {
582: super ();
583: }
584:
585: public NestedException(Exception e) {
586: super ();
587: nestedException = e;
588: }
589:
590: public NestedException(Exception e, String msg) {
591: super (msg);
592: nestedException = e;
593: }
594:
595: public Exception getNestedException() {
596: return nestedException;
597: }
598:
599: public void printStackTrace(PrintStream ps) {
600: if (nestedException != null) {
601: ps.println("Nested: " + getMessage());
602: nestedException.printStackTrace(ps);
603: } else {
604: super .printStackTrace(ps);
605: }
606: }
607:
608: public void printStackTrace(PrintWriter w) {
609: if (nestedException != null) {
610: w.println("Nested: " + getMessage());
611: nestedException.printStackTrace(w);
612: } else {
613: super .printStackTrace(w);
614: }
615: }
616:
617: public void printStackTrace() {
618: printStackTrace(new PrintStream(System.out));
619: }
620:
621: }
622:
623: public void debug(String m) {
624: if (debug) {
625: System.out.println(m);
626: }
627: }
628: }
|