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

import com.lmax.disruptor.BlockingWaitStrategy;
import com.lmax.disruptor.EventFactory;
import com.lmax.disruptor.EventHandler;
import com.lmax.disruptor.EventTranslatorTwoArg;
import com.lmax.disruptor.ExceptionHandler;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.Sequence;
import com.lmax.disruptor.SequenceReportingEventHandler;
import com.lmax.disruptor.SleepingWaitStrategy;
import com.lmax.disruptor.WaitStrategy;
import com.lmax.disruptor.YieldingWaitStrategy;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.ProducerType;
import com.lmax.disruptor.util.Util;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.jmx.RingBufferAdmin;
import org.apache.logging.log4j.status.StatusLogger;

class AsyncLoggerConfigHelper {
   private static final int MAX_DRAIN_ATTEMPTS_BEFORE_SHUTDOWN = 200;
   private static final int SLEEP_MILLIS_BETWEEN_DRAIN_ATTEMPTS = 50;
   private static final int RINGBUFFER_MIN_SIZE = 128;
   private static final int RINGBUFFER_DEFAULT_SIZE = 262144;
   private static final Logger LOGGER = StatusLogger.getLogger();
   private static ThreadFactory threadFactory = new DaemonThreadFactory("AsyncLoggerConfig-");
   private static volatile Disruptor<AsyncLoggerConfigHelper.Log4jEventWrapper> disruptor;
   private static ExecutorService executor;
   private static volatile int count = 0;
   private static ThreadLocal<Boolean> isAppenderThread = new ThreadLocal();
   private static final EventFactory<AsyncLoggerConfigHelper.Log4jEventWrapper> FACTORY = new EventFactory<AsyncLoggerConfigHelper.Log4jEventWrapper>() {
      public AsyncLoggerConfigHelper.Log4jEventWrapper newInstance() {
         return new AsyncLoggerConfigHelper.Log4jEventWrapper();
      }
   };
   private final EventTranslatorTwoArg<AsyncLoggerConfigHelper.Log4jEventWrapper, LogEvent, AsyncLoggerConfig> translator = new EventTranslatorTwoArg<AsyncLoggerConfigHelper.Log4jEventWrapper, LogEvent, AsyncLoggerConfig>() {
      public void translateTo(AsyncLoggerConfigHelper.Log4jEventWrapper ringBufferElement, long sequence, LogEvent logEvent, AsyncLoggerConfig loggerConfig) {
         ringBufferElement.event = logEvent;
         ringBufferElement.loggerConfig = loggerConfig;
      }
   };
   private final AsyncLoggerConfig asyncLoggerConfig;

   public AsyncLoggerConfigHelper(AsyncLoggerConfig asyncLoggerConfig) {
      this.asyncLoggerConfig = asyncLoggerConfig;
      claim();
   }

   private static synchronized void initDisruptor() {
      if (disruptor != null) {
         LOGGER.trace("AsyncLoggerConfigHelper not starting new disruptor, using existing object. Ref count is {}.", count);
      } else {
         LOGGER.trace("AsyncLoggerConfigHelper creating new disruptor. Ref count is {}.", count);
         int ringBufferSize = calculateRingBufferSize();
         WaitStrategy waitStrategy = createWaitStrategy();
         executor = Executors.newSingleThreadExecutor(threadFactory);
         initThreadLocalForExecutorThread();
         disruptor = new Disruptor(FACTORY, ringBufferSize, executor, ProducerType.MULTI, waitStrategy);
         EventHandler<AsyncLoggerConfigHelper.Log4jEventWrapper>[] handlers = new AsyncLoggerConfigHelper.Log4jEventWrapperHandler[]{new AsyncLoggerConfigHelper.Log4jEventWrapperHandler()};
         ExceptionHandler errorHandler = getExceptionHandler();
         disruptor.handleExceptionsWith(errorHandler);
         disruptor.handleEventsWith(handlers);
         LOGGER.debug("Starting AsyncLoggerConfig disruptor with ringbuffer size={}, waitStrategy={}, exceptionHandler={}...", disruptor.getRingBuffer().getBufferSize(), waitStrategy.getClass().getSimpleName(), errorHandler);
         disruptor.start();
      }
   }

   private static WaitStrategy createWaitStrategy() {
      String strategy = System.getProperty("AsyncLoggerConfig.WaitStrategy");
      LOGGER.debug("property AsyncLoggerConfig.WaitStrategy={}", strategy);
      if ("Sleep".equals(strategy)) {
         return new SleepingWaitStrategy();
      } else if ("Yield".equals(strategy)) {
         return new YieldingWaitStrategy();
      } else if ("Block".equals(strategy)) {
         return new BlockingWaitStrategy();
      } else {
         LOGGER.debug("disruptor event handler uses BlockingWaitStrategy");
         return new BlockingWaitStrategy();
      }
   }

   private static int calculateRingBufferSize() {
      int ringBufferSize = 262144;
      String userPreferredRBSize = System.getProperty("AsyncLoggerConfig.RingBufferSize", String.valueOf(ringBufferSize));

      try {
         int size = Integer.parseInt(userPreferredRBSize);
         if (size < 128) {
            size = 128;
            LOGGER.warn("Invalid RingBufferSize {}, using minimum size {}.", userPreferredRBSize, 128);
         }

         ringBufferSize = size;
      } catch (Exception var3) {
         LOGGER.warn("Invalid RingBufferSize {}, using default size {}.", userPreferredRBSize, ringBufferSize);
      }

      return Util.ceilingNextPowerOfTwo(ringBufferSize);
   }

   private static ExceptionHandler getExceptionHandler() {
      String cls = System.getProperty("AsyncLoggerConfig.ExceptionHandler");
      if (cls == null) {
         return null;
      } else {
         try {
            Class<? extends ExceptionHandler> klass = Class.forName(cls);
            ExceptionHandler result = (ExceptionHandler)klass.newInstance();
            return result;
         } catch (Exception var3) {
            LOGGER.debug((String)("AsyncLoggerConfig.ExceptionHandler not set: error creating " + cls + ": "), (Throwable)var3);
            return null;
         }
      }
   }

   static synchronized void claim() {
      ++count;
      initDisruptor();
   }

   static synchronized void release() {
      if (--count > 0) {
         LOGGER.trace("AsyncLoggerConfigHelper: not shutting down disruptor: ref count is {}.", count);
      } else {
         Disruptor<AsyncLoggerConfigHelper.Log4jEventWrapper> temp = disruptor;
         if (temp == null) {
            LOGGER.trace("AsyncLoggerConfigHelper: disruptor already shut down: ref count is {}. (Resetting to zero.)", count);
            count = 0;
         } else {
            LOGGER.trace("AsyncLoggerConfigHelper: shutting down disruptor: ref count is {}.", count);
            disruptor = null;

            for(int i = 0; hasBacklog(temp) && i < 200; ++i) {
               try {
                  Thread.sleep(50L);
               } catch (InterruptedException var3) {
               }
            }

            temp.shutdown();
            executor.shutdown();
            executor = null;
         }
      }
   }

   private static boolean hasBacklog(Disruptor<?> disruptor) {
      RingBuffer<?> ringBuffer = disruptor.getRingBuffer();
      return !ringBuffer.hasAvailableCapacity(ringBuffer.getBufferSize());
   }

   private static void initThreadLocalForExecutorThread() {
      executor.submit(new Runnable() {
         public void run() {
            AsyncLoggerConfigHelper.isAppenderThread.set(Boolean.TRUE);
         }
      });
   }

   public boolean callAppendersFromAnotherThread(LogEvent event) {
      Disruptor<AsyncLoggerConfigHelper.Log4jEventWrapper> temp = disruptor;
      if (temp == null) {
         LOGGER.fatal("Ignoring log event after log4j was shut down");
         return true;
      } else if (isAppenderThread.get() == Boolean.TRUE && temp.getRingBuffer().remainingCapacity() == 0L) {
         return false;
      } else {
         try {
            LogEvent logEvent = event;
            if (event instanceof RingBufferLogEvent) {
               logEvent = ((RingBufferLogEvent)event).createMemento();
            }

            disruptor.getRingBuffer().publishEvent(this.translator, logEvent, this.asyncLoggerConfig);
         } catch (NullPointerException var4) {
            LOGGER.fatal("Ignoring log event after log4j was shut down.");
         }

         return true;
      }
   }

   public RingBufferAdmin createRingBufferAdmin(String contextName, String loggerConfigName) {
      return RingBufferAdmin.forAsyncLoggerConfig(disruptor.getRingBuffer(), contextName, loggerConfigName);
   }

   private static class Log4jEventWrapperHandler implements SequenceReportingEventHandler<AsyncLoggerConfigHelper.Log4jEventWrapper> {
      private static final int NOTIFY_PROGRESS_THRESHOLD = 50;
      private Sequence sequenceCallback;
      private int counter;

      private Log4jEventWrapperHandler() {
      }

      public void setSequenceCallback(Sequence sequenceCallback) {
         this.sequenceCallback = sequenceCallback;
      }

      public void onEvent(AsyncLoggerConfigHelper.Log4jEventWrapper event, long sequence, boolean endOfBatch) throws Exception {
         event.event.setEndOfBatch(endOfBatch);
         event.loggerConfig.asyncCallAppenders(event.event);
         event.clear();
         if (++this.counter > 50) {
            this.sequenceCallback.set(sequence);
            this.counter = 0;
         }

      }

      // $FF: synthetic method
      Log4jEventWrapperHandler(Object x0) {
         this();
      }
   }

   private static class Log4jEventWrapper {
      private AsyncLoggerConfig loggerConfig;
      private LogEvent event;

      private Log4jEventWrapper() {
      }

      public void clear() {
         this.loggerConfig = null;
         this.event = null;
      }

      // $FF: synthetic method
      Log4jEventWrapper(Object x0) {
         this();
      }
   }
}
