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.lang.reflect.InvocationTargetException;
20  import java.net.URI;
21  import java.util.ArrayList;
22  import java.util.Arrays;
23  import java.util.List;
24  import java.util.Map;
25  
26  import org.apache.logging.log4j.Level;
27  import org.apache.logging.log4j.core.config.AbstractConfiguration;
28  import org.apache.logging.log4j.core.config.Configuration;
29  import org.apache.logging.log4j.core.config.ConfigurationFactory;
30  import org.apache.logging.log4j.core.config.ConfigurationSource;
31  import org.apache.logging.log4j.core.config.Node;
32  import org.apache.logging.log4j.core.config.Reconfigurable;
33  import org.apache.logging.log4j.core.config.plugins.util.ResolverUtil;
34  import org.apache.logging.log4j.core.config.status.StatusConfiguration;
35  import org.apache.logging.log4j.core.util.Loader;
36  import org.apache.logging.log4j.core.util.Patterns;
37  import org.apache.logging.log4j.core.util.Source;
38  import org.apache.logging.log4j.core.util.WatchManager;
39  import org.apache.logging.log4j.core.util.Watcher;
40  import org.apache.logging.log4j.util.PropertiesUtil;
41  
42  /**
43   * A Composite Configuration.
44   */
45  public class CompositeConfiguration extends AbstractConfiguration implements Reconfigurable {
46  
47      /**
48       * Allow the ConfigurationFactory class to be specified as a system property.
49       */
50      public static final String MERGE_STRATEGY_PROPERTY = "log4j.mergeStrategy";
51  
52      private static final String[] VERBOSE_CLASSES = new String[] {ResolverUtil.class.getName()};
53  
54      private final List<? extends AbstractConfiguration> configurations;
55  
56      private MergeStrategy mergeStrategy;
57  
58      /**
59       * Construct the ComponsiteConfiguration.
60       *
61       * @param configurations The List of Configurations to merge.
62       */
63      public CompositeConfiguration(final List<? extends AbstractConfiguration> configurations) {
64          super(configurations.get(0).getLoggerContext(), ConfigurationSource.NULL_SOURCE);
65          rootNode = configurations.get(0).getRootNode();
66          this.configurations = configurations;
67          final String mergeStrategyClassName = PropertiesUtil.getProperties().getStringProperty(MERGE_STRATEGY_PROPERTY,
68                  DefaultMergeStrategy.class.getName());
69          try {
70              mergeStrategy = Loader.newInstanceOf(mergeStrategyClassName);
71          } catch (ClassNotFoundException | IllegalAccessException | NoSuchMethodException | InvocationTargetException |
72                  InstantiationException ex) {
73              mergeStrategy = new DefaultMergeStrategy();
74          }
75          for (final AbstractConfiguration config : configurations) {
76              mergeStrategy.mergeRootProperties(rootNode, config);
77          }
78          final StatusConfiguration statusConfig = new StatusConfiguration().withVerboseClasses(VERBOSE_CLASSES)
79                  .withStatus(getDefaultStatus());
80          for (final Map.Entry<String, String> entry : rootNode.getAttributes().entrySet()) {
81              final String key = entry.getKey();
82              final String value = getConfigurationStrSubstitutor().replace(entry.getValue());
83              if ("status".equalsIgnoreCase(key)) {
84                  statusConfig.withStatus(value.toUpperCase());
85              } else if ("dest".equalsIgnoreCase(key)) {
86                  statusConfig.withDestination(value);
87              } else if ("shutdownHook".equalsIgnoreCase(key)) {
88                  isShutdownHookEnabled = !"disable".equalsIgnoreCase(value);
89              } else if ("shutdownTimeout".equalsIgnoreCase(key)) {
90                  shutdownTimeoutMillis = Long.parseLong(value);
91              } else if ("verbose".equalsIgnoreCase(key)) {
92                  statusConfig.withVerbosity(value);
93              } else if ("packages".equalsIgnoreCase(key)) {
94                  pluginPackages.addAll(Arrays.asList(value.split(Patterns.COMMA_SEPARATOR)));
95              } else if ("name".equalsIgnoreCase(key)) {
96                  setName(value);
97              }
98          }
99          statusConfig.initialize();
100     }
101 
102     @Override
103     public void setup() {
104         final AbstractConfiguration targetConfiguration = configurations.get(0);
105         staffChildConfiguration(targetConfiguration);
106         final WatchManager watchManager = getWatchManager();
107         final WatchManager targetWatchManager = targetConfiguration.getWatchManager();
108         if (targetWatchManager.getIntervalSeconds() > 0) {
109             watchManager.setIntervalSeconds(targetWatchManager.getIntervalSeconds());
110             final Map<Source, Watcher> watchers = targetWatchManager.getConfigurationWatchers();
111             for (final Map.Entry<Source, Watcher> entry : watchers.entrySet()) {
112                 watchManager.watch(entry.getKey(), entry.getValue().newWatcher(this, listeners,
113                     entry.getValue().getLastModified()));
114             }
115         }
116         for (final AbstractConfiguration sourceConfiguration : configurations.subList(1, configurations.size())) {
117             staffChildConfiguration(sourceConfiguration);
118             final Node sourceRoot = sourceConfiguration.getRootNode();
119             mergeStrategy.mergConfigurations(rootNode, sourceRoot, getPluginManager());
120             if (LOGGER.isEnabled(Level.ALL)) {
121                 final StringBuilder sb = new StringBuilder();
122                 printNodes("", rootNode, sb);
123                 System.out.println(sb.toString());
124             }
125             final int monitorInterval = sourceConfiguration.getWatchManager().getIntervalSeconds();
126             if (monitorInterval > 0) {
127                 final int currentInterval = watchManager.getIntervalSeconds();
128                 if (currentInterval <= 0 || monitorInterval < currentInterval) {
129                     watchManager.setIntervalSeconds(monitorInterval);
130                 }
131                 final WatchManager sourceWatchManager = sourceConfiguration.getWatchManager();
132                 final Map<Source, Watcher> watchers = sourceWatchManager.getConfigurationWatchers();
133                 for (final Map.Entry<Source, Watcher> entry : watchers.entrySet()) {
134                     watchManager.watch(entry.getKey(), entry.getValue().newWatcher(this, listeners,
135                         entry.getValue().getLastModified()));
136                 }
137             }
138         }
139     }
140 
141     @Override
142     public Configuration reconfigure() {
143         LOGGER.debug("Reconfiguring composite configuration");
144         final List<AbstractConfiguration> configs = new ArrayList<>();
145         final ConfigurationFactory factory = ConfigurationFactory.getInstance();
146         for (final AbstractConfiguration config : configurations) {
147             final ConfigurationSource source = config.getConfigurationSource();
148             final URI sourceURI = source.getURI();
149             Configuration currentConfig = config;
150             if (sourceURI == null) {
151                 LOGGER.warn("Unable to determine URI for configuration {}, changes to it will be ignored",
152                         config.getName());
153             } else {
154                 currentConfig = factory.getConfiguration(getLoggerContext(), config.getName(), sourceURI);
155                 if (currentConfig == null) {
156                     LOGGER.warn("Unable to reload configuration {}, changes to it will be ignored", config.getName());
157                 }
158             }
159             configs.add((AbstractConfiguration) currentConfig);
160 
161         }
162 
163         return new CompositeConfiguration(configs);
164     }
165 
166     private void staffChildConfiguration(final AbstractConfiguration childConfiguration) {
167         childConfiguration.setPluginManager(pluginManager);
168         childConfiguration.setScriptManager(scriptManager);
169         childConfiguration.setup();
170     }
171 
172     private void printNodes(final String indent, final Node node, final StringBuilder sb) {
173         sb.append(indent).append(node.getName()).append(" type: ").append(node.getType()).append("\n");
174         sb.append(indent).append(node.getAttributes().toString()).append("\n");
175         for (final Node child : node.getChildren()) {
176             printNodes(indent + "  ", child, sb);
177         }
178     }
179 
180     @Override
181     public String toString() {
182         return getClass().getName() + "@" + Integer.toHexString(hashCode()) + " [configurations=" + configurations
183                 + ", mergeStrategy=" + mergeStrategy + ", rootNode=" + rootNode + ", listeners=" + listeners
184                 + ", pluginPackages=" + pluginPackages + ", pluginManager=" + pluginManager + ", isShutdownHookEnabled="
185                 + isShutdownHookEnabled + ", shutdownTimeoutMillis=" + shutdownTimeoutMillis + ", scriptManager="
186                 + scriptManager + "]";
187     }
188 }