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