1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.log4j.extras;
19
20 import org.apache.log4j.Appender;
21 import org.apache.log4j.Layout;
22 import org.apache.log4j.Level;
23 import org.apache.log4j.LogManager;
24 import org.apache.log4j.Logger;
25 import org.apache.log4j.config.PropertySetter;
26 import org.apache.log4j.helpers.FileWatchdog;
27 import org.apache.log4j.helpers.Loader;
28 import org.apache.log4j.helpers.LogLog;
29 import org.apache.log4j.helpers.OptionConverter;
30 import org.apache.log4j.or.RendererMap;
31 import org.apache.log4j.spi.AppenderAttachable;
32 import org.apache.log4j.spi.Configurator;
33 import org.apache.log4j.spi.ErrorHandler;
34 import org.apache.log4j.spi.Filter;
35 import org.apache.log4j.spi.LoggerFactory;
36 import org.apache.log4j.spi.LoggerRepository;
37 import org.apache.log4j.spi.OptionHandler;
38 import org.apache.log4j.spi.RendererSupport;
39 import org.apache.log4j.xml.SAXErrorHandler;
40 import org.apache.log4j.xml.UnrecognizedElementHandler;
41 import org.w3c.dom.Document;
42 import org.w3c.dom.Element;
43 import org.w3c.dom.NamedNodeMap;
44 import org.w3c.dom.Node;
45 import org.w3c.dom.NodeList;
46 import org.xml.sax.EntityResolver;
47 import org.xml.sax.InputSource;
48 import org.xml.sax.SAXException;
49
50 import javax.xml.parsers.DocumentBuilder;
51 import javax.xml.parsers.DocumentBuilderFactory;
52 import javax.xml.parsers.FactoryConfigurationError;
53 import java.io.ByteArrayInputStream;
54 import java.io.File;
55 import java.io.IOException;
56 import java.io.InputStream;
57 import java.io.Reader;
58 import java.lang.reflect.Method;
59 import java.net.URL;
60 import java.util.Hashtable;
61 import java.util.Properties;
62
63 /***
64 * This is a duplicate (with minor modifications)
65 * of the log4j 1.2.15 DOMConfigurator
66 * renamed for use with earlier versions of log4j.
67 *
68 */
69 public class DOMConfigurator implements Configurator {
70
71 static final String CONFIGURATION_TAG = "log4j:configuration";
72 static final String OLD_CONFIGURATION_TAG = "configuration";
73 static final String RENDERER_TAG = "renderer";
74 static final String APPENDER_TAG = "appender";
75 static final String APPENDER_REF_TAG = "appender-ref";
76 static final String PARAM_TAG = "param";
77 static final String LAYOUT_TAG = "layout";
78 static final String CATEGORY = "category";
79 static final String LOGGER = "logger";
80 static final String LOGGER_REF = "logger-ref";
81 static final String CATEGORY_FACTORY_TAG = "categoryFactory";
82 static final String LOGGER_FACTORY_TAG = "loggerFactory";
83 static final String NAME_ATTR = "name";
84 static final String CLASS_ATTR = "class";
85 static final String VALUE_ATTR = "value";
86 static final String ROOT_TAG = "root";
87 static final String ROOT_REF = "root-ref";
88 static final String LEVEL_TAG = "level";
89 static final String PRIORITY_TAG = "priority";
90 static final String FILTER_TAG = "filter";
91 static final String ERROR_HANDLER_TAG = "errorHandler";
92 static final String REF_ATTR = "ref";
93 static final String ADDITIVITY_ATTR = "additivity";
94 static final String THRESHOLD_ATTR = "threshold";
95 static final String CONFIG_DEBUG_ATTR = "configDebug";
96 static final String INTERNAL_DEBUG_ATTR = "debug";
97 private static final String RESET_ATTR = "reset";
98 static final String RENDERING_CLASS_ATTR = "renderingClass";
99 static final String RENDERED_CLASS_ATTR = "renderedClass";
100
101 static final String EMPTY_STR = "";
102 static final Class[] ONE_STRING_PARAM = new Class[] {String.class};
103
104 final static String dbfKey = "javax.xml.parsers.DocumentBuilderFactory";
105
106
107
108 private Hashtable appenderBag;
109
110 private Properties props;
111 private LoggerRepository repository;
112
113 private LoggerFactory catFactory = null;
114
115 /***
116 No argument constructor.
117 */
118 public DOMConfigurator() {
119 appenderBag = new Hashtable();
120 }
121
122 /***
123 Used internally to parse appenders by IDREF name.
124 */
125 protected
126 Appender findAppenderByName(Document doc, String appenderName) {
127 Appender appender = (Appender) appenderBag.get(appenderName);
128
129 if(appender != null) {
130 return appender;
131 } else {
132
133
134
135
136 Element element = null;
137 NodeList list = doc.getElementsByTagName("appender");
138 for (int t=0; t < list.getLength(); t++) {
139 Node node = list.item(t);
140 NamedNodeMap map= node.getAttributes();
141 Node attrNode = map.getNamedItem("name");
142 if (appenderName.equals(attrNode.getNodeValue())) {
143 element = (Element) node;
144 break;
145 }
146 }
147
148
149 if(element == null) {
150 LogLog.error("No appender named ["+appenderName+"] could be found.");
151 return null;
152 } else {
153 appender = parseAppender(element);
154 if (appender != null) {
155 appenderBag.put(appenderName, appender);
156 }
157 return appender;
158 }
159 }
160 }
161 /***
162 Used internally to parse appenders by IDREF element.
163 */
164 protected
165 Appender findAppenderByReference(Element appenderRef) {
166 String appenderName = subst(appenderRef.getAttribute(REF_ATTR));
167 Document doc = appenderRef.getOwnerDocument();
168 return findAppenderByName(doc, appenderName);
169 }
170
171 /***
172 * Delegates unrecognized content to created instance if
173 * it supports UnrecognizedElementParser.
174 * @since 1.2.15
175 * @param instance instance, may be null.
176 * @param element element, may not be null.
177 * @param props properties
178 * @throws IOException thrown if configuration of owner object
179 * should be abandoned.
180 */
181 private static void parseUnrecognizedElement(final Object instance,
182 final Element element,
183 final Properties props) throws Exception {
184 boolean recognized = false;
185 if (instance instanceof UnrecognizedElementHandler) {
186 recognized = ((UnrecognizedElementHandler) instance).parseUnrecognizedElement(
187 element, props);
188 }
189 if (!recognized) {
190 LogLog.warn("Unrecognized element " + element.getNodeName());
191 }
192 }
193
194 /***
195 * Delegates unrecognized content to created instance if
196 * it supports UnrecognizedElementParser and catches and
197 * logs any exception.
198 * @since 1.2.15
199 * @param instance instance, may be null.
200 * @param element element, may not be null.
201 * @param props properties
202 */
203 private static void quietParseUnrecognizedElement(final Object instance,
204 final Element element,
205 final Properties props) {
206 try {
207 parseUnrecognizedElement(instance, element, props);
208 } catch (Exception ex) {
209 LogLog.error("Error in extension content: ", ex);
210 }
211 }
212
213 /***
214 Used internally to parse an appender element.
215 */
216 protected
217 Appender parseAppender (Element appenderElement) {
218 String className = subst(appenderElement.getAttribute(CLASS_ATTR));
219 LogLog.debug("Class name: [" + className+']');
220 try {
221 Object instance = Loader.loadClass(className).newInstance();
222 Appender appender = (Appender)instance;
223 PropertySetter propSetter = new PropertySetter(appender);
224
225 appender.setName(subst(appenderElement.getAttribute(NAME_ATTR)));
226
227 NodeList children = appenderElement.getChildNodes();
228 final int length = children.getLength();
229
230 for (int loop = 0; loop < length; loop++) {
231 Node currentNode = children.item(loop);
232
233
234 if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
235 Element currentElement = (Element)currentNode;
236
237
238 if (currentElement.getTagName().equals(PARAM_TAG)) {
239 setParameter(currentElement, propSetter);
240 }
241
242 else if (currentElement.getTagName().equals(LAYOUT_TAG)) {
243 appender.setLayout(parseLayout(currentElement));
244 }
245
246 else if (currentElement.getTagName().equals(FILTER_TAG)) {
247 parseFilters(currentElement, appender);
248 }
249 else if (currentElement.getTagName().equals(ERROR_HANDLER_TAG)) {
250 parseErrorHandler(currentElement, appender);
251 }
252 else if (currentElement.getTagName().equals(APPENDER_REF_TAG)) {
253 String refName = subst(currentElement.getAttribute(REF_ATTR));
254 if(appender instanceof AppenderAttachable) {
255 AppenderAttachable aa = (AppenderAttachable) appender;
256 LogLog.debug("Attaching appender named ["+ refName+
257 "] to appender named ["+ appender.getName()+"].");
258 aa.addAppender(findAppenderByReference(currentElement));
259 } else {
260 LogLog.error("Requesting attachment of appender named ["+
261 refName+ "] to appender named ["+ appender.getName()+
262 "] which does not implement org.apache.log4j.spi.AppenderAttachable.");
263 }
264 } else {
265 parseUnrecognizedElement(instance, currentElement, props);
266 }
267 }
268 }
269 propSetter.activate();
270 return appender;
271 }
272
273
274 catch (Exception oops) {
275 LogLog.error("Could not create an Appender. Reported error follows.",
276 oops);
277 return null;
278 }
279 }
280
281 /***
282 Used internally to parse an {@link ErrorHandler} element.
283 */
284 protected
285 void parseErrorHandler(Element element, Appender appender) {
286 ErrorHandler eh = (ErrorHandler) OptionConverter.instantiateByClassName(
287 subst(element.getAttribute(CLASS_ATTR)),
288 org.apache.log4j.spi.ErrorHandler.class,
289 null);
290
291 if(eh != null) {
292 eh.setAppender(appender);
293
294 PropertySetter propSetter = new PropertySetter(eh);
295 NodeList children = element.getChildNodes();
296 final int length = children.getLength();
297
298 for (int loop = 0; loop < length; loop++) {
299 Node currentNode = children.item(loop);
300 if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
301 Element currentElement = (Element) currentNode;
302 String tagName = currentElement.getTagName();
303 if(tagName.equals(PARAM_TAG)) {
304 setParameter(currentElement, propSetter);
305 } else if(tagName.equals(APPENDER_REF_TAG)) {
306 eh.setBackupAppender(findAppenderByReference(currentElement));
307 } else if(tagName.equals(LOGGER_REF)) {
308 String loggerName = currentElement.getAttribute(REF_ATTR);
309 Logger logger = (catFactory == null) ? repository.getLogger(loggerName)
310 : repository.getLogger(loggerName, catFactory);
311 eh.setLogger(logger);
312 } else if(tagName.equals(ROOT_REF)) {
313 Logger root = repository.getRootLogger();
314 eh.setLogger(root);
315 } else {
316 quietParseUnrecognizedElement(eh, currentElement, props);
317 }
318 }
319 }
320 propSetter.activate();
321 appender.setErrorHandler(eh);
322 }
323 }
324
325 /***
326 Used internally to parse a filter element.
327 */
328 protected
329 void parseFilters(Element element, Appender appender) {
330 String clazz = subst(element.getAttribute(CLASS_ATTR));
331 Filter filter = (Filter) OptionConverter.instantiateByClassName(clazz,
332 Filter.class, null);
333
334 if(filter != null) {
335 PropertySetter propSetter = new PropertySetter(filter);
336 NodeList children = element.getChildNodes();
337 final int length = children.getLength();
338
339 for (int loop = 0; loop < length; loop++) {
340 Node currentNode = children.item(loop);
341 if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
342 Element currentElement = (Element) currentNode;
343 String tagName = currentElement.getTagName();
344 if(tagName.equals(PARAM_TAG)) {
345 setParameter(currentElement, propSetter);
346 } else {
347 quietParseUnrecognizedElement(filter, currentElement, props);
348 }
349 }
350 }
351 propSetter.activate();
352 LogLog.debug("Adding filter of type ["+filter.getClass()
353 +"] to appender named ["+appender.getName()+"].");
354 appender.addFilter(filter);
355 }
356 }
357
358 /***
359 Used internally to parse an category element.
360 */
361 protected
362 void parseCategory (Element loggerElement) {
363
364 String catName = subst(loggerElement.getAttribute(NAME_ATTR));
365
366 Logger cat;
367
368 String className = subst(loggerElement.getAttribute(CLASS_ATTR));
369
370
371 if(EMPTY_STR.equals(className)) {
372 LogLog.debug("Retreiving an instance of org.apache.log4j.Logger.");
373 cat = (catFactory == null) ? repository.getLogger(catName) : repository.getLogger(catName, catFactory);
374 }
375 else {
376 LogLog.debug("Desired logger sub-class: ["+className+']');
377 try {
378 Class clazz = Loader.loadClass(className);
379 Method getInstanceMethod = clazz.getMethod("getLogger",
380 ONE_STRING_PARAM);
381 cat = (Logger) getInstanceMethod.invoke(null, new Object[] {catName});
382 } catch (Exception oops) {
383 LogLog.error("Could not retrieve category ["+catName+
384 "]. Reported error follows.", oops);
385 return;
386 }
387 }
388
389
390
391
392 synchronized(cat) {
393 boolean additivity = OptionConverter.toBoolean(
394 subst(loggerElement.getAttribute(ADDITIVITY_ATTR)),
395 true);
396
397 LogLog.debug("Setting ["+cat.getName()+"] additivity to ["+additivity+"].");
398 cat.setAdditivity(additivity);
399 parseChildrenOfLoggerElement(loggerElement, cat, false);
400 }
401 }
402
403
404 /***
405 Used internally to parse the category factory element.
406 */
407 protected
408 void parseCategoryFactory(Element factoryElement) {
409 String className = subst(factoryElement.getAttribute(CLASS_ATTR));
410
411 if(EMPTY_STR.equals(className)) {
412 LogLog.error("Category Factory tag " + CLASS_ATTR + " attribute not found.");
413 LogLog.debug("No Category Factory configured.");
414 }
415 else {
416 LogLog.debug("Desired category factory: ["+className+']');
417 Object factory = OptionConverter.instantiateByClassName(className,
418 LoggerFactory.class,
419 null);
420 if (factory instanceof LoggerFactory) {
421 catFactory = (LoggerFactory) factory;
422 } else {
423 LogLog.error("Category Factory class " + className + " does not implement org.apache.log4j.LoggerFactory");
424 }
425 PropertySetter propSetter = new PropertySetter(factory);
426
427 Element currentElement = null;
428 Node currentNode = null;
429 NodeList children = factoryElement.getChildNodes();
430 final int length = children.getLength();
431
432 for (int loop=0; loop < length; loop++) {
433 currentNode = children.item(loop);
434 if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
435 currentElement = (Element)currentNode;
436 if (currentElement.getTagName().equals(PARAM_TAG)) {
437 setParameter(currentElement, propSetter);
438 } else {
439 quietParseUnrecognizedElement(factory, currentElement, props);
440 }
441 }
442 }
443 }
444 }
445
446
447 /***
448 Used internally to parse the roor category element.
449 */
450 protected
451 void parseRoot (Element rootElement) {
452 Logger root = repository.getRootLogger();
453
454 synchronized(root) {
455 parseChildrenOfLoggerElement(rootElement, root, true);
456 }
457 }
458
459
460 /***
461 Used internally to parse the children of a category element.
462 */
463 protected
464 void parseChildrenOfLoggerElement(Element catElement,
465 Logger cat, boolean isRoot) {
466
467 PropertySetter propSetter = new PropertySetter(cat);
468
469
470
471 cat.removeAllAppenders();
472
473
474 NodeList children = catElement.getChildNodes();
475 final int length = children.getLength();
476
477 for (int loop = 0; loop < length; loop++) {
478 Node currentNode = children.item(loop);
479
480 if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
481 Element currentElement = (Element) currentNode;
482 String tagName = currentElement.getTagName();
483
484 if (tagName.equals(APPENDER_REF_TAG)) {
485 Element appenderRef = (Element) currentNode;
486 Appender appender = findAppenderByReference(appenderRef);
487 String refName = subst(appenderRef.getAttribute(REF_ATTR));
488 if(appender != null)
489 LogLog.debug("Adding appender named ["+ refName+
490 "] to category ["+cat.getName()+"].");
491 else
492 LogLog.debug("Appender named ["+ refName + "] not found.");
493
494 cat.addAppender(appender);
495
496 } else if(tagName.equals(LEVEL_TAG)) {
497 parseLevel(currentElement, cat, isRoot);
498 } else if(tagName.equals(PRIORITY_TAG)) {
499 parseLevel(currentElement, cat, isRoot);
500 } else if(tagName.equals(PARAM_TAG)) {
501 setParameter(currentElement, propSetter);
502 } else {
503 quietParseUnrecognizedElement(cat, currentElement, props);
504 }
505 }
506 }
507 propSetter.activate();
508 }
509
510 /***
511 Used internally to parse a layout element.
512 */
513 protected
514 Layout parseLayout (Element layout_element) {
515 String className = subst(layout_element.getAttribute(CLASS_ATTR));
516 LogLog.debug("Parsing layout of class: \""+className+"\"");
517 try {
518 Object instance = Loader.loadClass(className).newInstance();
519 Layout layout = (Layout)instance;
520 PropertySetter propSetter = new PropertySetter(layout);
521
522 NodeList params = layout_element.getChildNodes();
523 final int length = params.getLength();
524
525 for (int loop = 0; loop < length; loop++) {
526 Node currentNode = (Node)params.item(loop);
527 if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
528 Element currentElement = (Element) currentNode;
529 String tagName = currentElement.getTagName();
530 if(tagName.equals(PARAM_TAG)) {
531 setParameter(currentElement, propSetter);
532 } else {
533 parseUnrecognizedElement(instance, currentElement, props);
534 }
535 }
536 }
537
538 propSetter.activate();
539 return layout;
540 }
541 catch (Exception oops) {
542 LogLog.error("Could not create the Layout. Reported error follows.",
543 oops);
544 return null;
545 }
546 }
547
548 protected
549 void parseRenderer(Element element) {
550 String renderingClass = subst(element.getAttribute(RENDERING_CLASS_ATTR));
551 String renderedClass = subst(element.getAttribute(RENDERED_CLASS_ATTR));
552 if(repository instanceof RendererSupport) {
553 RendererMap.addRenderer((RendererSupport) repository, renderedClass,
554 renderingClass);
555 }
556 }
557
558 /***
559 Used internally to parse a level element.
560 */
561 protected
562 void parseLevel(Element element, Logger logger, boolean isRoot) {
563 String catName = logger.getName();
564 if(isRoot) {
565 catName = "root";
566 }
567
568 String priStr = subst(element.getAttribute(VALUE_ATTR));
569 LogLog.debug("Level value for "+catName+" is ["+priStr+"].");
570
571 if(INHERITED.equalsIgnoreCase(priStr) || NULL.equalsIgnoreCase(priStr)) {
572 if(isRoot) {
573 LogLog.error("Root level cannot be inherited. Ignoring directive.");
574 } else {
575 logger.setLevel(null);
576 }
577 } else {
578 String className = subst(element.getAttribute(CLASS_ATTR));
579 if(EMPTY_STR.equals(className)) {
580 logger.setLevel(OptionConverter.toLevel(priStr, Level.DEBUG));
581 } else {
582 LogLog.debug("Desired Level sub-class: ["+className+']');
583 try {
584 Class clazz = Loader.loadClass(className);
585 Method toLevelMethod = clazz.getMethod("toLevel",
586 ONE_STRING_PARAM);
587 Level pri = (Level) toLevelMethod.invoke(null,
588 new Object[] {priStr});
589 logger.setLevel(pri);
590 } catch (Exception oops) {
591 LogLog.error("Could not create level ["+priStr+
592 "]. Reported error follows.", oops);
593 return;
594 }
595 }
596 }
597 LogLog.debug(catName + " level set to " + logger.getLevel());
598 }
599
600 protected
601 void setParameter(Element elem, PropertySetter propSetter) {
602 setParameter(elem, propSetter, props);
603 }
604
605
606 /***
607 Configure log4j using a <code>configuration</code> element as
608 defined in the log4j.dtd.
609
610 */
611 static
612 public
613 void configure (Element element) {
614 DOMConfigurator configurator = new DOMConfigurator();
615 configurator.doConfigure(element, LogManager.getLoggerRepository());
616 }
617
618 /***
619 Like {@link #configureAndWatch(String, long)} except that the
620 default delay as defined by {@link FileWatchdog#DEFAULT_DELAY} is
621 used.
622
623 @param configFilename A log4j configuration file in XML format.
624
625 */
626 static
627 public
628 void configureAndWatch(String configFilename) {
629 configureAndWatch(configFilename, FileWatchdog.DEFAULT_DELAY);
630 }
631
632 /***
633 Read the configuration file <code>configFilename</code> if it
634 exists. Moreover, a thread will be created that will periodically
635 check if <code>configFilename</code> has been created or
636 modified. The period is determined by the <code>delay</code>
637 argument. If a change or file creation is detected, then
638 <code>configFilename</code> is read to configure log4j.
639
640 @param configFilename A log4j configuration file in XML format.
641 @param delay The delay in milliseconds to wait between each check.
642 */
643 static
644 public
645 void configureAndWatch(String configFilename, long delay) {
646 XMLWatchdog xdog = new XMLWatchdog(configFilename);
647 xdog.setDelay(delay);
648 xdog.start();
649 }
650
651 private interface ParseAction {
652 Document parse(final DocumentBuilder parser) throws SAXException, IOException;
653 }
654
655
656 public
657 void doConfigure(final String filename, LoggerRepository repository) {
658 ParseAction action = new ParseAction() {
659 public Document parse(final DocumentBuilder parser) throws SAXException, IOException {
660 return parser.parse(new File(filename));
661 }
662 public String toString() {
663 return "file [" + filename + "]";
664 }
665 };
666 doConfigure(action, repository);
667 }
668
669
670 public
671 void doConfigure(final URL url, LoggerRepository repository) {
672 ParseAction action = new ParseAction() {
673 public Document parse(final DocumentBuilder parser) throws SAXException, IOException {
674 InputStream stream = url.openStream();
675 try {
676 InputSource src = new InputSource(stream);
677 src.setSystemId(url.toString());
678 return parser.parse(src);
679 } finally {
680 stream.close();
681 }
682 }
683 public String toString() {
684 return "url [" + url.toString() + "]";
685 }
686 };
687 doConfigure(action, repository);
688 }
689
690 /***
691 Configure log4j by reading in a log4j.dtd compliant XML
692 configuration file.
693
694 */
695 public
696 void doConfigure(final InputStream inputStream, LoggerRepository repository)
697 throws FactoryConfigurationError {
698 ParseAction action = new ParseAction() {
699 public Document parse(final DocumentBuilder parser) throws SAXException, IOException {
700 InputSource inputSource = new InputSource(inputStream);
701 inputSource.setSystemId("dummy://log4j.dtd");
702 return parser.parse(inputSource);
703 }
704 public String toString() {
705 return "input stream [" + inputStream.toString() + "]";
706 }
707 };
708 doConfigure(action, repository);
709 }
710
711 /***
712 Configure log4j by reading in a log4j.dtd compliant XML
713 configuration file.
714
715 */
716 public
717 void doConfigure(final Reader reader, LoggerRepository repository)
718 throws FactoryConfigurationError {
719 ParseAction action = new ParseAction() {
720 public Document parse(final DocumentBuilder parser) throws SAXException, IOException {
721 InputSource inputSource = new InputSource(reader);
722 inputSource.setSystemId("dummy://log4j.dtd");
723 return parser.parse(inputSource);
724 }
725 public String toString() {
726 return "reader [" + reader.toString() + "]";
727 }
728 };
729 doConfigure(action, repository);
730 }
731
732 /***
733 Configure log4j by reading in a log4j.dtd compliant XML
734 configuration file.
735
736 */
737 protected
738 void doConfigure(final InputSource inputSource, LoggerRepository repository)
739 throws FactoryConfigurationError {
740 if (inputSource.getSystemId() == null) {
741 inputSource.setSystemId("dummy://log4j.dtd");
742 }
743 ParseAction action = new ParseAction() {
744 public Document parse(final DocumentBuilder parser) throws SAXException, IOException {
745 return parser.parse(inputSource);
746 }
747 public String toString() {
748 return "input source [" + inputSource.toString() + "]";
749 }
750 };
751 doConfigure(action, repository);
752 }
753
754
755 private final void doConfigure(final ParseAction action, final LoggerRepository repository)
756 throws FactoryConfigurationError {
757 DocumentBuilderFactory dbf = null;
758 this.repository = repository;
759 try {
760 LogLog.debug("System property is :"+
761 OptionConverter.getSystemProperty(dbfKey,
762 null));
763 dbf = DocumentBuilderFactory.newInstance();
764 LogLog.debug("Standard DocumentBuilderFactory search succeded.");
765 LogLog.debug("DocumentBuilderFactory is: "+dbf.getClass().getName());
766 } catch(FactoryConfigurationError fce) {
767 Exception e = fce.getException();
768 LogLog.debug("Could not instantiate a DocumentBuilderFactory.", e);
769 throw fce;
770 }
771
772 try {
773 dbf.setValidating(true);
774
775 DocumentBuilder docBuilder = dbf.newDocumentBuilder();
776
777 docBuilder.setErrorHandler(new SAXErrorHandler());
778 docBuilder.setEntityResolver(new Log4jEntityResolver());
779
780 Document doc = action.parse(docBuilder);
781 parse(doc.getDocumentElement());
782 } catch (Exception e) {
783
784 LogLog.error("Could not parse "+ action.toString() + ".", e);
785 }
786 }
787
788 /***
789 Configure by taking in an DOM element.
790 */
791 public void doConfigure(Element element, LoggerRepository repository) {
792 this.repository = repository;
793 parse(element);
794 }
795
796
797 /***
798 A static version of {@link #doConfigure(String, LoggerRepository)}. */
799 static
800 public
801 void configure(String filename) throws FactoryConfigurationError {
802 new DOMConfigurator().doConfigure(filename,
803 LogManager.getLoggerRepository());
804 }
805
806 /***
807 A static version of {@link #doConfigure(URL, LoggerRepository)}.
808 */
809 static
810 public
811 void configure(URL url) throws FactoryConfigurationError {
812 new DOMConfigurator().doConfigure(url, LogManager.getLoggerRepository());
813 }
814
815 /***
816 Used internally to configure the log4j framework by parsing a DOM
817 tree of XML elements based on <a
818 href="doc-files/log4j.dtd">log4j.dtd</a>.
819
820 */
821 protected
822 void parse(Element element) {
823
824 String rootElementName = element.getTagName();
825
826 if (!rootElementName.equals(CONFIGURATION_TAG)) {
827 if(rootElementName.equals(OLD_CONFIGURATION_TAG)) {
828 LogLog.warn("The <"+OLD_CONFIGURATION_TAG+
829 "> element has been deprecated.");
830 LogLog.warn("Use the <"+CONFIGURATION_TAG+"> element instead.");
831 } else {
832 LogLog.error("DOM element is - not a <"+CONFIGURATION_TAG+"> element.");
833 return;
834 }
835 }
836
837 String debugAttrib = subst(element.getAttribute(INTERNAL_DEBUG_ATTR));
838
839 LogLog.debug("debug attribute= \"" + debugAttrib +"\".");
840
841
842 if(!debugAttrib.equals("") && !debugAttrib.equals("null")) {
843 LogLog.setInternalDebugging(OptionConverter.toBoolean(debugAttrib, true));
844 } else {
845 LogLog.debug("Ignoring " + INTERNAL_DEBUG_ATTR + " attribute.");
846 }
847
848
849
850
851
852 String resetAttrib = subst(element.getAttribute(RESET_ATTR));
853 LogLog.debug("reset attribute= \"" + resetAttrib +"\".");
854 if(!("".equals(resetAttrib)) &&
855 OptionConverter.toBoolean(resetAttrib, false)) {
856 repository.resetConfiguration();
857 }
858
859
860 String confDebug = subst(element.getAttribute(CONFIG_DEBUG_ATTR));
861 if(!confDebug.equals("") && !confDebug.equals("null")) {
862 LogLog.warn("The \""+CONFIG_DEBUG_ATTR+"\" attribute is deprecated.");
863 LogLog.warn("Use the \""+INTERNAL_DEBUG_ATTR+"\" attribute instead.");
864 LogLog.setInternalDebugging(OptionConverter.toBoolean(confDebug, true));
865 }
866
867 String thresholdStr = subst(element.getAttribute(THRESHOLD_ATTR));
868 LogLog.debug("Threshold =\"" + thresholdStr +"\".");
869 if(!"".equals(thresholdStr) && !"null".equals(thresholdStr)) {
870 repository.setThreshold(thresholdStr);
871 }
872
873
874
875
876
877
878
879
880
881
882 String tagName = null;
883 Element currentElement = null;
884 Node currentNode = null;
885 NodeList children = element.getChildNodes();
886 final int length = children.getLength();
887
888 for (int loop = 0; loop < length; loop++) {
889 currentNode = children.item(loop);
890 if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
891 currentElement = (Element) currentNode;
892 tagName = currentElement.getTagName();
893
894 if (tagName.equals(CATEGORY_FACTORY_TAG) || tagName.equals(LOGGER_FACTORY_TAG)) {
895 parseCategoryFactory(currentElement);
896 }
897 }
898 }
899
900 for (int loop = 0; loop < length; loop++) {
901 currentNode = children.item(loop);
902 if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
903 currentElement = (Element) currentNode;
904 tagName = currentElement.getTagName();
905
906 if (tagName.equals(CATEGORY) || tagName.equals(LOGGER)) {
907 parseCategory(currentElement);
908 } else if (tagName.equals(ROOT_TAG)) {
909 parseRoot(currentElement);
910 } else if(tagName.equals(RENDERER_TAG)) {
911 parseRenderer(currentElement);
912 } else if (!(tagName.equals(APPENDER_TAG)
913 || tagName.equals(CATEGORY_FACTORY_TAG)
914 || tagName.equals(LOGGER_FACTORY_TAG))) {
915 quietParseUnrecognizedElement(repository, currentElement, props);
916 }
917 }
918 }
919 }
920
921
922 protected
923 String subst(final String value) {
924 return subst(value, props);
925 }
926
927 /***
928 * Substitutes property value for any references in expression.
929 *
930 * @param value value from configuration file, may contain
931 * literal text, property references or both
932 * @param props properties.
933 * @return evaluated expression, may still contain expressions
934 * if unable to expand.
935 * @since 1.2.15
936 */
937 public static String subst(final String value, final Properties props) {
938 try {
939 return OptionConverter.substVars(value, props);
940 } catch (IllegalArgumentException e) {
941 LogLog.warn("Could not perform variable substitution.", e);
942 return value;
943 }
944 }
945
946
947 /***
948 * Sets a parameter based from configuration file content.
949 *
950 * @param elem param element, may not be null.
951 * @param propSetter property setter, may not be null.
952 * @param props properties
953 * @since 1.2.15
954 */
955 public static void setParameter(final Element elem,
956 final PropertySetter propSetter,
957 final Properties props) {
958 String name = subst(elem.getAttribute("name"), props);
959 String value = (elem.getAttribute("value"));
960 value = subst(OptionConverter.convertSpecialChars(value), props);
961 propSetter.setProperty(name, value);
962 }
963
964
965 /***
966 * Creates an OptionHandler and processes any nested param elements
967 * but does not call activateOptions. If the class also supports
968 * UnrecognizedElementParser, the parseUnrecognizedElement method
969 * will be call for any child elements other than param.
970 *
971 * @param element element, may not be null.
972 * @param props properties
973 * @param expectedClass interface or class expected to be implemented
974 * by created class
975 * @return created class or null.
976 * @throws Exception thrown if the contain object should be abandoned.
977 * @since 1.2.15
978 */
979 public static OptionHandler parseElement(final Element element,
980 final Properties props,
981 final Class expectedClass) throws Exception {
982 String clazz = subst(element.getAttribute("class"), props);
983 Object instance = OptionConverter.instantiateByClassName(clazz,
984 expectedClass, null);
985
986 if (instance instanceof OptionHandler) {
987 OptionHandler optionHandler = (OptionHandler) instance;
988 PropertySetter propSetter = new PropertySetter(optionHandler);
989 NodeList children = element.getChildNodes();
990 final int length = children.getLength();
991
992 for (int loop = 0; loop < length; loop++) {
993 Node currentNode = children.item(loop);
994 if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
995 Element currentElement = (Element) currentNode;
996 String tagName = currentElement.getTagName();
997 if (tagName.equals("param")) {
998 setParameter(currentElement, propSetter, props);
999 } else {
1000 parseUnrecognizedElement(instance, currentElement, props);
1001 }
1002 }
1003 }
1004 return optionHandler;
1005 }
1006 return null;
1007 }
1008
1009 private static class XMLWatchdog extends FileWatchdog {
1010
1011 XMLWatchdog(String filename) {
1012 super(filename);
1013 }
1014
1015 /***
1016 Call {@link DOMConfigurator#configure(String)} with the
1017 <code>filename</code> to reconfigure log4j. */
1018 public
1019 void doOnChange() {
1020 new DOMConfigurator().doConfigure(filename,
1021 LogManager.getLoggerRepository());
1022 }
1023 }
1024
1025
1026 /***
1027 * An {@link EntityResolver} specifically designed to return
1028 * <code>log4j.dtd</code> which is embedded within the log4j jar
1029 * file.
1030 *
1031 * @author Paul Austin
1032 * */
1033 private static final class Log4jEntityResolver implements EntityResolver {
1034
1035 public InputSource resolveEntity (String publicId, String systemId) {
1036 if (systemId.endsWith("log4j.dtd")) {
1037 InputStream in = Log4jEntityResolver.class.getResourceAsStream("log4j.dtd");
1038 if (in == null) {
1039 LogLog.warn("Could not find [log4j.dtd] using [" +
1040 Log4jEntityResolver.class.getClassLoader()
1041 + "] class loader, parsed without DTD.");
1042 in = new ByteArrayInputStream(new byte[0]);
1043 }
1044 return new InputSource(in);
1045 } else {
1046 return null;
1047 }
1048 }
1049 }
1050
1051
1052 }
1053
1054