001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache license, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the license for the specific language governing permissions and
015 * limitations under the license.
016 */
017 package org.apache.logging.log4j.core.config;
018
019 import org.apache.logging.log4j.Level;
020 import org.apache.logging.log4j.Logger;
021 import org.apache.logging.log4j.core.Appender;
022 import org.apache.logging.log4j.core.Filter;
023 import org.apache.logging.log4j.core.Layout;
024 import org.apache.logging.log4j.core.LogEvent;
025 import org.apache.logging.log4j.core.appender.ConsoleAppender;
026 import org.apache.logging.log4j.core.config.plugins.PluginAttr;
027 import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
028 import org.apache.logging.log4j.core.config.plugins.PluginFactory;
029 import org.apache.logging.log4j.core.config.plugins.PluginManager;
030 import org.apache.logging.log4j.core.config.plugins.PluginElement;
031 import org.apache.logging.log4j.core.config.plugins.PluginNode;
032 import org.apache.logging.log4j.core.config.plugins.PluginType;
033 import org.apache.logging.log4j.core.config.plugins.PluginValue;
034 import org.apache.logging.log4j.core.filter.AbstractFilterable;
035 import org.apache.logging.log4j.core.helpers.NameUtil;
036 import org.apache.logging.log4j.core.layout.PatternLayout;
037 import org.apache.logging.log4j.core.lookup.Interpolator;
038 import org.apache.logging.log4j.core.lookup.MapLookup;
039 import org.apache.logging.log4j.core.lookup.StrLookup;
040 import org.apache.logging.log4j.core.lookup.StrSubstitutor;
041 import org.apache.logging.log4j.core.net.Advertiser;
042 import org.apache.logging.log4j.status.StatusLogger;
043 import org.apache.logging.log4j.util.PropertiesUtil;
044
045 import java.io.Serializable;
046 import java.lang.annotation.Annotation;
047 import java.lang.reflect.Array;
048 import java.lang.reflect.Method;
049 import java.lang.reflect.Modifier;
050 import java.util.ArrayList;
051 import java.util.Collections;
052 import java.util.List;
053 import java.util.Map;
054 import java.util.concurrent.ConcurrentHashMap;
055 import java.util.concurrent.ConcurrentMap;
056 import java.util.concurrent.CopyOnWriteArrayList;
057
058 /**
059 * The Base Configuration. Many configuration implementations will extend this class.
060 */
061 public class BaseConfiguration extends AbstractFilterable implements Configuration {
062 /**
063 * Allow subclasses access to the status logger without creating another instance.
064 */
065 protected static final Logger LOGGER = StatusLogger.getLogger();
066
067 /**
068 * The root node of the configuration.
069 */
070 protected Node rootNode;
071
072 /**
073 * Listeners for configuration changes.
074 */
075 protected final List<ConfigurationListener> listeners =
076 new CopyOnWriteArrayList<ConfigurationListener>();
077
078 /**
079 * The ConfigurationMonitor that checks for configuration changes.
080 */
081 protected ConfigurationMonitor monitor = new DefaultConfigurationMonitor();
082
083 /**
084 * The Advertiser which exposes appender configurations to external systems.
085 */
086 protected Advertiser advertiser = new DefaultAdvertiser();
087
088 private String name;
089
090 private ConcurrentMap<String, Appender<?>> appenders = new ConcurrentHashMap<String, Appender<?>>();
091
092 private ConcurrentMap<String, LoggerConfig> loggers = new ConcurrentHashMap<String, LoggerConfig>();
093
094 private final StrLookup tempLookup = new Interpolator();
095
096 private final StrSubstitutor subst = new StrSubstitutor(tempLookup);
097
098 private LoggerConfig root = new LoggerConfig();
099
100 private final boolean started = false;
101
102 private final ConcurrentMap<String, Object> componentMap = new ConcurrentHashMap<String, Object>();
103
104 /**
105 * Constructor.
106 */
107 protected BaseConfiguration() {
108 rootNode = new Node();
109 }
110
111 @Override
112 @SuppressWarnings("unchecked")
113 public Map<String, String> getProperties() {
114 return (Map<String, String>) componentMap.get(CONTEXT_PROPERTIES);
115 }
116
117 /**
118 * Initialize the configuration.
119 */
120 @Override
121 public void start() {
122 setup();
123 doConfigure();
124 for (final LoggerConfig logger : loggers.values()) {
125 logger.startFilter();
126 }
127 for (final Appender appender : appenders.values()) {
128 appender.start();
129 }
130
131 startFilter();
132 }
133
134 /**
135 * Tear down the configuration.
136 */
137 @Override
138 public void stop() {
139 // Stop the appenders in reverse order in case they still have activity.
140 final Appender[] array = appenders.values().toArray(new Appender[appenders.size()]);
141 for (int i = array.length - 1; i >= 0; --i) {
142 array[i].stop();
143 }
144 for (final LoggerConfig logger : loggers.values()) {
145 logger.clearAppenders();
146 logger.stopFilter();
147 }
148 root.stopFilter();
149 stopFilter();
150 }
151
152 protected void setup() {
153 }
154
155 @Override
156 public Object getComponent(final String name) {
157 return componentMap.get(name);
158 }
159
160 @Override
161 public void addComponent(final String name, final Object obj) {
162 componentMap.putIfAbsent(name, obj);
163 }
164
165 @SuppressWarnings("unchecked")
166 protected void doConfigure() {
167 boolean setRoot = false;
168 boolean setLoggers = false;
169 for (final Node child : rootNode.getChildren()) {
170 createConfiguration(child, null);
171 if (child.getObject() == null) {
172 continue;
173 }
174 if (child.getName().equalsIgnoreCase("properties")) {
175 if (tempLookup == subst.getVariableResolver()) {
176 subst.setVariableResolver((StrLookup) child.getObject());
177 } else {
178 LOGGER.error("Properties declaration must be the first element in the configuration");
179 }
180 continue;
181 } else if (tempLookup == subst.getVariableResolver()) {
182 final Map<String, String> map = (Map<String, String>) componentMap.get(CONTEXT_PROPERTIES);
183 final StrLookup lookup = map == null ? null : new MapLookup(map);
184 subst.setVariableResolver(new Interpolator(lookup));
185 }
186 if (child.getName().equalsIgnoreCase("appenders")) {
187 appenders = (ConcurrentMap<String, Appender<?>>) child.getObject();
188 } else if (child.getObject() instanceof Filter) {
189 addFilter((Filter) child.getObject());
190 } else if (child.getName().equalsIgnoreCase("loggers")) {
191 final Loggers l = (Loggers) child.getObject();
192 loggers = l.getMap();
193 setLoggers = true;
194 if (l.getRoot() != null) {
195 root = l.getRoot();
196 setRoot = true;
197 }
198 } else {
199 LOGGER.error("Unknown object \"" + child.getName() + "\" of type " +
200 child.getObject().getClass().getName() + " is ignored");
201 }
202 }
203
204 if (!setLoggers) {
205 LOGGER.warn("No Loggers were configured, using default. Is the Loggers element missing?");
206 setToDefault();
207 return;
208 } else if (!setRoot) {
209 LOGGER.warn("No Root logger was configured, creating default ERROR-level Root logger with Console appender");
210 setToDefault();
211 // return; // LOG4J2-219: creating default root=ok, but don't exclude configured Loggers
212 }
213
214 for (final Map.Entry<String, LoggerConfig> entry : loggers.entrySet()) {
215 final LoggerConfig l = entry.getValue();
216 for (final AppenderRef ref : l.getAppenderRefs()) {
217 final Appender app = appenders.get(ref.getRef());
218 if (app != null) {
219 l.addAppender(app, ref.getLevel(), ref.getFilter());
220 } else {
221 LOGGER.error("Unable to locate appender " + ref.getRef() + " for logger " + l.getName());
222 }
223 }
224
225 }
226
227 setParents();
228 }
229
230 private void setToDefault() {
231 setName(DefaultConfiguration.DEFAULT_NAME);
232 final Layout<? extends Serializable> layout =
233 PatternLayout.createLayout("%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n",
234 null, null, null);
235 final Appender<?> appender = ConsoleAppender.createAppender(layout, null, "SYSTEM_OUT", "Console", "false",
236 "true");
237 appender.start();
238 addAppender(appender);
239 final LoggerConfig root = getRootLogger();
240 root.addAppender(appender, null, null);
241
242 final String levelName = PropertiesUtil.getProperties().getStringProperty(DefaultConfiguration.DEFAULT_LEVEL);
243 final Level level = levelName != null && Level.valueOf(levelName) != null ?
244 Level.valueOf(levelName) : Level.ERROR;
245 root.setLevel(level);
246 }
247
248 protected PluginManager getPluginManager() {
249 //don't cache a pluginmanager instance - packages may be updated, requiring
250 // re-discovery of plugins
251 PluginManager mgr = new PluginManager("Core");
252 mgr.collectPlugins();
253 return mgr;
254 }
255
256 /**
257 * Set the name of the configuration.
258 * @param name The name.
259 */
260 public void setName(final String name) {
261 this.name = name;
262 }
263
264 /**
265 * Returns the name of the configuration.
266 * @return the name of the configuration.
267 */
268 @Override
269 public String getName() {
270 return name;
271 }
272
273 /**
274 * Add a listener for changes on the configuration.
275 * @param listener The ConfigurationListener to add.
276 */
277 @Override
278 public void addListener(final ConfigurationListener listener) {
279 listeners.add(listener);
280 }
281
282 /**
283 * Remove a ConfigurationListener.
284 * @param listener The ConfigurationListener to remove.
285 */
286 @Override
287 public void removeListener(final ConfigurationListener listener) {
288 listeners.remove(listener);
289 }
290
291 /**
292 * Returns the Appender with the specified name.
293 * @param name The name of the Appender.
294 * @return the Appender with the specified name or null if the Appender cannot be located.
295 */
296 public Appender<?> getAppender(final String name) {
297 return appenders.get(name);
298 }
299
300 /**
301 * Returns a Map containing all the Appenders and their name.
302 * @return A Map containing each Appender's name and the Appender object.
303 */
304 @Override
305 public Map<String, Appender<?>> getAppenders() {
306 return appenders;
307 }
308
309 /**
310 * Adds an Appender to the configuration.
311 * @param appender The Appender to add.
312 */
313 public void addAppender(final Appender appender) {
314 appenders.put(appender.getName(), appender);
315 }
316
317 @Override
318 public StrSubstitutor getSubst() {
319 return subst;
320 }
321
322 @Override
323 public void setConfigurationMonitor(ConfigurationMonitor monitor) {
324 this.monitor = monitor;
325 }
326
327 @Override
328 public ConfigurationMonitor getConfigurationMonitor() {
329 return monitor;
330 }
331
332 @Override
333 public void setAdvertiser(Advertiser advertiser) {
334 this.advertiser = advertiser;
335 }
336
337 @Override
338 public Advertiser getAdvertiser() {
339 return advertiser;
340 }
341
342 /**
343 * Associates an Appender with a LoggerConfig. This method is synchronized in case a Logger with the
344 * same name is being updated at the same time.
345 *
346 * Note: This method is not used when configuring via configuration. It is primarily used by
347 * unit tests.
348 * @param logger The Logger the Appender will be associated with.
349 * @param appender The Appender.
350 */
351 @Override
352 public synchronized void addLoggerAppender(final org.apache.logging.log4j.core.Logger logger,
353 final Appender<?> appender) {
354 final String name = logger.getName();
355 appenders.putIfAbsent(appender.getName(), appender);
356 final LoggerConfig lc = getLoggerConfig(name);
357 if (lc.getName().equals(name)) {
358 lc.addAppender(appender, null, null);
359 } else {
360 final LoggerConfig nlc = new LoggerConfig(name, lc.getLevel(), lc.isAdditive());
361 nlc.addAppender(appender, null, null);
362 nlc.setParent(lc);
363 loggers.putIfAbsent(name, nlc);
364 setParents();
365 logger.getContext().updateLoggers();
366 }
367 }
368 /**
369 * Associates a Filter with a LoggerConfig. This method is synchronized in case a Logger with the
370 * same name is being updated at the same time.
371 *
372 * Note: This method is not used when configuring via configuration. It is primarily used by
373 * unit tests.
374 * @param logger The Logger the Fo;ter will be associated with.
375 * @param filter The Filter.
376 */
377 @Override
378 public synchronized void addLoggerFilter(final org.apache.logging.log4j.core.Logger logger, final Filter filter) {
379 final String name = logger.getName();
380 final LoggerConfig lc = getLoggerConfig(name);
381 if (lc.getName().equals(name)) {
382
383 lc.addFilter(filter);
384 } else {
385 final LoggerConfig nlc = new LoggerConfig(name, lc.getLevel(), lc.isAdditive());
386 nlc.addFilter(filter);
387 nlc.setParent(lc);
388 loggers.putIfAbsent(name, nlc);
389 setParents();
390 logger.getContext().updateLoggers();
391 }
392 }
393 /**
394 * Marks a LoggerConfig as additive. This method is synchronized in case a Logger with the
395 * same name is being updated at the same time.
396 *
397 * Note: This method is not used when configuring via configuration. It is primarily used by
398 * unit tests.
399 * @param logger The Logger the Appender will be associated with.
400 * @param additive True if the LoggerConfig should be additive, false otherwise.
401 */
402 @Override
403 public synchronized void setLoggerAdditive(final org.apache.logging.log4j.core.Logger logger,
404 final boolean additive) {
405 final String name = logger.getName();
406 final LoggerConfig lc = getLoggerConfig(name);
407 if (lc.getName().equals(name)) {
408 lc.setAdditive(additive);
409 } else {
410 final LoggerConfig nlc = new LoggerConfig(name, lc.getLevel(), additive);
411 nlc.setParent(lc);
412 loggers.putIfAbsent(name, nlc);
413 setParents();
414 logger.getContext().updateLoggers();
415 }
416 }
417
418 /**
419 * Remove an Appender. First removes any associations between LoggerConfigs and the Appender, removes
420 * the Appender from this appender list and then stops the appender. This method is synchronized in
421 * case an Appender with the same name is being added during the removal.
422 * @param name the name of the appender to remove.
423 */
424 public synchronized void removeAppender(final String name) {
425 for (final LoggerConfig logger : loggers.values()) {
426 logger.removeAppender(name);
427 }
428 final Appender app = appenders.remove(name);
429
430 if (app != null) {
431 app.stop();
432 }
433 }
434
435 /**
436 * Locates the appropriate LoggerConfig for a Logger name. This will remove tokens from the
437 * package name as necessary or return the root LoggerConfig if no other matches were found.
438 * @param name The Logger name.
439 * @return The located LoggerConfig.
440 */
441 @Override
442 public LoggerConfig getLoggerConfig(final String name) {
443 if (loggers.containsKey(name)) {
444 return loggers.get(name);
445 }
446 String substr = name;
447 while ((substr = NameUtil.getSubName(substr)) != null) {
448 if (loggers.containsKey(substr)) {
449 return loggers.get(substr);
450 }
451 }
452 return root;
453 }
454
455 /**
456 * Returns the root Logger.
457 * @return the root Logger.
458 */
459 public LoggerConfig getRootLogger() {
460 return root;
461 }
462
463 /**
464 * Returns a Map of all the LoggerConfigs.
465 * @return a Map with each entry containing the name of the Logger and the LoggerConfig.
466 */
467 @Override
468 public Map<String, LoggerConfig> getLoggers() {
469 return Collections.unmodifiableMap(loggers);
470 }
471
472 /**
473 * Returns the LoggerConfig with the specified name.
474 * @param name The Logger name.
475 * @return The LoggerConfig or null if no match was found.
476 */
477 public LoggerConfig getLogger(final String name) {
478 return loggers.get(name);
479 }
480
481 /**
482 * Adding a logger cannot be done atomically so is not allowed in an active configuration. Adding
483 * or removing a Logger requires creating a new configuration and then switching.
484 *
485 * @param name The name of the Logger.
486 * @param loggerConfig The LoggerConfig.
487 */
488 public void addLogger(final String name, final LoggerConfig loggerConfig) {
489 if (started) {
490 final String msg = "Cannot add logger " + name + " to an active configuration";
491 LOGGER.warn(msg);
492 throw new IllegalStateException(msg);
493 }
494 loggers.put(name, loggerConfig);
495 setParents();
496 }
497
498 /**
499 * Removing a logger cannot be done atomically so is not allowed in an active configuration. Adding
500 * or removing a Logger requires creating a new configuration and then switching.
501 *
502 * @param name The name of the Logger.
503 */
504 public void removeLogger(final String name) {
505 if (started) {
506 final String msg = "Cannot remove logger " + name + " in an active configuration";
507 LOGGER.warn(msg);
508 throw new IllegalStateException(msg);
509 }
510 loggers.remove(name);
511 setParents();
512 }
513
514 @Override
515 public void createConfiguration(final Node node, final LogEvent event) {
516 final PluginType type = node.getType();
517 if (type != null && type.isDeferChildren()) {
518 node.setObject(createPluginObject(type, node, event));
519 } else {
520 for (final Node child : node.getChildren()) {
521 createConfiguration(child, event);
522 }
523
524 if (type == null) {
525 if (node.getParent() != null) {
526 LOGGER.error("Unable to locate plugin for " + node.getName());
527 }
528 } else {
529 node.setObject(createPluginObject(type, node, event));
530 }
531 }
532 }
533
534 /*
535 * Retrieve a static public 'method to create the desired object. Every parameter
536 * will be annotated to identify the appropriate attribute or element to use to
537 * set the value of the parameter.
538 * Parameters annotated with PluginAttr will always be set as Strings.
539 * Parameters annotated with PluginElement may be Objects or arrays. Collections
540 * and Maps are currently not supported, although the factory method that is called
541 * can create these from an array.
542 *
543 * Although the happy path works, more work still needs to be done to log incorrect
544 * parameters. These will generally result in unhelpful InvocationTargetExceptions.
545 * @param classClass the class.
546 * @return the instantiate method or null if there is none by that
547 * description.
548 */
549 private Object createPluginObject(final PluginType type, final Node node, final LogEvent event)
550 {
551 final Class clazz = type.getPluginClass();
552
553 if (Map.class.isAssignableFrom(clazz)) {
554 try {
555 @SuppressWarnings("unchecked")
556 final Map<String, Object> map = (Map<String, Object>) clazz.newInstance();
557 for (final Node child : node.getChildren()) {
558 map.put(child.getName(), child.getObject());
559 }
560 return map;
561 } catch (final Exception ex) {
562 LOGGER.warn("Unable to create Map for " + type.getElementName() + " of class " +
563 clazz);
564 }
565 }
566
567 if (List.class.isAssignableFrom(clazz)) {
568 try {
569 @SuppressWarnings("unchecked")
570 final List<Object> list = (List<Object>) clazz.newInstance();
571 for (final Node child : node.getChildren()) {
572 list.add(child.getObject());
573 }
574 return list;
575 } catch (final Exception ex) {
576 LOGGER.warn("Unable to create List for " + type.getElementName() + " of class " +
577 clazz);
578 }
579 }
580
581 Method factoryMethod = null;
582
583 for (final Method method : clazz.getMethods()) {
584 if (method.isAnnotationPresent(PluginFactory.class)) {
585 factoryMethod = method;
586 break;
587 }
588 }
589 if (factoryMethod == null) {
590 return null;
591 }
592
593 final Annotation[][] parmArray = factoryMethod.getParameterAnnotations();
594 final Class[] parmClasses = factoryMethod.getParameterTypes();
595 if (parmArray.length != parmClasses.length) {
596 LOGGER.error("Number of parameter annotations does not equal the number of paramters");
597 }
598 final Object[] parms = new Object[parmClasses.length];
599
600 int index = 0;
601 final Map<String, String> attrs = node.getAttributes();
602 final List<Node> children = node.getChildren();
603 final StringBuilder sb = new StringBuilder();
604 final List<Node> used = new ArrayList<Node>();
605
606 /*
607 * For each parameter:
608 * If the parameter is an attribute store the value of the attribute in the parameter array.
609 * If the parameter is an element:
610 * Determine if the required parameter is an array.
611 * If so, if a child contains the array, use it,
612 * otherwise create the array from all child nodes of the correct type.
613 * Store the array into the parameter array.
614 * If not an array, store the object in the child node into the parameter array.
615 */
616 for (final Annotation[] parmTypes : parmArray) {
617 for (final Annotation a : parmTypes) {
618 if (sb.length() == 0) {
619 sb.append(" with params(");
620 } else {
621 sb.append(", ");
622 }
623 if (a instanceof PluginNode) {
624 parms[index] = node;
625 sb.append("Node=").append(node.getName());
626 } else if (a instanceof PluginConfiguration) {
627 parms[index] = this;
628 if (this.name != null) {
629 sb.append("Configuration(").append(name).append(")");
630 } else {
631 sb.append("Configuration");
632 }
633 } else if (a instanceof PluginValue) {
634 final String name = ((PluginValue) a).value();
635 String v = node.getValue();
636 if (v == null) {
637 v = getAttrValue("value", attrs);
638 }
639 final String value = subst.replace(event, v);
640 sb.append(name).append("=\"").append(value).append("\"");
641 parms[index] = value;
642 } else if (a instanceof PluginAttr) {
643 final String name = ((PluginAttr) a).value();
644 final String value = subst.replace(event, getAttrValue(name, attrs));
645 sb.append(name).append("=\"").append(value).append("\"");
646 parms[index] = value;
647 } else if (a instanceof PluginElement) {
648 final PluginElement elem = (PluginElement) a;
649 final String name = elem.value();
650 if (parmClasses[index].isArray()) {
651 final Class<?> parmClass = parmClasses[index].getComponentType();
652 final List<Object> list = new ArrayList<Object>();
653 sb.append(name).append("={");
654 boolean first = true;
655 for (final Node child : children) {
656 final PluginType childType = child.getType();
657 if (elem.value().equalsIgnoreCase(childType.getElementName()) ||
658 parmClass.isAssignableFrom(childType.getPluginClass())) {
659 used.add(child);
660 if (!first) {
661 sb.append(", ");
662 }
663 first = false;
664 final Object obj = child.getObject();
665 if (obj == null) {
666 LOGGER.error("Null object returned for " + child.getName() + " in " +
667 node.getName());
668 continue;
669 }
670 if (obj.getClass().isArray()) {
671 printArray(sb, (Object[]) obj);
672 parms[index] = obj;
673 break;
674 }
675 sb.append(child.toString());
676 list.add(obj);
677 }
678 }
679 sb.append("}");
680 if (parms[index] != null) {
681 break;
682 }
683 if (list.size() > 0 && !parmClass.isAssignableFrom(list.get(0).getClass())) {
684 LOGGER.error("Attempted to assign List containing class " +
685 list.get(0).getClass().getName() + " to array of type " + parmClass +
686 " for attribute " + name);
687 break;
688 }
689 final Object[] array = (Object[]) Array.newInstance(parmClass, list.size());
690 int i = 0;
691 for (final Object obj : list) {
692 array[i] = obj;
693 ++i;
694 }
695 parms[index] = array;
696 } else {
697 final Class<?> parmClass = parmClasses[index];
698 boolean present = false;
699 for (final Node child : children) {
700 final PluginType childType = child.getType();
701 if (elem.value().equals(childType.getElementName()) ||
702 parmClass.isAssignableFrom(childType.getPluginClass())) {
703 sb.append(child.getName()).append("(").append(child.toString()).append(")");
704 present = true;
705 used.add(child);
706 parms[index] = child.getObject();
707 break;
708 }
709 }
710 if (!present) {
711 sb.append("null");
712 }
713 }
714 }
715 }
716 ++index;
717 }
718 if (sb.length() > 0) {
719 sb.append(")");
720 }
721
722 if (attrs.size() > 0) {
723 final StringBuilder eb = new StringBuilder();
724 for (final String key : attrs.keySet()) {
725 if (eb.length() == 0) {
726 eb.append(node.getName());
727 eb.append(" contains ");
728 if (attrs.size() == 1) {
729 eb.append("an invalid element or attribute ");
730 } else {
731 eb.append("invalid attributes ");
732 }
733 } else {
734 eb.append(", ");
735 }
736 eb.append("\"");
737 eb.append(key);
738 eb.append("\"");
739
740 }
741 LOGGER.error(eb.toString());
742 }
743
744 if (!type.isDeferChildren() && used.size() != children.size()) {
745 for (final Node child : children) {
746 if (used.contains(child)) {
747 continue;
748 }
749 final String nodeType = node.getType().getElementName();
750 final String start = nodeType.equals(node.getName()) ? node.getName() : nodeType + " " + node.getName();
751 LOGGER.error(start + " has no parameter that matches element " + child.getName());
752 }
753 }
754
755 try {
756 final int mod = factoryMethod.getModifiers();
757 if (!Modifier.isStatic(mod)) {
758 LOGGER.error(factoryMethod.getName() + " method is not static on class " +
759 clazz.getName() + " for element " + node.getName());
760 return null;
761 }
762 LOGGER.debug("Calling {} on class {} for element {}{}", factoryMethod.getName(), clazz.getName(),
763 node.getName(), sb.toString());
764 //if (parms.length > 0) {
765 return factoryMethod.invoke(null, parms);
766 //}
767 //return factoryMethod.invoke(null, node);
768 } catch (final Exception e) {
769 LOGGER.error("Unable to invoke method " + factoryMethod.getName() + " in class " +
770 clazz.getName() + " for element " + node.getName(), e);
771 }
772 return null;
773 }
774
775 private void printArray(final StringBuilder sb, final Object... array) {
776 boolean first = true;
777 for (final Object obj : array) {
778 if (!first) {
779 sb.append(", ");
780 }
781 sb.append(obj.toString());
782 first = false;
783 }
784 }
785
786 private String getAttrValue(final String name, final Map<String, String> attrs) {
787 for (final String key : attrs.keySet()) {
788 if (key.equalsIgnoreCase(name)) {
789 final String attr = attrs.get(key);
790 attrs.remove(key);
791 return attr;
792 }
793 }
794 return null;
795 }
796
797 private void setParents() {
798 for (final Map.Entry<String, LoggerConfig> entry : loggers.entrySet()) {
799 final LoggerConfig logger = entry.getValue();
800 String name = entry.getKey();
801 if (!name.equals("")) {
802 final int i = name.lastIndexOf('.');
803 if (i > 0) {
804 name = name.substring(0, i);
805 LoggerConfig parent = getLoggerConfig(name);
806 if (parent == null) {
807 parent = root;
808 }
809 logger.setParent(parent);
810 } else {
811 logger.setParent(root);
812 }
813 }
814 }
815 }
816 }