View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.core.config.composite;
18  
19  import java.io.File;
20  import java.lang.reflect.InvocationTargetException;
21  import java.net.URI;
22  import java.util.ArrayList;
23  import java.util.Arrays;
24  import java.util.List;
25  import java.util.Map;
26  
27  import org.apache.logging.log4j.Level;
28  import org.apache.logging.log4j.core.config.AbstractConfiguration;
29  import org.apache.logging.log4j.core.config.Configuration;
30  import org.apache.logging.log4j.core.config.ConfigurationFactory;
31  import org.apache.logging.log4j.core.config.ConfigurationSource;
32  import org.apache.logging.log4j.core.config.ConfiguratonFileWatcher;
33  import org.apache.logging.log4j.core.config.Node;
34  import org.apache.logging.log4j.core.config.Reconfigurable;
35  import org.apache.logging.log4j.core.config.plugins.util.ResolverUtil;
36  import org.apache.logging.log4j.core.config.status.StatusConfiguration;
37  import org.apache.logging.log4j.core.util.FileWatcher;
38  import org.apache.logging.log4j.core.util.Loader;
39  import org.apache.logging.log4j.core.util.Patterns;
40  import org.apache.logging.log4j.core.util.WatchManager;
41  import org.apache.logging.log4j.util.PropertiesUtil;
42  
43  /**
44   * A Composite Configuration.
45   */
46  public class CompositeConfiguration extends AbstractConfiguration implements Reconfigurable {
47  
48      /**
49       * Allow the ConfigurationFactory class to be specified as a system property.
50       */
51      public static final String MERGE_STRATEGY_PROPERTY = "log4j.mergeStrategy";
52  
53      private static final String[] VERBOSE_CLASSES = new String[] {ResolverUtil.class.getName()};
54  
55      private final List<? extends AbstractConfiguration> configurations;
56  
57      private MergeStrategy mergeStrategy;
58  
59      /**
60       * Construct the ComponsiteConfiguration.
61       *
62       * @param configurations The List of Configurations to merge.
63       */
64      public CompositeConfiguration(final List<? extends AbstractConfiguration> configurations) {
65          super(configurations.get(0).getLoggerContext(), ConfigurationSource.NULL_SOURCE);
66          rootNode = configurations.get(0).getRootNode();
67          this.configurations = configurations;
68          final String mergeStrategyClassName = PropertiesUtil.getProperties().getStringProperty(MERGE_STRATEGY_PROPERTY,
69                  DefaultMergeStrategy.class.getName());
70          try {
71              mergeStrategy = Loader.newInstanceOf(mergeStrategyClassName);
72          } catch (ClassNotFoundException | IllegalAccessException | NoSuchMethodException | InvocationTargetException |
73                  InstantiationException ex) {
74              mergeStrategy = new DefaultMergeStrategy();
75          }
76          for (final AbstractConfiguration config : configurations) {
77              mergeStrategy.mergeRootProperties(rootNode, config);
78          }
79          final StatusConfiguration statusConfig = new StatusConfiguration().withVerboseClasses(VERBOSE_CLASSES)
80                  .withStatus(getDefaultStatus());
81          for (final Map.Entry<String, String> entry : rootNode.getAttributes().entrySet()) {
82              final String key = entry.getKey();
83              final String value = getStrSubstitutor().replace(entry.getValue());
84              if ("status".equalsIgnoreCase(key)) {
85                  statusConfig.withStatus(value.toUpperCase());
86              } else if ("dest".equalsIgnoreCase(key)) {
87                  statusConfig.withDestination(value);
88              } else if ("shutdownHook".equalsIgnoreCase(key)) {
89                  isShutdownHookEnabled = !"disable".equalsIgnoreCase(value);
90              } else if ("shutdownTimeout".equalsIgnoreCase(key)) {
91                  shutdownTimeoutMillis = Long.parseLong(value);
92              } else if ("verbose".equalsIgnoreCase(key)) {
93                  statusConfig.withVerbosity(value);
94              } else if ("packages".equalsIgnoreCase(key)) {
95                  pluginPackages.addAll(Arrays.asList(value.split(Patterns.COMMA_SEPARATOR)));
96              } else if ("name".equalsIgnoreCase(key)) {
97                  setName(value);
98              }
99          }
100         statusConfig.initialize();
101     }
102 
103     @Override
104     public void setup() {
105         final AbstractConfiguration targetConfiguration = configurations.get(0);
106         staffChildConfiguration(targetConfiguration);
107         final WatchManager watchManager = getWatchManager();
108         final WatchManager targetWatchManager = targetConfiguration.getWatchManager();
109         final FileWatcher fileWatcher = new ConfiguratonFileWatcher(this, listeners);
110         if (targetWatchManager.getIntervalSeconds() > 0) {
111             watchManager.setIntervalSeconds(targetWatchManager.getIntervalSeconds());
112             final Map<File, FileWatcher> watchers = targetWatchManager.getWatchers();
113             for (final Map.Entry<File, FileWatcher> entry : watchers.entrySet()) {
114                 if (entry.getValue() instanceof ConfiguratonFileWatcher) {
115                     watchManager.watchFile(entry.getKey(), fileWatcher);
116                 }
117             }
118         }
119         for (final AbstractConfiguration sourceConfiguration : configurations.subList(1, configurations.size())) {
120             staffChildConfiguration(sourceConfiguration);
121             final Node sourceRoot = sourceConfiguration.getRootNode();
122             mergeStrategy.mergConfigurations(rootNode, sourceRoot, getPluginManager());
123             if (LOGGER.isEnabled(Level.ALL)) {
124                 final StringBuilder sb = new StringBuilder();
125                 printNodes("", rootNode, sb);
126                 System.out.println(sb.toString());
127             }
128             final int monitorInterval = sourceConfiguration.getWatchManager().getIntervalSeconds();
129             if (monitorInterval > 0) {
130                 final int currentInterval = watchManager.getIntervalSeconds();
131                 if (currentInterval <= 0 || monitorInterval < currentInterval) {
132                     watchManager.setIntervalSeconds(monitorInterval);
133                 }
134                 final WatchManager sourceWatchManager = sourceConfiguration.getWatchManager();
135                 final Map<File, FileWatcher> watchers = sourceWatchManager.getWatchers();
136                 for (final Map.Entry<File, FileWatcher> entry : watchers.entrySet()) {
137                     if (entry.getValue() instanceof ConfiguratonFileWatcher) {
138                         watchManager.watchFile(entry.getKey(), fileWatcher);
139                     }
140                 }
141             }
142         }
143     }
144 
145     @Override
146     public Configuration reconfigure() {
147         LOGGER.debug("Reconfiguring composite configuration");
148         final List<AbstractConfiguration> configs = new ArrayList<>();
149         final ConfigurationFactory factory = ConfigurationFactory.getInstance();
150         for (final AbstractConfiguration config : configurations) {
151             final ConfigurationSource source = config.getConfigurationSource();
152             final URI sourceURI = source.getURI();
153             Configuration currentConfig = config;
154             if (sourceURI == null) {
155                 LOGGER.warn("Unable to determine URI for configuration {}, changes to it will be ignored",
156                         config.getName());
157             } else {
158                 currentConfig = factory.getConfiguration(getLoggerContext(), config.getName(), sourceURI);
159                 if (currentConfig == null) {
160                     LOGGER.warn("Unable to reload configuration {}, changes to it will be ignored", config.getName());
161                 }
162             }
163             configs.add((AbstractConfiguration) currentConfig);
164 
165         }
166 
167         return new CompositeConfiguration(configs);
168     }
169 
170     private void staffChildConfiguration(final AbstractConfiguration childConfiguration) {
171         childConfiguration.setPluginManager(pluginManager);
172         childConfiguration.setScriptManager(scriptManager);
173         childConfiguration.setup();
174     }
175 
176     private void printNodes(final String indent, final Node node, final 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 (final Node child : node.getChildren()) {
180             printNodes(indent + "  ", child, sb);
181         }
182     }
183 
184     @Override
185     public String toString() {
186         return getClass().getName() + "@" + Integer.toHexString(hashCode()) + " [configurations=" + configurations
187                 + ", mergeStrategy=" + mergeStrategy + ", rootNode=" + rootNode + ", listeners=" + listeners
188                 + ", pluginPackages=" + pluginPackages + ", pluginManager=" + pluginManager + ", isShutdownHookEnabled="
189                 + isShutdownHookEnabled + ", shutdownTimeoutMillis=" + shutdownTimeoutMillis + ", scriptManager="
190                 + scriptManager + "]";
191     }
192 }