content: package com.actiontech.dble.net;
  
  import com.actiontech.dble.config.ProblemReporter;
  import com.actiontech.dble.config.model.SystemConfig;
  import com.actiontech.dble.config.util.StartProblemReporter;
  import com.actiontech.dble.util.CompareUtil;
  import com.actiontech.dble.util.StringUtil;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import java.io.IOException;
  import java.net.SocketException;
  import java.net.SocketOption;
  import java.nio.channels.NetworkChannel;
  import java.nio.channels.SocketChannel;
  import java.util.Objects;
  import java.util.Set;
  
  public final class DbleSocketOptions {
      private static final Logger LOGGER = LoggerFactory.getLogger("DbleSocketOptions");
  
      private final ProblemReporter problemReporter = StartProblemReporter.getInstance();
      private static final String WARNING_FORMAT = "Property [ %s ] '%s' in bootstrap.cnf is illegal, you may need use the default value %s replaced";
      private static final DbleSocketOptions INSTANCE = new DbleSocketOptions();
  
  
      private static final boolean KEEP_ALIVE_OPT_SUPPORTED;
  
      // https://bugs.openjdk.org/browse/JDK-8194298
      public static final String ORACLE_VERSION = "1.8.0_261";
      public static final String OPEN_VERSION = "1.8.0_272";
      public static final String TCP_KEEP_IDLE = "TCP_KEEPIDLE";
      public static final String TCP_KEEP_INTERVAL = "TCP_KEEPINTERVAL";
      public static final String TCP_KEEP_COUNT = "TCP_KEEPCOUNT";
      private static NetworkChannel networkChannel = null;
  
      private int tcpKeepIdle = 30;
      private int tcpKeepInterval = 10;
      private int tcpKeepCount = 3;
  
  
      private DbleSocketOptions() {
      }
  
      static {
          KEEP_ALIVE_OPT_SUPPORTED = keepAliveOptSupported();
      }
  
      public void check() throws IOException {
          SystemConfig instance = SystemConfig.getInstance();
          if (!checkHelp(DbleSocketOptions.TCP_KEEP_INTERVAL, instance.getTcpKeepInterval())) {
              problemReporter.warn(String.format(WARNING_FORMAT, "tcpKeepInterval", instance.getTcpKeepInterval(), getTcpKeepInterval()));
          }
          if (!checkHelp(DbleSocketOptions.TCP_KEEP_IDLE, instance.getTcpKeepIdle())) {
              problemReporter.warn(String.format(WARNING_FORMAT, "tcpKeepIdle", instance.getTcpKeepIdle(), getTcpKeepIdle()));
          }
          if (!checkHelp(DbleSocketOptions.TCP_KEEP_COUNT, instance.getTcpKeepCount())) {
              problemReporter.warn(String.format(WARNING_FORMAT, "tcpKeepCount", instance.getTcpKeepCount(), getTcpKeepCount()));
          }
      }
  
      /**
       * did not added the note for upper-bound because values are
       * also OS specific.
       * <p>
       * since https://mail.openjdk.org/pipermail/net-dev/2018-May/011430.html
       *
       * @param socketName
       * @param value
       * @throws IOException
       */
      private boolean checkHelp(String socketName, int value) throws IOException {
          if (KEEP_ALIVE_OPT_SUPPORTED) {
              if (Objects.isNull(networkChannel)) {
                  networkChannel = SocketChannel.open();
              }
              try {
                  Set<SocketOption<?>> socketOptions = networkChannel.supportedOptions();
                  SocketOption<Integer> socket;
                  socket = (SocketOption<Integer>) socketOptions.stream().filter(socketOption -> StringUtil.equals(socketName, socketOption.name())).findFirst().get();
                  networkChannel.setOption(socket, value);
                  return true;
              } catch (SocketException e) {
                  LOGGER.warn(e.toString(), e);
                  return false;
              }
          }
          return true;
      }
  
  
      /**
       * In general, connection establishment should be controlled by dble default tcp parameters
       *
       * @param channel
       * @throws IOException
       * @since https://bugs.openjdk.org/browse/JDK-8194298
       */
      public void setKeepAliveOptions(NetworkChannel channel) throws IOException {
          if (KEEP_ALIVE_OPT_SUPPORTED) {
              SystemConfig instance = SystemConfig.getInstance();
              int curTcpKeepIdle = instance.getTcpKeepIdle();
              int curTcpKeepInterval = instance.getTcpKeepInterval();
              int curTcpKeepCount = instance.getTcpKeepCount();
              Set<SocketOption<?>> socketOptions = channel.supportedOptions();
              //Compile compatibility
              SocketOption<Integer> socket;
  
              for (SocketOption<?> socketOption : socketOptions) {
                  switch (socketOption.name()) {
                      case TCP_KEEP_IDLE:
                          socket = (SocketOption<Integer>) socketOption;
                          channel.setOption(socket, curTcpKeepIdle);
                          break;
                      case TCP_KEEP_INTERVAL:
                          socket = (SocketOption<Integer>) socketOption;
                          channel.setOption(socket, curTcpKeepInterval);
                          break;
                      case TCP_KEEP_COUNT:
                          socket = (SocketOption<Integer>) socketOption;
                          channel.setOption(socket, curTcpKeepCount);
                          break;
                      default:
                          break;
                  }
              }
          }
      }
  
      private static boolean keepAliveOptSupported() {
          String property = System.getProperty("java.vendor");
          String version = System.getProperty("java.version");
          if (osName().contains("Windows")) {
              return false;
          }
          if (property.toLowerCase().contains("oracle")) {
              return CompareUtil.versionCompare(ORACLE_VERSION, version, "\\.") <= 0;
          } else {
              return CompareUtil.versionCompare(OPEN_VERSION, version, "\\.") <= 0;
          }
      }
  
      public void clean() {
          if (Objects.nonNull(networkChannel)) {
              try {
                  networkChannel.close();
              } catch (IOException e) {
                  LOGGER.warn("close channel error {}", e);
              }
          }
      }
  
      public static String osName() {
          return System.getProperty("os.name");
      }
  
      public static boolean isKeepAliveOPTSupported() {
          return KEEP_ALIVE_OPT_SUPPORTED;
      }
  
      public int getTcpKeepIdle() {
          return tcpKeepIdle;
      }
  
      public int getTcpKeepInterval() {
          return tcpKeepInterval;
      }
  
      public int getTcpKeepCount() {
          return tcpKeepCount;
      }
  
      public static DbleSocketOptions getInstance() {
          return INSTANCE;
      }
  }
  
  