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;
018
019import java.io.ByteArrayOutputStream;
020import java.io.IOException;
021import java.io.InputStream;
022import java.io.Serializable;
023import java.lang.ref.WeakReference;
024import java.util.ArrayList;
025import java.util.Arrays;
026import java.util.Collection;
027import java.util.Collections;
028import java.util.HashSet;
029import java.util.LinkedHashMap;
030import java.util.List;
031import java.util.Map;
032import java.util.Objects;
033import java.util.Set;
034import java.util.concurrent.ConcurrentHashMap;
035import java.util.concurrent.ConcurrentMap;
036import java.util.concurrent.CopyOnWriteArrayList;
037import java.util.concurrent.TimeUnit;
038
039import org.apache.logging.log4j.Level;
040import org.apache.logging.log4j.core.Appender;
041import org.apache.logging.log4j.core.Filter;
042import org.apache.logging.log4j.core.Layout;
043import org.apache.logging.log4j.core.LifeCycle2;
044import org.apache.logging.log4j.core.LogEvent;
045import org.apache.logging.log4j.core.LoggerContext;
046import org.apache.logging.log4j.core.Version;
047import org.apache.logging.log4j.core.appender.AsyncAppender;
048import org.apache.logging.log4j.core.appender.ConsoleAppender;
049import org.apache.logging.log4j.core.async.AsyncLoggerConfig;
050import org.apache.logging.log4j.core.async.AsyncLoggerConfigDelegate;
051import org.apache.logging.log4j.core.async.AsyncLoggerConfigDisruptor;
052import org.apache.logging.log4j.core.config.arbiters.Arbiter;
053import org.apache.logging.log4j.core.config.arbiters.SelectArbiter;
054import org.apache.logging.log4j.core.config.plugins.util.PluginBuilder;
055import org.apache.logging.log4j.core.config.plugins.util.PluginManager;
056import org.apache.logging.log4j.core.config.plugins.util.PluginType;
057import org.apache.logging.log4j.core.filter.AbstractFilterable;
058import org.apache.logging.log4j.core.layout.PatternLayout;
059import org.apache.logging.log4j.core.lookup.Interpolator;
060import org.apache.logging.log4j.core.lookup.MapLookup;
061import org.apache.logging.log4j.core.lookup.StrLookup;
062import org.apache.logging.log4j.core.lookup.StrSubstitutor;
063import org.apache.logging.log4j.core.net.Advertiser;
064import org.apache.logging.log4j.core.script.AbstractScript;
065import org.apache.logging.log4j.core.script.ScriptManager;
066import org.apache.logging.log4j.core.script.ScriptRef;
067import org.apache.logging.log4j.core.util.Constants;
068import org.apache.logging.log4j.core.util.DummyNanoClock;
069import org.apache.logging.log4j.core.util.Loader;
070import org.apache.logging.log4j.core.util.NameUtil;
071import org.apache.logging.log4j.core.util.NanoClock;
072import org.apache.logging.log4j.core.util.Source;
073import org.apache.logging.log4j.core.util.WatchManager;
074import org.apache.logging.log4j.core.util.Watcher;
075import org.apache.logging.log4j.core.util.WatcherFactory;
076import org.apache.logging.log4j.util.PropertiesUtil;
077
078/**
079 * The base Configuration. Many configuration implementations will extend this class.
080 */
081public abstract class AbstractConfiguration extends AbstractFilterable implements Configuration {
082
083    private static final int BUF_SIZE = 16384;
084
085    /**
086     * The root node of the configuration.
087     */
088    protected Node rootNode;
089
090    /**
091     * Listeners for configuration changes.
092     */
093    protected final List<ConfigurationListener> listeners = new CopyOnWriteArrayList<>();
094
095    /**
096     * Packages found in configuration "packages" attribute.
097     */
098    protected final List<String> pluginPackages = new ArrayList<>();
099
100    /**
101     * The plugin manager.
102     */
103    protected PluginManager pluginManager;
104
105    /**
106     * Shutdown hook is enabled by default.
107     */
108    protected boolean isShutdownHookEnabled = true;
109
110    /**
111     * Shutdown timeout in milliseconds.
112     */
113    protected long shutdownTimeoutMillis = 0;
114
115    /**
116     * The Script manager.
117     */
118    protected ScriptManager scriptManager;
119
120    /**
121     * The Advertiser which exposes appender configurations to external systems.
122     */
123    private Advertiser advertiser = new DefaultAdvertiser();
124    private Node advertiserNode = null;
125    private Object advertisement;
126    private String name;
127    private ConcurrentMap<String, Appender> appenders = new ConcurrentHashMap<>();
128    private ConcurrentMap<String, LoggerConfig> loggerConfigs = new ConcurrentHashMap<>();
129    private List<CustomLevelConfig> customLevels = Collections.emptyList();
130    private final ConcurrentMap<String, String> propertyMap = new ConcurrentHashMap<>();
131    private final StrLookup tempLookup = new Interpolator(propertyMap);
132    private final StrSubstitutor subst = new StrSubstitutor(tempLookup);
133    private LoggerConfig root = new LoggerConfig();
134    private final ConcurrentMap<String, Object> componentMap = new ConcurrentHashMap<>();
135    private final ConfigurationSource configurationSource;
136    private final ConfigurationScheduler configurationScheduler = new ConfigurationScheduler();
137    private final WatchManager watchManager = new WatchManager(configurationScheduler);
138    private AsyncLoggerConfigDisruptor asyncLoggerConfigDisruptor;
139    private NanoClock nanoClock = new DummyNanoClock();
140    private final WeakReference<LoggerContext> loggerContext;
141
142    /**
143     * Constructor.
144     */
145    protected AbstractConfiguration(final LoggerContext loggerContext, final ConfigurationSource configurationSource) {
146        this.loggerContext = new WeakReference<>(loggerContext);
147        // The loggerContext is null for the NullConfiguration class.
148        // this.loggerContext = new WeakReference(Objects.requireNonNull(loggerContext, "loggerContext is null"));
149        this.configurationSource = Objects.requireNonNull(configurationSource, "configurationSource is null");
150        componentMap.put(Configuration.CONTEXT_PROPERTIES, propertyMap);
151        pluginManager = new PluginManager(Node.CATEGORY);
152        rootNode = new Node();
153        setState(State.INITIALIZING);
154
155    }
156
157    @Override
158    public ConfigurationSource getConfigurationSource() {
159        return configurationSource;
160    }
161
162    @Override
163    public List<String> getPluginPackages() {
164        return pluginPackages;
165    }
166
167    @Override
168    public Map<String, String> getProperties() {
169        return propertyMap;
170    }
171
172    @Override
173    public ScriptManager getScriptManager() {
174        return scriptManager;
175    }
176
177    public void setScriptManager(final ScriptManager scriptManager) {
178        this.scriptManager = scriptManager;
179    }
180
181    public PluginManager getPluginManager() {
182        return pluginManager;
183    }
184
185    public void setPluginManager(final PluginManager pluginManager) {
186        this.pluginManager = pluginManager;
187    }
188
189    @Override
190    public WatchManager getWatchManager() {
191        return watchManager;
192    }
193
194    @Override
195    public ConfigurationScheduler getScheduler() {
196        return configurationScheduler;
197    }
198
199    public Node getRootNode() {
200        return rootNode;
201    }
202
203    @Override
204    public AsyncLoggerConfigDelegate getAsyncLoggerConfigDelegate() {
205        // lazily instantiate only when requested by AsyncLoggers:
206        // loading AsyncLoggerConfigDisruptor requires LMAX Disruptor jar on classpath
207        if (asyncLoggerConfigDisruptor == null) {
208            asyncLoggerConfigDisruptor = new AsyncLoggerConfigDisruptor();
209        }
210        return asyncLoggerConfigDisruptor;
211    }
212
213    /**
214     * Initialize the configuration.
215     */
216    @Override
217    public void initialize() {
218        LOGGER.debug(Version.getProductString() + " initializing configuration {}", this);
219        subst.setConfiguration(this);
220        try {
221            scriptManager = new ScriptManager(this, watchManager);
222        } catch (final LinkageError | Exception e) {
223            // LOG4J2-1920 ScriptEngineManager is not available in Android
224            LOGGER.info("Cannot initialize scripting support because this JRE does not support it.", e);
225        }
226        pluginManager.collectPlugins(pluginPackages);
227        final PluginManager levelPlugins = new PluginManager(Level.CATEGORY);
228        levelPlugins.collectPlugins(pluginPackages);
229        final Map<String, PluginType<?>> plugins = levelPlugins.getPlugins();
230        if (plugins != null) {
231            for (final PluginType<?> type : plugins.values()) {
232                try {
233                    // Cause the class to be initialized if it isn't already.
234                    Loader.initializeClass(type.getPluginClass().getName(), type.getPluginClass().getClassLoader());
235                } catch (final Exception e) {
236                    LOGGER.error("Unable to initialize {} due to {}", type.getPluginClass().getName(), e.getClass()
237                            .getSimpleName(), e);
238                }
239            }
240        }
241        setup();
242        setupAdvertisement();
243        doConfigure();
244        setState(State.INITIALIZED);
245        LOGGER.debug("Configuration {} initialized", this);
246    }
247
248    protected void initializeWatchers(Reconfigurable reconfigurable, ConfigurationSource configSource,
249        int monitorIntervalSeconds) {
250        if (configSource.getFile() != null || configSource.getURL() != null) {
251            if (monitorIntervalSeconds > 0) {
252                watchManager.setIntervalSeconds(monitorIntervalSeconds);
253                if (configSource.getFile() != null) {
254                    final Source cfgSource = new Source(configSource);
255                    final long lastModifeid = configSource.getFile().lastModified();
256                    final ConfigurationFileWatcher watcher = new ConfigurationFileWatcher(this, reconfigurable,
257                        listeners, lastModifeid);
258                    watchManager.watch(cfgSource, watcher);
259                } else if (configSource.getURL() != null) {
260                    monitorSource(reconfigurable, configSource);
261                }
262            } else if (watchManager.hasEventListeners() && configSource.getURL() != null
263                && monitorIntervalSeconds >= 0) {
264                monitorSource(reconfigurable, configSource);
265            }
266        }
267    }
268
269    private void monitorSource(Reconfigurable reconfigurable, ConfigurationSource configSource) {
270                if (configSource.getLastModified() > 0) {
271                        final Source cfgSource = new Source(configSource);
272                        final Watcher watcher = WatcherFactory.getInstance(pluginPackages)
273                                .newWatcher(cfgSource, this, reconfigurable, listeners, configSource.getLastModified());
274                        if (watcher != null) {
275                                watchManager.watch(cfgSource, watcher);
276                        }
277                } else {
278                        LOGGER.info("{} does not support dynamic reconfiguration", configSource.getURI());
279                }
280        }
281
282        /**
283     * Start the configuration.
284     */
285    @Override
286    public void start() {
287        // Preserve the prior behavior of initializing during start if not initialized.
288        if (getState().equals(State.INITIALIZING)) {
289            initialize();
290        }
291        LOGGER.debug("Starting configuration {}", this);
292        this.setStarting();
293        if (watchManager.getIntervalSeconds() >= 0) {
294            watchManager.start();
295        }
296        if (hasAsyncLoggers()) {
297            asyncLoggerConfigDisruptor.start();
298        }
299        final Set<LoggerConfig> alreadyStarted = new HashSet<>();
300        for (final LoggerConfig logger : loggerConfigs.values()) {
301            logger.start();
302            alreadyStarted.add(logger);
303        }
304        for (final Appender appender : appenders.values()) {
305            appender.start();
306        }
307        if (!alreadyStarted.contains(root)) { // LOG4J2-392
308            root.start(); // LOG4J2-336
309        }
310        super.start();
311        LOGGER.debug("Started configuration {} OK.", this);
312    }
313
314    private boolean hasAsyncLoggers() {
315        if (root instanceof AsyncLoggerConfig) {
316            return true;
317        }
318        for (final LoggerConfig logger : loggerConfigs.values()) {
319            if (logger instanceof AsyncLoggerConfig) {
320                return true;
321            }
322        }
323        return false;
324    }
325
326    /**
327     * Tear down the configuration.
328     */
329    @Override
330    public boolean stop(final long timeout, final TimeUnit timeUnit) {
331        this.setStopping();
332        super.stop(timeout, timeUnit, false);
333        LOGGER.trace("Stopping {}...", this);
334
335        // Stop the components that are closest to the application first:
336        // 1. Notify all LoggerConfigs' ReliabilityStrategy that the configuration will be stopped.
337        // 2. Stop the LoggerConfig objects (this may stop nested Filters)
338        // 3. Stop the AsyncLoggerConfigDelegate. This shuts down the AsyncLoggerConfig Disruptor
339        //    and waits until all events in the RingBuffer have been processed.
340        // 4. Stop all AsyncAppenders. This shuts down the associated thread and
341        //    waits until all events in the queue have been processed. (With optional timeout.)
342        // 5. Notify all LoggerConfigs' ReliabilityStrategy that appenders will be stopped.
343        //    This guarantees that any event received by a LoggerConfig before reconfiguration
344        //    are passed on to the Appenders before the Appenders are stopped.
345        // 6. Stop the remaining running Appenders. (It should now be safe to do so.)
346        // 7. Notify all LoggerConfigs that their Appenders can be cleaned up.
347
348        for (final LoggerConfig loggerConfig : loggerConfigs.values()) {
349            loggerConfig.getReliabilityStrategy().beforeStopConfiguration(this);
350        }
351        root.getReliabilityStrategy().beforeStopConfiguration(this);
352
353        final String cls = getClass().getSimpleName();
354        LOGGER.trace("{} notified {} ReliabilityStrategies that config will be stopped.", cls, loggerConfigs.size()
355                + 1);
356
357        if (!loggerConfigs.isEmpty()) {
358            LOGGER.trace("{} stopping {} LoggerConfigs.", cls, loggerConfigs.size());
359            for (final LoggerConfig logger : loggerConfigs.values()) {
360                logger.stop(timeout, timeUnit);
361            }
362        }
363        LOGGER.trace("{} stopping root LoggerConfig.", cls);
364        if (!root.isStopped()) {
365            root.stop(timeout, timeUnit);
366        }
367
368        if (hasAsyncLoggers()) {
369            LOGGER.trace("{} stopping AsyncLoggerConfigDisruptor.", cls);
370            asyncLoggerConfigDisruptor.stop(timeout, timeUnit);
371        }
372
373        LOGGER.trace("{} notifying ReliabilityStrategies that appenders will be stopped.", cls);
374        for (final LoggerConfig loggerConfig : loggerConfigs.values()) {
375            loggerConfig.getReliabilityStrategy().beforeStopAppenders();
376        }
377        root.getReliabilityStrategy().beforeStopAppenders();
378
379        // Stop the appenders in reverse order in case they still have activity.
380        final Appender[] array = appenders.values().toArray(new Appender[appenders.size()]);
381        final List<Appender> async = getAsyncAppenders(array);
382        if (!async.isEmpty()) {
383            // LOG4J2-511, LOG4J2-392 stop AsyncAppenders first
384            LOGGER.trace("{} stopping {} AsyncAppenders.", cls, async.size());
385            for (final Appender appender : async) {
386                if (appender instanceof LifeCycle2) {
387                    ((LifeCycle2) appender).stop(timeout, timeUnit);
388                } else {
389                    appender.stop();
390                }
391            }
392        }
393
394        LOGGER.trace("{} stopping remaining Appenders.", cls);
395        int appenderCount = 0;
396        for (int i = array.length - 1; i >= 0; --i) {
397            if (array[i].isStarted()) { // then stop remaining Appenders
398                if (array[i] instanceof LifeCycle2) {
399                    ((LifeCycle2) array[i]).stop(timeout, timeUnit);
400                } else {
401                    array[i].stop();
402                }
403                appenderCount++;
404            }
405        }
406        LOGGER.trace("{} stopped {} remaining Appenders.", cls, appenderCount);
407
408        LOGGER.trace("{} cleaning Appenders from {} LoggerConfigs.", cls, loggerConfigs.size() + 1);
409        for (final LoggerConfig loggerConfig : loggerConfigs.values()) {
410
411            // LOG4J2-520, LOG4J2-392:
412            // Important: do not clear appenders until after all AsyncLoggerConfigs
413            // have been stopped! Stopping the last AsyncLoggerConfig will
414            // shut down the disruptor and wait for all enqueued events to be processed.
415            // Only *after this* the appenders can be cleared or events will be lost.
416            loggerConfig.clearAppenders();
417        }
418        root.clearAppenders();
419
420        if (watchManager.isStarted()) {
421            watchManager.stop(timeout, timeUnit);
422        }
423        configurationScheduler.stop(timeout, timeUnit);
424
425        if (advertiser != null && advertisement != null) {
426            advertiser.unadvertise(advertisement);
427        }
428        setStopped();
429        LOGGER.debug("Stopped {} OK", this);
430        return true;
431    }
432
433    private List<Appender> getAsyncAppenders(final Appender[] all) {
434        final List<Appender> result = new ArrayList<>();
435        for (int i = all.length - 1; i >= 0; --i) {
436            if (all[i] instanceof AsyncAppender) {
437                result.add(all[i]);
438            }
439        }
440        return result;
441    }
442
443    @Override
444    public boolean isShutdownHookEnabled() {
445        return isShutdownHookEnabled;
446    }
447
448    @Override
449    public long getShutdownTimeoutMillis() {
450        return shutdownTimeoutMillis;
451    }
452
453    public void setup() {
454        // default does nothing, subclasses do work.
455    }
456
457    protected Level getDefaultStatus() {
458        final String statusLevel = PropertiesUtil.getProperties().getStringProperty(
459                Constants.LOG4J_DEFAULT_STATUS_LEVEL, Level.ERROR.name());
460        try {
461            return Level.toLevel(statusLevel);
462        } catch (final Exception ex) {
463            return Level.ERROR;
464        }
465    }
466
467    protected void createAdvertiser(final String advertiserString, final ConfigurationSource configSource,
468            final byte[] buffer, final String contentType) {
469        if (advertiserString != null) {
470            final Node node = new Node(null, advertiserString, null);
471            final Map<String, String> attributes = node.getAttributes();
472            attributes.put("content", new String(buffer));
473            attributes.put("contentType", contentType);
474            attributes.put("name", "configuration");
475            if (configSource.getLocation() != null) {
476                attributes.put("location", configSource.getLocation());
477            }
478            advertiserNode = node;
479        }
480    }
481
482    private void setupAdvertisement() {
483        if (advertiserNode != null) {
484            final String nodeName = advertiserNode.getName();
485            final PluginType<?> type = pluginManager.getPluginType(nodeName);
486            if (type != null) {
487                final Class<? extends Advertiser> clazz = type.getPluginClass().asSubclass(Advertiser.class);
488                try {
489                    advertiser = clazz.newInstance();
490                    advertisement = advertiser.advertise(advertiserNode.getAttributes());
491                } catch (final InstantiationException e) {
492                    LOGGER.error("InstantiationException attempting to instantiate advertiser: {}", nodeName, e);
493                } catch (final IllegalAccessException e) {
494                    LOGGER.error("IllegalAccessException attempting to instantiate advertiser: {}", nodeName, e);
495                }
496            }
497        }
498    }
499
500    @SuppressWarnings("unchecked")
501    @Override
502    public <T> T getComponent(final String componentName) {
503        return (T) componentMap.get(componentName);
504    }
505
506    @Override
507    public void addComponent(final String componentName, final Object obj) {
508        componentMap.putIfAbsent(componentName, obj);
509    }
510
511    protected void preConfigure(final Node node) {
512        try {
513            for (final Node child : node.getChildren()) {
514                if (child.getType() == null) {
515                    LOGGER.error("Unable to locate plugin type for " + child.getName());
516                    continue;
517                }
518                final Class<?> clazz = child.getType().getPluginClass();
519                if (clazz.isAnnotationPresent(Scheduled.class)) {
520                    configurationScheduler.incrementScheduledItems();
521                }
522                preConfigure(child);
523            }
524        } catch (final Exception ex) {
525            LOGGER.error("Error capturing node data for node " + node.getName(), ex);
526        }
527    }
528
529    /**
530     * Process conditions by evaluating them and including the children of conditions that are true
531     * and discarding those that are not.
532     * @param node The node to evaluate.
533     */
534    protected void processConditionals(final Node node) {
535        try {
536            List<Node> addList = new ArrayList<>();
537            List<Node> removeList = new ArrayList<>();
538            for (final Node child : node.getChildren()) {
539                final PluginType<?> type = child.getType();
540                if (type != null && Arbiter.ELEMENT_TYPE.equals(type.getElementName())) {
541                    final Class<?> clazz = type.getPluginClass();
542                    if (SelectArbiter.class.isAssignableFrom(clazz)) {
543                        removeList.add(child);
544                        addList.addAll(processSelect(child, type));
545                    } else if (Arbiter.class.isAssignableFrom(clazz)) {
546                        removeList.add(child);
547                        try {
548                            Arbiter condition = (Arbiter) createPluginObject(type, child, null);
549                            if (condition.isCondition()) {
550                                addList.addAll(child.getChildren());
551                                processConditionals(child);
552                            }
553                        } catch (final Exception inner) {
554                            LOGGER.error("Exception processing {}: Ignoring and including children",
555                                    type.getPluginClass());
556                            processConditionals(child);
557                        }
558                    } else {
559                        LOGGER.error("Encountered Condition Plugin that does not implement Condition: {}",
560                                child.getName());
561                        processConditionals(child);
562                    }
563                } else {
564                    processConditionals(child);
565                }
566            }
567            if (!removeList.isEmpty()) {
568                List<Node> children = node.getChildren();
569                children.removeAll(removeList);
570                children.addAll(addList);
571                for (Node grandChild : addList) {
572                    grandChild.setParent(node);
573                }
574            }
575        } catch (final Exception ex) {
576            LOGGER.error("Error capturing node data for node " + node.getName(), ex);
577        }
578    }
579
580    /**
581     * Handle Select nodes. This finds the first child condition that returns true and attaches its children
582     * to the parent of the Select Node. Other Nodes are discarded.
583     * @param selectNode The Select Node.
584     * @param type The PluginType of the Select Node.
585     * @return The list of Nodes to be added to the parent.
586     */
587    protected List<Node> processSelect(Node selectNode, PluginType<?> type) {
588        List<Node> addList = new ArrayList<>();
589        SelectArbiter select = (SelectArbiter) createPluginObject(type, selectNode, null);
590        List<Arbiter> conditions = new ArrayList<>();
591        for (Node child : selectNode.getChildren()) {
592            PluginType<?> nodeType = child.getType();
593            if (nodeType != null) {
594                if (Arbiter.class.isAssignableFrom(nodeType.getPluginClass())) {
595                    Arbiter condition = (Arbiter) createPluginObject(nodeType, child, null);
596                    conditions.add(condition);
597                    child.setObject(condition);
598                } else {
599                    LOGGER.error("Invalid Node {} for Select. Must be a Condition",
600                            child.getName());
601                }
602            } else {
603                LOGGER.error("No PluginType for node {}", child.getName());
604            }
605        }
606        Arbiter condition = select.evaluateConditions(conditions);
607        if (condition != null) {
608            for (Node child : selectNode.getChildren()) {
609                if (condition == child.getObject()) {
610                    addList.addAll(child.getChildren());
611                    processConditionals(child);
612                }
613            }
614        }
615        return addList;
616    }
617
618    protected void doConfigure() {
619        processConditionals(rootNode);
620        preConfigure(rootNode);
621        configurationScheduler.start();
622        if (rootNode.hasChildren() && rootNode.getChildren().get(0).getName().equalsIgnoreCase("Properties")) {
623            final Node first = rootNode.getChildren().get(0);
624            createConfiguration(first, null);
625            if (first.getObject() != null) {
626                subst.setVariableResolver((StrLookup) first.getObject());
627            }
628        } else {
629            final Map<String, String> map = this.getComponent(CONTEXT_PROPERTIES);
630            final StrLookup lookup = map == null ? null : new MapLookup(map);
631            subst.setVariableResolver(new Interpolator(lookup, pluginPackages));
632        }
633
634        boolean setLoggers = false;
635        boolean setRoot = false;
636        for (final Node child : rootNode.getChildren()) {
637            if (child.getName().equalsIgnoreCase("Properties")) {
638                if (tempLookup == subst.getVariableResolver()) {
639                    LOGGER.error("Properties declaration must be the first element in the configuration");
640                }
641                continue;
642            }
643            createConfiguration(child, null);
644            if (child.getObject() == null) {
645                continue;
646            }
647            if (child.getName().equalsIgnoreCase("Scripts")) {
648                for (final AbstractScript script : child.getObject(AbstractScript[].class)) {
649                    if (script instanceof ScriptRef) {
650                        LOGGER.error("Script reference to {} not added. Scripts definition cannot contain script references",
651                                script.getName());
652                    } else if (scriptManager != null) {
653                        scriptManager.addScript(script);
654                    }
655                }
656            } else if (child.getName().equalsIgnoreCase("Appenders")) {
657                appenders = child.getObject();
658            } else if (child.isInstanceOf(Filter.class)) {
659                addFilter(child.getObject(Filter.class));
660            } else if (child.getName().equalsIgnoreCase("Loggers")) {
661                final Loggers l = child.getObject();
662                loggerConfigs = l.getMap();
663                setLoggers = true;
664                if (l.getRoot() != null) {
665                    root = l.getRoot();
666                    setRoot = true;
667                }
668            } else if (child.getName().equalsIgnoreCase("CustomLevels")) {
669                customLevels = child.getObject(CustomLevels.class).getCustomLevels();
670            } else if (child.isInstanceOf(CustomLevelConfig.class)) {
671                final List<CustomLevelConfig> copy = new ArrayList<>(customLevels);
672                copy.add(child.getObject(CustomLevelConfig.class));
673                customLevels = copy;
674            } else {
675                final List<String> expected = Arrays.asList("\"Appenders\"", "\"Loggers\"", "\"Properties\"",
676                        "\"Scripts\"", "\"CustomLevels\"");
677                LOGGER.error("Unknown object \"{}\" of type {} is ignored: try nesting it inside one of: {}.",
678                        child.getName(), child.getObject().getClass().getName(), expected);
679            }
680        }
681
682        if (!setLoggers) {
683            LOGGER.warn("No Loggers were configured, using default. Is the Loggers element missing?");
684            setToDefault();
685            return;
686        } else if (!setRoot) {
687            LOGGER.warn("No Root logger was configured, creating default ERROR-level Root logger with Console appender");
688            setToDefault();
689            // return; // LOG4J2-219: creating default root=ok, but don't exclude configured Loggers
690        }
691
692        for (final Map.Entry<String, LoggerConfig> entry : loggerConfigs.entrySet()) {
693            final LoggerConfig loggerConfig = entry.getValue();
694            for (final AppenderRef ref : loggerConfig.getAppenderRefs()) {
695                final Appender app = appenders.get(ref.getRef());
696                if (app != null) {
697                    loggerConfig.addAppender(app, ref.getLevel(), ref.getFilter());
698                } else {
699                    LOGGER.error("Unable to locate appender \"{}\" for logger config \"{}\"", ref.getRef(),
700                            loggerConfig);
701                }
702            }
703
704        }
705
706        setParents();
707    }
708
709    protected void setToDefault() {
710        // LOG4J2-1176 facilitate memory leak investigation
711        setName(DefaultConfiguration.DEFAULT_NAME + "@" + Integer.toHexString(hashCode()));
712        final Layout<? extends Serializable> layout = PatternLayout.newBuilder()
713                .withPattern(DefaultConfiguration.DEFAULT_PATTERN)
714                .withConfiguration(this)
715                .build();
716        final Appender appender = ConsoleAppender.createDefaultAppenderForLayout(layout);
717        appender.start();
718        addAppender(appender);
719        final LoggerConfig rootLoggerConfig = getRootLogger();
720        rootLoggerConfig.addAppender(appender, null, null);
721
722        final Level defaultLevel = Level.ERROR;
723        final String levelName = PropertiesUtil.getProperties().getStringProperty(DefaultConfiguration.DEFAULT_LEVEL,
724                defaultLevel.name());
725        final Level level = Level.valueOf(levelName);
726        rootLoggerConfig.setLevel(level != null ? level : defaultLevel);
727    }
728
729    /**
730     * Set the name of the configuration.
731     *
732     * @param name The name.
733     */
734    public void setName(final String name) {
735        this.name = name;
736    }
737
738    /**
739     * Returns the name of the configuration.
740     *
741     * @return the name of the configuration.
742     */
743    @Override
744    public String getName() {
745        return name;
746    }
747
748    /**
749     * Add a listener for changes on the configuration.
750     *
751     * @param listener The ConfigurationListener to add.
752     */
753    @Override
754    public void addListener(final ConfigurationListener listener) {
755        listeners.add(listener);
756    }
757
758    /**
759     * Remove a ConfigurationListener.
760     *
761     * @param listener The ConfigurationListener to remove.
762     */
763    @Override
764    public void removeListener(final ConfigurationListener listener) {
765        listeners.remove(listener);
766    }
767
768    /**
769     * Returns the Appender with the specified name.
770     *
771     * @param appenderName The name of the Appender.
772     * @return the Appender with the specified name or null if the Appender cannot be located.
773     */
774    @Override
775    @SuppressWarnings("unchecked")
776    public <T extends Appender> T getAppender(final String appenderName) {
777        return appenderName != null ? (T) appenders.get(appenderName) : null;
778    }
779
780    /**
781     * Returns a Map containing all the Appenders and their name.
782     *
783     * @return A Map containing each Appender's name and the Appender object.
784     */
785    @Override
786    public Map<String, Appender> getAppenders() {
787        return appenders;
788    }
789
790    /**
791     * Adds an Appender to the configuration.
792     *
793     * @param appender The Appender to add.
794     */
795    @Override
796    public void addAppender(final Appender appender) {
797        if (appender != null) {
798            appenders.putIfAbsent(appender.getName(), appender);
799        }
800    }
801
802    @Override
803    public StrSubstitutor getStrSubstitutor() {
804        return subst;
805    }
806
807    @Override
808    public void setAdvertiser(final Advertiser advertiser) {
809        this.advertiser = advertiser;
810    }
811
812    @Override
813    public Advertiser getAdvertiser() {
814        return advertiser;
815    }
816
817    /*
818     * (non-Javadoc)
819     *
820     * @see org.apache.logging.log4j.core.config.ReliabilityStrategyFactory#getReliabilityStrategy(org.apache.logging.log4j
821     * .core.config.LoggerConfig)
822     */
823    @Override
824    public ReliabilityStrategy getReliabilityStrategy(final LoggerConfig loggerConfig) {
825        return ReliabilityStrategyFactory.getReliabilityStrategy(loggerConfig);
826    }
827
828    /**
829     * Associates an Appender with a LoggerConfig. This method is synchronized in case a Logger with the same name is
830     * being updated at the same time.
831     *
832     * Note: This method is not used when configuring via configuration. It is primarily used by unit tests.
833     *
834     * @param logger The Logger the Appender will be associated with.
835     * @param appender The Appender.
836     */
837    @Override
838    public synchronized void addLoggerAppender(final org.apache.logging.log4j.core.Logger logger,
839            final Appender appender) {
840        if (appender == null || logger == null) {
841            return;
842        }
843        final String loggerName = logger.getName();
844        appenders.putIfAbsent(appender.getName(), appender);
845        final LoggerConfig lc = getLoggerConfig(loggerName);
846        if (lc.getName().equals(loggerName)) {
847            lc.addAppender(appender, null, null);
848        } else {
849            final LoggerConfig nlc = new LoggerConfig(loggerName, lc.getLevel(), lc.isAdditive());
850            nlc.addAppender(appender, null, null);
851            nlc.setParent(lc);
852            loggerConfigs.putIfAbsent(loggerName, nlc);
853            setParents();
854            logger.getContext().updateLoggers();
855        }
856    }
857
858    /**
859     * Associates a Filter with a LoggerConfig. This method is synchronized in case a Logger with the same name is being
860     * updated at the same time.
861     *
862     * Note: This method is not used when configuring via configuration. It is primarily used by unit tests.
863     *
864     * @param logger The Logger the Footer will be associated with.
865     * @param filter The Filter.
866     */
867    @Override
868    public synchronized void addLoggerFilter(final org.apache.logging.log4j.core.Logger logger, final Filter filter) {
869        final String loggerName = logger.getName();
870        final LoggerConfig lc = getLoggerConfig(loggerName);
871        if (lc.getName().equals(loggerName)) {
872            lc.addFilter(filter);
873        } else {
874            final LoggerConfig nlc = new LoggerConfig(loggerName, lc.getLevel(), lc.isAdditive());
875            nlc.addFilter(filter);
876            nlc.setParent(lc);
877            loggerConfigs.putIfAbsent(loggerName, nlc);
878            setParents();
879            logger.getContext().updateLoggers();
880        }
881    }
882
883    /**
884     * Marks a LoggerConfig as additive. This method is synchronized in case a Logger with the same name is being
885     * updated at the same time.
886     *
887     * Note: This method is not used when configuring via configuration. It is primarily used by unit tests.
888     *
889     * @param logger The Logger the Appender will be associated with.
890     * @param additive True if the LoggerConfig should be additive, false otherwise.
891     */
892    @Override
893    public synchronized void setLoggerAdditive(final org.apache.logging.log4j.core.Logger logger, final boolean additive) {
894        final String loggerName = logger.getName();
895        final LoggerConfig lc = getLoggerConfig(loggerName);
896        if (lc.getName().equals(loggerName)) {
897            lc.setAdditive(additive);
898        } else {
899            final LoggerConfig nlc = new LoggerConfig(loggerName, lc.getLevel(), additive);
900            nlc.setParent(lc);
901            loggerConfigs.putIfAbsent(loggerName, nlc);
902            setParents();
903            logger.getContext().updateLoggers();
904        }
905    }
906
907    /**
908     * Remove an Appender. First removes any associations between LoggerConfigs and the Appender, removes the Appender
909     * from this appender list and then stops the appender. This method is synchronized in case an Appender with the
910     * same name is being added during the removal.
911     *
912     * @param appenderName the name of the appender to remove.
913     */
914    public synchronized void removeAppender(final String appenderName) {
915        for (final LoggerConfig logger : loggerConfigs.values()) {
916            logger.removeAppender(appenderName);
917        }
918        final Appender app = appenderName != null ? appenders.remove(appenderName) : null;
919
920        if (app != null) {
921            app.stop();
922        }
923    }
924
925    /*
926     * (non-Javadoc)
927     *
928     * @see org.apache.logging.log4j.core.config.Configuration#getCustomLevels()
929     */
930    @Override
931    public List<CustomLevelConfig> getCustomLevels() {
932        return Collections.unmodifiableList(customLevels);
933    }
934
935    /**
936     * Locates the appropriate LoggerConfig for a Logger name. This will remove tokens from the package name as
937     * necessary or return the root LoggerConfig if no other matches were found.
938     *
939     * @param loggerName The Logger name.
940     * @return The located LoggerConfig.
941     */
942    @Override
943    public LoggerConfig getLoggerConfig(final String loggerName) {
944        LoggerConfig loggerConfig = loggerConfigs.get(loggerName);
945        if (loggerConfig != null) {
946            return loggerConfig;
947        }
948        String substr = loggerName;
949        while ((substr = NameUtil.getSubName(substr)) != null) {
950            loggerConfig = loggerConfigs.get(substr);
951            if (loggerConfig != null) {
952                return loggerConfig;
953            }
954        }
955        return root;
956    }
957
958    @Override
959    public LoggerContext getLoggerContext() {
960        return loggerContext.get();
961    }
962
963    /**
964     * Returns the root Logger.
965     *
966     * @return the root Logger.
967     */
968    @Override
969    public LoggerConfig getRootLogger() {
970        return root;
971    }
972
973    /**
974     * Returns a Map of all the LoggerConfigs.
975     *
976     * @return a Map with each entry containing the name of the Logger and the LoggerConfig.
977     */
978    @Override
979    public Map<String, LoggerConfig> getLoggers() {
980        return Collections.unmodifiableMap(loggerConfigs);
981    }
982
983    /**
984     * Returns the LoggerConfig with the specified name.
985     *
986     * @param loggerName The Logger name.
987     * @return The LoggerConfig or null if no match was found.
988     */
989    public LoggerConfig getLogger(final String loggerName) {
990        return loggerConfigs.get(loggerName);
991    }
992
993    /**
994     * Add a loggerConfig. The LoggerConfig must already be configured with Appenders, Filters, etc. After addLogger is
995     * called LoggerContext.updateLoggers must be called.
996     *
997     * @param loggerName The name of the Logger.
998     * @param loggerConfig The LoggerConfig.
999     */
1000    @Override
1001    public synchronized void addLogger(final String loggerName, final LoggerConfig loggerConfig) {
1002        loggerConfigs.putIfAbsent(loggerName, loggerConfig);
1003        setParents();
1004    }
1005
1006    /**
1007     * Remove a LoggerConfig.
1008     *
1009     * @param loggerName The name of the Logger.
1010     */
1011    @Override
1012    public synchronized void removeLogger(final String loggerName) {
1013        loggerConfigs.remove(loggerName);
1014        setParents();
1015    }
1016
1017    @Override
1018    public void createConfiguration(final Node node, final LogEvent event) {
1019        final PluginType<?> type = node.getType();
1020        if (type != null && type.isDeferChildren()) {
1021            node.setObject(createPluginObject(type, node, event));
1022        } else {
1023            for (final Node child : node.getChildren()) {
1024                createConfiguration(child, event);
1025            }
1026
1027            if (type == null) {
1028                if (node.getParent() != null) {
1029                    LOGGER.error("Unable to locate plugin for {}", node.getName());
1030                }
1031            } else {
1032                node.setObject(createPluginObject(type, node, event));
1033            }
1034        }
1035    }
1036
1037    /**
1038     * This method is used by Arbiters to create specific children.
1039     * @param type The PluginType.
1040     * @param node The Node.
1041     * @return The created object or null;
1042     */
1043    public Object createPluginObject(final PluginType<?> type, final Node node) {
1044        if (this.getState().equals(State.INITIALIZING)) {
1045            return createPluginObject(type, node, null);
1046        } else {
1047            LOGGER.warn("Plugin Object creation is not allowed after initialization");
1048            return null;
1049        }
1050    }
1051
1052    /**
1053     * Invokes a static factory method to either create the desired object or to create a builder object that creates
1054     * the desired object. In the case of a factory method, it should be annotated with
1055     * {@link org.apache.logging.log4j.core.config.plugins.PluginFactory}, and each parameter should be annotated with
1056     * an appropriate plugin annotation depending on what that parameter describes. Parameters annotated with
1057     * {@link org.apache.logging.log4j.core.config.plugins.PluginAttribute} must be a type that can be converted from a
1058     * string using one of the {@link org.apache.logging.log4j.core.config.plugins.convert.TypeConverter TypeConverters}
1059     * . Parameters with {@link org.apache.logging.log4j.core.config.plugins.PluginElement} may be any plugin class or
1060     * an array of a plugin class. Collections and Maps are currently not supported, although the factory method that is
1061     * called can create these from an array.
1062     *
1063     * Plugins can also be created using a builder class that implements
1064     * {@link org.apache.logging.log4j.core.util.Builder}. In that case, a static method annotated with
1065     * {@link org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute} should create the builder class, and
1066     * the various fields in the builder class should be annotated similarly to the method parameters. However, instead
1067     * of using PluginAttribute, one should use
1068     * {@link org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute} where the default value can be
1069     * specified as the default field value instead of as an additional annotation parameter.
1070     *
1071     * In either case, there are also annotations for specifying a
1072     * {@link org.apache.logging.log4j.core.config.Configuration} (
1073     * {@link org.apache.logging.log4j.core.config.plugins.PluginConfiguration}) or a
1074     * {@link org.apache.logging.log4j.core.config.Node} (
1075     * {@link org.apache.logging.log4j.core.config.plugins.PluginNode}).
1076     *
1077     * Although the happy path works, more work still needs to be done to log incorrect parameters. These will generally
1078     * result in unhelpful InvocationTargetExceptions.
1079     *
1080     * @param type the type of plugin to create.
1081     * @param node the corresponding configuration node for this plugin to create.
1082     * @param event the LogEvent that spurred the creation of this plugin
1083     * @return the created plugin object or {@code null} if there was an error setting it up.
1084     * @see org.apache.logging.log4j.core.config.plugins.util.PluginBuilder
1085     * @see org.apache.logging.log4j.core.config.plugins.visitors.PluginVisitor
1086     * @see org.apache.logging.log4j.core.config.plugins.convert.TypeConverter
1087     */
1088    private Object createPluginObject(final PluginType<?> type, final Node node, final LogEvent event) {
1089        final Class<?> clazz = type.getPluginClass();
1090
1091        if (Map.class.isAssignableFrom(clazz)) {
1092            try {
1093                return createPluginMap(node);
1094            } catch (final Exception e) {
1095                LOGGER.warn("Unable to create Map for {} of class {}", type.getElementName(), clazz, e);
1096            }
1097        }
1098
1099        if (Collection.class.isAssignableFrom(clazz)) {
1100            try {
1101                return createPluginCollection(node);
1102            } catch (final Exception e) {
1103                LOGGER.warn("Unable to create List for {} of class {}", type.getElementName(), clazz, e);
1104            }
1105        }
1106
1107        return new PluginBuilder(type).withConfiguration(this).withConfigurationNode(node).forLogEvent(event).build();
1108    }
1109
1110    private static Map<String, ?> createPluginMap(final Node node) {
1111        final Map<String, Object> map = new LinkedHashMap<>();
1112        for (final Node child : node.getChildren()) {
1113            final Object object = child.getObject();
1114            map.put(child.getName(), object);
1115        }
1116        return map;
1117    }
1118
1119    private static Collection<?> createPluginCollection(final Node node) {
1120        final List<Node> children = node.getChildren();
1121        final Collection<Object> list = new ArrayList<>(children.size());
1122        for (final Node child : children) {
1123            final Object object = child.getObject();
1124            list.add(object);
1125        }
1126        return list;
1127    }
1128
1129    private void setParents() {
1130        for (final Map.Entry<String, LoggerConfig> entry : loggerConfigs.entrySet()) {
1131            final LoggerConfig logger = entry.getValue();
1132            String key = entry.getKey();
1133            if (!key.isEmpty()) {
1134                final int i = key.lastIndexOf('.');
1135                if (i > 0) {
1136                    key = key.substring(0, i);
1137                    LoggerConfig parent = getLoggerConfig(key);
1138                    if (parent == null) {
1139                        parent = root;
1140                    }
1141                    logger.setParent(parent);
1142                } else {
1143                    logger.setParent(root);
1144                }
1145            }
1146        }
1147    }
1148
1149    /**
1150     * Reads an InputStream using buffered reads into a byte array buffer. The given InputStream will remain open after
1151     * invocation of this method.
1152     *
1153     * @param is the InputStream to read into a byte array buffer.
1154     * @return a byte array of the InputStream contents.
1155     * @throws IOException if the {@code read} method of the provided InputStream throws this exception.
1156     */
1157    protected static byte[] toByteArray(final InputStream is) throws IOException {
1158        final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
1159
1160        int nRead;
1161        final byte[] data = new byte[BUF_SIZE];
1162
1163        while ((nRead = is.read(data, 0, data.length)) != -1) {
1164            buffer.write(data, 0, nRead);
1165        }
1166
1167        return buffer.toByteArray();
1168    }
1169
1170    @Override
1171    public NanoClock getNanoClock() {
1172        return nanoClock;
1173    }
1174
1175    @Override
1176    public void setNanoClock(final NanoClock nanoClock) {
1177        this.nanoClock = Objects.requireNonNull(nanoClock, "nanoClock");
1178    }
1179}