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

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Serializable;
import java.net.ConnectException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.appender.AppenderLoggingException;
import org.apache.logging.log4j.core.appender.ManagerFactory;
import org.apache.logging.log4j.core.appender.OutputStreamManager;
import org.apache.logging.log4j.util.Strings;

public class TcpSocketManager extends AbstractSocketManager {
   public static final int DEFAULT_RECONNECTION_DELAY = 30000;
   private static final int DEFAULT_PORT = 4560;
   private static final TcpSocketManager.TcpSocketManagerFactory FACTORY = new TcpSocketManager.TcpSocketManagerFactory();
   private final int reconnectionDelay;
   private TcpSocketManager.Reconnector connector = null;
   private Socket socket;
   private final boolean retry;
   private final boolean immediateFail;

   public TcpSocketManager(String name, OutputStream os, Socket sock, InetAddress inetAddress, String host, int port, int delay, boolean immediateFail, Layout<? extends Serializable> layout) {
      super(name, os, inetAddress, host, port, layout);
      this.reconnectionDelay = delay;
      this.socket = sock;
      this.immediateFail = immediateFail;
      this.retry = delay > 0;
      if (sock == null) {
         this.connector = new TcpSocketManager.Reconnector(this);
         this.connector.setDaemon(true);
         this.connector.setPriority(1);
         this.connector.start();
      }

   }

   public static TcpSocketManager getSocketManager(String host, int port, int delay, boolean immediateFail, Layout<? extends Serializable> layout) {
      if (Strings.isEmpty(host)) {
         throw new IllegalArgumentException("A host name is required");
      } else {
         if (port <= 0) {
            port = 4560;
         }

         if (delay == 0) {
            delay = 30000;
         }

         return (TcpSocketManager)getManager("TCP:" + host + ':' + port, new TcpSocketManager.FactoryData(host, port, delay, immediateFail, layout), FACTORY);
      }
   }

   protected void write(byte[] bytes, int offset, int length) {
      if (this.socket == null) {
         if (this.connector != null && !this.immediateFail) {
            this.connector.latch();
         }

         if (this.socket == null) {
            String msg = "Error writing to " + this.getName() + " socket not available";
            throw new AppenderLoggingException(msg);
         }
      }

      synchronized(this) {
         try {
            this.getOutputStream().write(bytes, offset, length);
         } catch (IOException var8) {
            if (this.retry && this.connector == null) {
               this.connector = new TcpSocketManager.Reconnector(this);
               this.connector.setDaemon(true);
               this.connector.setPriority(1);
               this.connector.start();
            }

            String msg = "Error writing to " + this.getName();
            throw new AppenderLoggingException(msg, var8);
         }

      }
   }

   protected synchronized void close() {
      super.close();
      if (this.connector != null) {
         this.connector.shutdown();
         this.connector.interrupt();
         this.connector = null;
      }

   }

   public Map<String, String> getContentFormat() {
      Map<String, String> result = new HashMap(super.getContentFormat());
      result.put("protocol", "tcp");
      result.put("direction", "out");
      return result;
   }

   protected Socket createSocket(InetAddress host, int port) throws IOException {
      return this.createSocket(host.getHostName(), port);
   }

   protected Socket createSocket(String host, int port) throws IOException {
      return new Socket(host, port);
   }

   protected static class TcpSocketManagerFactory implements ManagerFactory<TcpSocketManager, TcpSocketManager.FactoryData> {
      public TcpSocketManager createManager(String name, TcpSocketManager.FactoryData data) {
         InetAddress inetAddress;
         try {
            inetAddress = InetAddress.getByName(data.host);
         } catch (UnknownHostException var6) {
            TcpSocketManager.LOGGER.error((String)("Could not find address of " + data.host), (Throwable)var6);
            return null;
         }

         try {
            Socket socket = new Socket(data.host, data.port);
            OutputStream os = socket.getOutputStream();
            return new TcpSocketManager(name, os, socket, inetAddress, data.host, data.port, data.delay, data.immediateFail, data.layout);
         } catch (IOException var7) {
            TcpSocketManager.LOGGER.error("TcpSocketManager (" + name + ") " + var7);
            OutputStream os = new ByteArrayOutputStream();
            return data.delay == 0 ? null : new TcpSocketManager(name, os, (Socket)null, inetAddress, data.host, data.port, data.delay, data.immediateFail, data.layout);
         }
      }
   }

   private static class FactoryData {
      private final String host;
      private final int port;
      private final int delay;
      private final boolean immediateFail;
      private final Layout<? extends Serializable> layout;

      public FactoryData(String host, int port, int delay, boolean immediateFail, Layout<? extends Serializable> layout) {
         this.host = host;
         this.port = port;
         this.delay = delay;
         this.immediateFail = immediateFail;
         this.layout = layout;
      }
   }

   private class Reconnector extends Thread {
      private final CountDownLatch latch = new CountDownLatch(1);
      private boolean shutdown = false;
      private final Object owner;

      public Reconnector(OutputStreamManager owner) {
         this.owner = owner;
      }

      public void latch() {
         try {
            this.latch.await();
         } catch (InterruptedException var2) {
         }

      }

      public void shutdown() {
         this.shutdown = true;
      }

      public void run() {
         while(!this.shutdown) {
            try {
               sleep((long)TcpSocketManager.this.reconnectionDelay);
               Socket sock = TcpSocketManager.this.createSocket(TcpSocketManager.this.inetAddress, TcpSocketManager.this.port);
               OutputStream newOS = sock.getOutputStream();
               synchronized(this.owner) {
                  try {
                     TcpSocketManager.this.getOutputStream().close();
                  } catch (IOException var13) {
                  }

                  TcpSocketManager.this.setOutputStream(newOS);
                  TcpSocketManager.this.socket = sock;
                  TcpSocketManager.this.connector = null;
                  this.shutdown = true;
               }

               TcpSocketManager.LOGGER.debug("Connection to " + TcpSocketManager.this.host + ':' + TcpSocketManager.this.port + " reestablished.");
            } catch (InterruptedException var15) {
               TcpSocketManager.LOGGER.debug("Reconnection interrupted.");
            } catch (ConnectException var16) {
               TcpSocketManager.LOGGER.debug(TcpSocketManager.this.host + ':' + TcpSocketManager.this.port + " refused connection");
            } catch (IOException var17) {
               TcpSocketManager.LOGGER.debug("Unable to reconnect to " + TcpSocketManager.this.host + ':' + TcpSocketManager.this.port);
            } finally {
               this.latch.countDown();
            }
         }

      }
   }
}
