package org.apache.logging.log4j.core.config;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.Logger;
import org.apache.logging.log4j.core.appender.AsyncAppender;
import org.apache.logging.log4j.core.appender.ConsoleAppender;
import org.apache.logging.log4j.core.async.AsyncLoggerConfig;
import org.apache.logging.log4j.core.async.AsyncLoggerContextSelector;
import org.apache.logging.log4j.core.config.plugins.util.PluginBuilder;
import org.apache.logging.log4j.core.config.plugins.util.PluginManager;
import org.apache.logging.log4j.core.config.plugins.util.PluginType;
import org.apache.logging.log4j.core.filter.AbstractFilterable;
import org.apache.logging.log4j.core.impl.Log4jContextFactory;
import org.apache.logging.log4j.core.layout.PatternLayout;
import org.apache.logging.log4j.core.lookup.Interpolator;
import org.apache.logging.log4j.core.lookup.MapLookup;
import org.apache.logging.log4j.core.lookup.StrLookup;
import org.apache.logging.log4j.core.lookup.StrSubstitutor;
import org.apache.logging.log4j.core.net.Advertiser;
import org.apache.logging.log4j.core.selector.ContextSelector;
import org.apache.logging.log4j.core.util.Assert;
import org.apache.logging.log4j.core.util.Loader;
import org.apache.logging.log4j.core.util.NameUtil;
import org.apache.logging.log4j.spi.LoggerContextFactory;
import org.apache.logging.log4j.util.PropertiesUtil;

public abstract class AbstractConfiguration extends AbstractFilterable implements Configuration {
   private static final int BUF_SIZE = 16384;
   protected Node rootNode;
   protected final List<ConfigurationListener> listeners = new CopyOnWriteArrayList();
   protected ConfigurationMonitor monitor = new DefaultConfigurationMonitor();
   private Advertiser advertiser = new DefaultAdvertiser();
   private Node advertiserNode = null;
   private Object advertisement;
   protected boolean isShutdownHookEnabled = true;
   private String name;
   private ConcurrentMap<String, Appender> appenders = new ConcurrentHashMap();
   private ConcurrentMap<String, LoggerConfig> loggers = new ConcurrentHashMap();
   private final ConcurrentMap<String, String> properties = new ConcurrentHashMap();
   private final StrLookup tempLookup;
   private final StrSubstitutor subst;
   private LoggerConfig root;
   private final ConcurrentMap<String, Object> componentMap;
   protected PluginManager pluginManager;
   private final ConfigurationSource configurationSource;

   protected AbstractConfiguration(ConfigurationSource configurationSource) {
      this.tempLookup = new Interpolator(this.properties);
      this.subst = new StrSubstitutor(this.tempLookup);
      this.root = new LoggerConfig();
      this.componentMap = new ConcurrentHashMap();
      this.configurationSource = (ConfigurationSource)Assert.requireNonNull(configurationSource, "configurationSource is null");
      this.componentMap.put("ContextProperties", this.properties);
      this.pluginManager = new PluginManager("Core");
      this.rootNode = new Node();
   }

   public ConfigurationSource getConfigurationSource() {
      return this.configurationSource;
   }

   public Map<String, String> getProperties() {
      return this.properties;
   }

   public void start() {
      LOGGER.debug("Starting configuration {}", this);
      this.setStarting();
      this.pluginManager.collectPlugins();
      PluginManager levelPlugins = new PluginManager("Level");
      levelPlugins.collectPlugins();
      Map<String, PluginType<?>> plugins = levelPlugins.getPlugins();
      if (plugins != null) {
         Iterator i$ = plugins.values().iterator();

         while(i$.hasNext()) {
            PluginType type = (PluginType)i$.next();

            try {
               Loader.initializeClass(type.getPluginClass().getName(), type.getPluginClass().getClassLoader());
            } catch (Exception var6) {
               LOGGER.error("Unable to initialize {} due to {}", type.getPluginClass().getName(), var6.getClass().getSimpleName(), var6);
            }
         }
      }

      this.setup();
      this.setupAdvertisement();
      this.doConfigure();
      Set<LoggerConfig> alreadyStarted = new HashSet();
      Iterator i$ = this.loggers.values().iterator();

      while(i$.hasNext()) {
         LoggerConfig logger = (LoggerConfig)i$.next();
         logger.start();
         alreadyStarted.add(logger);
      }

      i$ = this.appenders.values().iterator();

      while(i$.hasNext()) {
         Appender appender = (Appender)i$.next();
         appender.start();
      }

      if (!alreadyStarted.contains(this.root)) {
         this.root.start();
      }

      super.start();
      LOGGER.debug("Started configuration {} OK.", this);
   }

   public void stop() {
      this.setStopping();
      LOGGER.trace("Stopping {}...", this);
      LoggerContextFactory factory = LogManager.getFactory();
      if (factory instanceof Log4jContextFactory) {
         ContextSelector selector = ((Log4jContextFactory)factory).getSelector();
         if (selector instanceof AsyncLoggerContextSelector) {
         }
      }

      Set<LoggerConfig> alreadyStopped = new HashSet();
      int asyncLoggerConfigCount = 0;
      Iterator i$ = this.loggers.values().iterator();

      while(i$.hasNext()) {
         LoggerConfig logger = (LoggerConfig)i$.next();
         if (logger instanceof AsyncLoggerConfig) {
            logger.stop();
            ++asyncLoggerConfigCount;
            alreadyStopped.add(logger);
         }
      }

      if (this.root instanceof AsyncLoggerConfig) {
         this.root.stop();
         ++asyncLoggerConfigCount;
         alreadyStopped.add(this.root);
      }

      LOGGER.trace("AbstractConfiguration stopped {} AsyncLoggerConfigs.", asyncLoggerConfigCount);
      Appender[] array = (Appender[])this.appenders.values().toArray(new Appender[this.appenders.size()]);
      int asyncAppenderCount = 0;

      int appenderCount;
      for(appenderCount = array.length - 1; appenderCount >= 0; --appenderCount) {
         if (array[appenderCount] instanceof AsyncAppender) {
            array[appenderCount].stop();
            ++asyncAppenderCount;
         }
      }

      LOGGER.trace("AbstractConfiguration stopped {} AsyncAppenders.", asyncAppenderCount);
      appenderCount = 0;

      int loggerCount;
      for(loggerCount = array.length - 1; loggerCount >= 0; --loggerCount) {
         if (array[loggerCount].isStarted()) {
            array[loggerCount].stop();
            ++appenderCount;
         }
      }

      LOGGER.trace("AbstractConfiguration stopped {} Appenders.", appenderCount);
      loggerCount = 0;
      Iterator i$ = this.loggers.values().iterator();

      while(i$.hasNext()) {
         LoggerConfig logger = (LoggerConfig)i$.next();
         logger.clearAppenders();
         if (!alreadyStopped.contains(logger)) {
            logger.stop();
            ++loggerCount;
         }
      }

      LOGGER.trace("AbstractConfiguration stopped {} Loggers.", loggerCount);
      if (!alreadyStopped.contains(this.root)) {
         this.root.stop();
      }

      super.stop();
      if (this.advertiser != null && this.advertisement != null) {
         this.advertiser.unadvertise(this.advertisement);
      }

      LOGGER.debug("Stopped {} OK", this);
   }

   public boolean isShutdownHookEnabled() {
      return this.isShutdownHookEnabled;
   }

   protected void setup() {
   }

   protected Level getDefaultStatus() {
      String statusLevel = PropertiesUtil.getProperties().getStringProperty("Log4jDefaultStatusLevel", Level.ERROR.name());

      try {
         return Level.toLevel(statusLevel);
      } catch (Exception var3) {
         return Level.ERROR;
      }
   }

   protected void createAdvertiser(String advertiserString, ConfigurationSource configSource, byte[] buffer, String contentType) {
      if (advertiserString != null) {
         Node node = new Node((Node)null, advertiserString, (PluginType)null);
         Map<String, String> attributes = node.getAttributes();
         attributes.put("content", new String(buffer));
         attributes.put("contentType", contentType);
         attributes.put("name", "configuration");
         if (configSource.getLocation() != null) {
            attributes.put("location", configSource.getLocation());
         }

         this.advertiserNode = node;
      }

   }

   private void setupAdvertisement() {
      if (this.advertiserNode != null) {
         String name = this.advertiserNode.getName();
         PluginType<Advertiser> type = this.pluginManager.getPluginType(name);
         if (type != null) {
            Class clazz = type.getPluginClass();

            try {
               this.advertiser = (Advertiser)clazz.newInstance();
               this.advertisement = this.advertiser.advertise(this.advertiserNode.getAttributes());
            } catch (InstantiationException var5) {
               LOGGER.error("InstantiationException attempting to instantiate advertiser: {}", name, var5);
            } catch (IllegalAccessException var6) {
               LOGGER.error("IllegalAccessException attempting to instantiate advertiser: {}", name, var6);
            }
         }
      }

   }

   public <T> T getComponent(String name) {
      return this.componentMap.get(name);
   }

   public void addComponent(String name, Object obj) {
      this.componentMap.putIfAbsent(name, obj);
   }

   protected void doConfigure() {
      if (this.rootNode.hasChildren() && ((Node)this.rootNode.getChildren().get(0)).getName().equalsIgnoreCase("Properties")) {
         Node first = (Node)this.rootNode.getChildren().get(0);
         this.createConfiguration(first, (LogEvent)null);
         if (first.getObject() != null) {
            this.subst.setVariableResolver((StrLookup)first.getObject());
         }
      } else {
         Map<String, String> map = (Map)this.componentMap.get("ContextProperties");
         StrLookup lookup = map == null ? null : new MapLookup(map);
         this.subst.setVariableResolver(new Interpolator(lookup));
      }

      boolean setLoggers = false;
      boolean setRoot = false;
      Iterator i$ = this.rootNode.getChildren().iterator();

      while(i$.hasNext()) {
         Node child = (Node)i$.next();
         if (child.getName().equalsIgnoreCase("Properties")) {
            if (this.tempLookup == this.subst.getVariableResolver()) {
               LOGGER.error("Properties declaration must be the first element in the configuration");
            }
         } else {
            this.createConfiguration(child, (LogEvent)null);
            if (child.getObject() != null) {
               if (child.getName().equalsIgnoreCase("Appenders")) {
                  this.appenders = (ConcurrentMap)child.getObject();
               } else if (child.getObject() instanceof Filter) {
                  this.addFilter((Filter)child.getObject());
               } else if (child.getName().equalsIgnoreCase("Loggers")) {
                  Loggers l = (Loggers)child.getObject();
                  this.loggers = l.getMap();
                  setLoggers = true;
                  if (l.getRoot() != null) {
                     this.root = l.getRoot();
                     setRoot = true;
                  }
               } else {
                  LOGGER.error("Unknown object \"{}\" of type {} is ignored.", child.getName(), child.getObject().getClass().getName());
               }
            }
         }
      }

      if (!setLoggers) {
         LOGGER.warn("No Loggers were configured, using default. Is the Loggers element missing?");
         this.setToDefault();
      } else {
         if (!setRoot) {
            LOGGER.warn("No Root logger was configured, creating default ERROR-level Root logger with Console appender");
            this.setToDefault();
         }

         i$ = this.loggers.entrySet().iterator();

         while(i$.hasNext()) {
            Entry<String, LoggerConfig> entry = (Entry)i$.next();
            LoggerConfig l = (LoggerConfig)entry.getValue();
            Iterator i$ = l.getAppenderRefs().iterator();

            while(i$.hasNext()) {
               AppenderRef ref = (AppenderRef)i$.next();
               Appender app = (Appender)this.appenders.get(ref.getRef());
               if (app != null) {
                  l.addAppender(app, ref.getLevel(), ref.getFilter());
               } else {
                  LOGGER.error("Unable to locate appender {} for logger {}", ref.getRef(), l.getName());
               }
            }
         }

         this.setParents();
      }
   }

   private void setToDefault() {
      this.setName("Default");
      Layout<? extends Serializable> layout = PatternLayout.newBuilder().withPattern("%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n").withConfiguration(this).build();
      Appender appender = ConsoleAppender.createAppender(layout, (Filter)null, "SYSTEM_OUT", "Console", "false", "true");
      appender.start();
      this.addAppender(appender);
      LoggerConfig root = this.getRootLogger();
      root.addAppender(appender, (Level)null, (Filter)null);
      String levelName = PropertiesUtil.getProperties().getStringProperty("org.apache.logging.log4j.level");
      Level level = levelName != null && Level.getLevel(levelName) != null ? Level.getLevel(levelName) : Level.ERROR;
      root.setLevel(level);
   }

   public void setName(String name) {
      this.name = name;
   }

   public String getName() {
      return this.name;
   }

   public void addListener(ConfigurationListener listener) {
      this.listeners.add(listener);
   }

   public void removeListener(ConfigurationListener listener) {
      this.listeners.remove(listener);
   }

   public Appender getAppender(String name) {
      return (Appender)this.appenders.get(name);
   }

   public Map<String, Appender> getAppenders() {
      return this.appenders;
   }

   public void addAppender(Appender appender) {
      this.appenders.putIfAbsent(appender.getName(), appender);
   }

   public StrSubstitutor getStrSubstitutor() {
      return this.subst;
   }

   public void setConfigurationMonitor(ConfigurationMonitor monitor) {
      this.monitor = monitor;
   }

   public ConfigurationMonitor getConfigurationMonitor() {
      return this.monitor;
   }

   public void setAdvertiser(Advertiser advertiser) {
      this.advertiser = advertiser;
   }

   public Advertiser getAdvertiser() {
      return this.advertiser;
   }

   public synchronized void addLoggerAppender(Logger logger, Appender appender) {
      String name = logger.getName();
      this.appenders.putIfAbsent(appender.getName(), appender);
      LoggerConfig lc = this.getLoggerConfig(name);
      if (lc.getName().equals(name)) {
         lc.addAppender(appender, (Level)null, (Filter)null);
      } else {
         LoggerConfig nlc = new LoggerConfig(name, lc.getLevel(), lc.isAdditive());
         nlc.addAppender(appender, (Level)null, (Filter)null);
         nlc.setParent(lc);
         this.loggers.putIfAbsent(name, nlc);
         this.setParents();
         logger.getContext().updateLoggers();
      }

   }

   public synchronized void addLoggerFilter(Logger logger, Filter filter) {
      String name = logger.getName();
      LoggerConfig lc = this.getLoggerConfig(name);
      if (lc.getName().equals(name)) {
         lc.addFilter(filter);
      } else {
         LoggerConfig nlc = new LoggerConfig(name, lc.getLevel(), lc.isAdditive());
         nlc.addFilter(filter);
         nlc.setParent(lc);
         this.loggers.putIfAbsent(name, nlc);
         this.setParents();
         logger.getContext().updateLoggers();
      }

   }

   public synchronized void setLoggerAdditive(Logger logger, boolean additive) {
      String name = logger.getName();
      LoggerConfig lc = this.getLoggerConfig(name);
      if (lc.getName().equals(name)) {
         lc.setAdditive(additive);
      } else {
         LoggerConfig nlc = new LoggerConfig(name, lc.getLevel(), additive);
         nlc.setParent(lc);
         this.loggers.putIfAbsent(name, nlc);
         this.setParents();
         logger.getContext().updateLoggers();
      }

   }

   public synchronized void removeAppender(String name) {
      Iterator i$ = this.loggers.values().iterator();

      while(i$.hasNext()) {
         LoggerConfig logger = (LoggerConfig)i$.next();
         logger.removeAppender(name);
      }

      Appender app = (Appender)this.appenders.remove(name);
      if (app != null) {
         app.stop();
      }

   }

   public LoggerConfig getLoggerConfig(String name) {
      if (this.loggers.containsKey(name)) {
         return (LoggerConfig)this.loggers.get(name);
      } else {
         String substr = name;

         do {
            if ((substr = NameUtil.getSubName(substr)) == null) {
               return this.root;
            }
         } while(!this.loggers.containsKey(substr));

         return (LoggerConfig)this.loggers.get(substr);
      }
   }

   public LoggerConfig getRootLogger() {
      return this.root;
   }

   public Map<String, LoggerConfig> getLoggers() {
      return Collections.unmodifiableMap(this.loggers);
   }

   public LoggerConfig getLogger(String name) {
      return (LoggerConfig)this.loggers.get(name);
   }

   public synchronized void addLogger(String name, LoggerConfig loggerConfig) {
      this.loggers.putIfAbsent(name, loggerConfig);
      this.setParents();
   }

   public synchronized void removeLogger(String name) {
      this.loggers.remove(name);
      this.setParents();
   }

   public void createConfiguration(Node node, LogEvent event) {
      PluginType<?> type = node.getType();
      if (type != null && type.isDeferChildren()) {
         node.setObject(this.createPluginObject(type, node, event));
      } else {
         Iterator i$ = node.getChildren().iterator();

         while(i$.hasNext()) {
            Node child = (Node)i$.next();
            this.createConfiguration(child, event);
         }

         if (type == null) {
            if (node.getParent() != null) {
               LOGGER.error("Unable to locate plugin for {}", node.getName());
            }
         } else {
            node.setObject(this.createPluginObject(type, node, event));
         }
      }

   }

   private <T> Object createPluginObject(PluginType<T> type, Node node, LogEvent event) {
      Class<T> clazz = type.getPluginClass();
      if (Map.class.isAssignableFrom(clazz)) {
         try {
            return createPluginMap(node, clazz);
         } catch (Exception var7) {
            LOGGER.warn("Unable to create Map for {} of class {}", type.getElementName(), clazz, var7);
         }
      }

      if (Collection.class.isAssignableFrom(clazz)) {
         try {
            return createPluginCollection(node, clazz);
         } catch (Exception var6) {
            LOGGER.warn("Unable to create List for {} of class {}", type.getElementName(), clazz, var6);
         }
      }

      return (new PluginBuilder(type)).withConfiguration(this).withConfigurationNode(node).forLogEvent(event).build();
   }

   private static <T> Object createPluginMap(Node node, Class<T> clazz) throws InstantiationException, IllegalAccessException {
      Map<String, Object> map = (Map)clazz.newInstance();
      Iterator i$ = node.getChildren().iterator();

      while(i$.hasNext()) {
         Node child = (Node)i$.next();
         map.put(child.getName(), child.getObject());
      }

      return map;
   }

   private static <T> Object createPluginCollection(Node node, Class<T> clazz) throws InstantiationException, IllegalAccessException {
      Collection<Object> list = (Collection)clazz.newInstance();
      Iterator i$ = node.getChildren().iterator();

      while(i$.hasNext()) {
         Node child = (Node)i$.next();
         list.add(child.getObject());
      }

      return list;
   }

   private void setParents() {
      Iterator i$ = this.loggers.entrySet().iterator();

      while(i$.hasNext()) {
         Entry<String, LoggerConfig> entry = (Entry)i$.next();
         LoggerConfig logger = (LoggerConfig)entry.getValue();
         String name = (String)entry.getKey();
         if (!name.isEmpty()) {
            int i = name.lastIndexOf(46);
            if (i > 0) {
               name = name.substring(0, i);
               LoggerConfig parent = this.getLoggerConfig(name);
               if (parent == null) {
                  parent = this.root;
               }

               logger.setParent(parent);
            } else {
               logger.setParent(this.root);
            }
         }
      }

   }

   protected static byte[] toByteArray(InputStream is) throws IOException {
      ByteArrayOutputStream buffer = new ByteArrayOutputStream();
      byte[] data = new byte[16384];

      int nRead;
      while((nRead = is.read(data, 0, data.length)) != -1) {
         buffer.write(data, 0, nRead);
      }

      return buffer.toByteArray();
   }
}
