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 */ 017package org.apache.logging.log4j.core.config.composite; 018 019import java.io.File; 020import java.lang.reflect.InvocationTargetException; 021import java.net.URI; 022import java.util.ArrayList; 023import java.util.Arrays; 024import java.util.List; 025import java.util.Map; 026 027import org.apache.logging.log4j.Level; 028import org.apache.logging.log4j.core.config.AbstractConfiguration; 029import org.apache.logging.log4j.core.config.Configuration; 030import org.apache.logging.log4j.core.config.ConfigurationFactory; 031import org.apache.logging.log4j.core.config.ConfigurationSource; 032import org.apache.logging.log4j.core.config.ConfiguratonFileWatcher; 033import org.apache.logging.log4j.core.config.Node; 034import org.apache.logging.log4j.core.config.Reconfigurable; 035import org.apache.logging.log4j.core.config.plugins.util.ResolverUtil; 036import org.apache.logging.log4j.core.config.status.StatusConfiguration; 037import org.apache.logging.log4j.core.util.FileWatcher; 038import org.apache.logging.log4j.core.util.Patterns; 039import org.apache.logging.log4j.core.util.WatchManager; 040import org.apache.logging.log4j.util.LoaderUtil; 041import org.apache.logging.log4j.util.PropertiesUtil; 042 043/** 044 * A Composite Configuration. 045 */ 046public class CompositeConfiguration extends AbstractConfiguration implements Reconfigurable { 047 048 /** 049 * Allow the ConfigurationFactory class to be specified as a system property. 050 */ 051 public static final String MERGE_STRATEGY_PROPERTY = "log4j.mergeStrategy"; 052 053 private static final String[] VERBOSE_CLASSES = new String[] {ResolverUtil.class.getName()}; 054 055 private List<? extends AbstractConfiguration> configurations; 056 057 private MergeStrategy mergeStrategy; 058 059 /** 060 * Construct the ComponsiteConfiguration. 061 * 062 * @param configurations The List of Configurations to merge. 063 */ 064 public CompositeConfiguration(List<? extends AbstractConfiguration> configurations) { 065 super(ConfigurationSource.NULL_SOURCE); 066 rootNode = configurations.get(0).getRootNode(); 067 this.configurations = configurations; 068 String mergeStrategyClassName = PropertiesUtil.getProperties().getStringProperty(MERGE_STRATEGY_PROPERTY, 069 DefaultMergeStrategy.class.getName()); 070 try { 071 mergeStrategy = LoaderUtil.newInstanceOf(mergeStrategyClassName); 072 } catch (ClassNotFoundException | IllegalAccessException | NoSuchMethodException | InvocationTargetException | 073 InstantiationException ex) { 074 mergeStrategy = new DefaultMergeStrategy(); 075 } 076 for (AbstractConfiguration config : configurations) { 077 mergeStrategy.mergeRootProperties(rootNode, config); 078 } 079 final StatusConfiguration statusConfig = new StatusConfiguration().withVerboseClasses(VERBOSE_CLASSES) 080 .withStatus(getDefaultStatus()); 081 for (final Map.Entry<String, String> entry : rootNode.getAttributes().entrySet()) { 082 final String key = entry.getKey(); 083 final String value = getStrSubstitutor().replace(entry.getValue()); 084 if ("status".equalsIgnoreCase(key)) { 085 statusConfig.withStatus(value.toUpperCase()); 086 } else if ("dest".equalsIgnoreCase(key)) { 087 statusConfig.withDestination(value); 088 } else if ("shutdownHook".equalsIgnoreCase(key)) { 089 isShutdownHookEnabled = !"disable".equalsIgnoreCase(value); 090 } else if ("verbose".equalsIgnoreCase(key)) { 091 statusConfig.withVerbosity(value); 092 } else if ("packages".equalsIgnoreCase(key)) { 093 pluginPackages.addAll(Arrays.asList(value.split(Patterns.COMMA_SEPARATOR))); 094 } else if ("name".equalsIgnoreCase(key)) { 095 setName(value); 096 } 097 } 098 statusConfig.initialize(); 099 } 100 101 @Override 102 public void setup() { 103 AbstractConfiguration targetConfiguration = configurations.get(0); 104 staffChildConfiguration(targetConfiguration); 105 WatchManager watchManager = getWatchManager(); 106 WatchManager targetWatchManager = targetConfiguration.getWatchManager(); 107 FileWatcher fileWatcher = new ConfiguratonFileWatcher(this, listeners); 108 if (targetWatchManager.getIntervalSeconds() > 0) { 109 watchManager.setIntervalSeconds(targetWatchManager.getIntervalSeconds()); 110 Map<File, FileWatcher> watchers = targetWatchManager.getWatchers(); 111 for (Map.Entry<File, FileWatcher> entry : watchers.entrySet()) { 112 if (entry.getValue() instanceof ConfiguratonFileWatcher) { 113 watchManager.watchFile(entry.getKey(), fileWatcher); 114 } 115 } 116 } 117 for (AbstractConfiguration sourceConfiguration : configurations.subList(1, configurations.size())) { 118 staffChildConfiguration(sourceConfiguration); 119 Node sourceRoot = sourceConfiguration.getRootNode(); 120 mergeStrategy.mergConfigurations(rootNode, sourceRoot, getPluginManager()); 121 if (LOGGER.isEnabled(Level.ALL)) { 122 StringBuilder sb = new StringBuilder(); 123 printNodes("", rootNode, sb); 124 System.out.println(sb.toString()); 125 } 126 int monitorInterval = sourceConfiguration.getWatchManager().getIntervalSeconds(); 127 if (monitorInterval > 0) { 128 int currentInterval = watchManager.getIntervalSeconds(); 129 if (currentInterval <= 0 || monitorInterval < currentInterval) { 130 watchManager.setIntervalSeconds(monitorInterval); 131 } 132 WatchManager sourceWatchManager = sourceConfiguration.getWatchManager(); 133 Map<File, FileWatcher> watchers = sourceWatchManager.getWatchers(); 134 for (Map.Entry<File, FileWatcher> entry : watchers.entrySet()) { 135 if (entry.getValue() instanceof ConfiguratonFileWatcher) { 136 watchManager.watchFile(entry.getKey(), fileWatcher); 137 } 138 } 139 } 140 } 141 } 142 143 @Override 144 public Configuration reconfigure() { 145 LOGGER.debug("Reconfiguring composite configuration"); 146 List<AbstractConfiguration> configs = new ArrayList<>(); 147 ConfigurationFactory factory = ConfigurationFactory.getInstance(); 148 for (AbstractConfiguration config : configurations) { 149 ConfigurationSource source = config.getConfigurationSource(); 150 URI sourceURI = source.getURI(); 151 Configuration currentConfig; 152 if (sourceURI != null) { 153 LOGGER.warn("Unable to determine URI for configuration {}, changes to it will be ignored", 154 config.getName()); 155 currentConfig = factory.getConfiguration(config.getName(), sourceURI); 156 if (currentConfig == null) { 157 LOGGER.warn("Unable to reload configuration {}, changes to it will be ignored", config.getName()); 158 currentConfig = config; 159 } 160 } else { 161 currentConfig = config; 162 } 163 configs.add((AbstractConfiguration) currentConfig); 164 165 } 166 167 return new CompositeConfiguration(configs); 168 } 169 170 private void staffChildConfiguration(AbstractConfiguration childConfiguration) { 171 childConfiguration.setPluginManager(pluginManager); 172 childConfiguration.setScriptManager(scriptManager); 173 childConfiguration.setup(); 174 } 175 176 private void printNodes(String indent, Node node, StringBuilder sb) { 177 sb.append(indent).append(node.getName()).append(" type: ").append(node.getType()).append("\n"); 178 sb.append(indent).append(node.getAttributes().toString()).append("\n"); 179 for (Node child : node.getChildren()) { 180 printNodes(indent + " ", child, sb); 181 } 182 } 183}