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, 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 }