package com.netflix.spinnaker.fiat.config;

import com.netflix.spinnaker.kork.jedis.JedisClientDelegate;
import com.netflix.spinnaker.kork.jedis.RedisClientDelegate;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.*;

import java.lang.reflect.Field;
import java.net.URI;

@Slf4j
@Configuration
@ConditionalOnProperty("redis.connection")
public class RedisConfig {
  @Bean
  @ConfigurationProperties("redis")
  public GenericObjectPoolConfig redisPoolConfig() {
    GenericObjectPoolConfig config = new JedisPoolConfig();
    config.setMaxTotal(20);
    config.setMaxIdle(20);
    config.setMinIdle(5);
    return config;
  }

  @Bean
  public JedisPool jedisPool(@Value("${redis.connection:redis://localhost:6379}") String connection,
                             @Value("${redis.timeout:2000}") int timeout,
                             GenericObjectPoolConfig redisPoolConfig) {
    return createPool(redisPoolConfig, connection, timeout);
  }

  @Bean
  RedisClientDelegate redisClientDelegate(JedisPool jedisPool) {
    return new JedisClientDelegate(jedisPool);
  }

  private static JedisPool createPool(GenericObjectPoolConfig redisPoolConfig,
                                      String connection,
                                      int timeout) {
    URI redisConnection = URI.create(connection);

    String host = redisConnection.getHost();
    int port = redisConnection.getPort() == -1 ? Protocol.DEFAULT_PORT : redisConnection.getPort();

    String path = redisConnection.getPath();
    if (StringUtils.isEmpty(path)) {
      path = "/" + String.valueOf(Protocol.DEFAULT_DATABASE);
    }
    int database = Integer.parseInt(path.split("/", 2)[1]);

    String password = null;
    if (redisConnection.getUserInfo() != null) {
      password = redisConnection.getUserInfo().split(":", 2)[1];
    }

    if (redisPoolConfig == null) {
      redisPoolConfig = new GenericObjectPoolConfig();
    }

    return new JedisPool(redisPoolConfig, host, port, timeout, password, database, null);
  }

  @Bean
  public HealthIndicator redisHealth(final JedisPool jedisPool) {
    return new HealthIndicator() {
      @Override
      public Health health() {
        Health.Builder health;
        try (Jedis jedis = jedisPool.getResource()) {
          if ("PONG".equals(jedis.ping())) {
            health = Health.up();
          } else {
            health = Health.down();
          }
        } catch (Exception ex) {
          health = Health.down(ex);
        }

        try {
          Field f = FieldUtils.getField(JedisPool.class, "internalPool", true /*forceAccess*/);
          Object o = FieldUtils.readField(f, jedisPool, true /*forceAccess*/);
          if (o instanceof GenericObjectPool) {
            GenericObjectPool internal = (GenericObjectPool) o;
            health.withDetail("maxIdle", internal.getMaxIdle());
            health.withDetail("minIdle", internal.getMinIdle());
            health.withDetail("numActive", internal.getNumActive());
            health.withDetail("numIdle", internal.getNumIdle());
            health.withDetail("numWaiters", internal.getNumWaiters());
          }
        } catch (IllegalAccessException iae) {
          log.debug("Can't access jedis' internal pool", iae);
        }

        return health.build();
      }
    };
  }
}

@@ -2679,1294 +2679,7 @@
 %0A  %7D
-%0A%0A  @Bean%0A  public HealthIndicator redisHealth(final JedisPool jedisPool) %7B%0A    return new HealthIndicator() %7B%0A      @Override%0A      public Health health() %7B%0A        Health.Builder health;%0A        try (Jedis jedis = jedisPool.getResource()) %7B%0A          if (%22PONG%22.equals(jedis.ping())) %7B%0A            health = Health.up();%0A          %7D else %7B%0A            health = Health.down();%0A          %7D%0A        %7D catch (Exception ex) %7B%0A          health = Health.down(ex);%0A        %7D%0A%0A        try %7B%0A          Field f = FieldUtils.getField(JedisPool.class, %22internalPool%22, true /*forceAccess*/);%0A          Object o = FieldUtils.readField(f, jedisPool, true /*forceAccess*/);%0A          if (o instanceof GenericObjectPool) %7B%0A            GenericObjectPool internal = (GenericObjectPool) o;%0A            health.withDetail(%22maxIdle%22, internal.getMaxIdle());%0A            health.withDetail(%22minIdle%22, internal.getMinIdle());%0A            health.withDetail(%22numActive%22, internal.getNumActive());%0A            health.withDetail(%22numIdle%22, internal.getNumIdle());%0A            health.withDetail(%22numWaiters%22, internal.getNumWaiters());%0A          %7D%0A        %7D catch (IllegalAccessException iae) %7B%0A          log.debug(%22Can't access jedis' internal pool%22, iae);%0A        %7D%0A%0A        return health.build();%0A      %7D%0A    %7D;%0A  %7D
 %0A%7D%0A

remove potentially expensive Redis health indicator (#363)