content: package com.sohu.cache.stats.app.impl;
  
  import java.util.List;
  import java.util.Map;
  
  import org.apache.commons.collections.MapUtils;
  import org.apache.commons.lang.StringUtils;
  import org.apache.commons.lang.math.NumberUtils;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import redis.clients.jedis.Jedis;
  import redis.clients.jedis.Protocol;
  
  import com.sohu.cache.constant.ImportAppResult;
  import com.sohu.cache.constant.InstanceStatusEnum;
  import com.sohu.cache.dao.InstanceDao;
  import com.sohu.cache.dao.InstanceStatsDao;
  import com.sohu.cache.entity.AppDesc;
  import com.sohu.cache.entity.InstanceInfo;
  import com.sohu.cache.entity.InstanceStats;
  import com.sohu.cache.entity.MachineInfo;
  import com.sohu.cache.machine.MachineCenter;
  import com.sohu.cache.redis.RedisCenter;
  import com.sohu.cache.stats.app.ImportAppCenter;
  import com.sohu.cache.util.ConstUtils;
  import com.sohu.cache.util.IdempotentConfirmer;
  import com.sohu.cache.web.service.AppService;
  
  /**
   * 导入应用
   * 
   * @author leifu
   * @Date 2016-4-16
   * @Time 下午3:42:49
   */
  public class ImportAppCenterImpl implements ImportAppCenter {
  
      private Logger logger = LoggerFactory.getLogger(ImportAppCenterImpl.class);
  
      private AppService appService;
  
      private RedisCenter redisCenter;
  
      private MachineCenter machineCenter;
  
      private InstanceDao instanceDao;
      
      private InstanceStatsDao instanceStatsDao;
      
      @Override
      public ImportAppResult check(AppDesc appDesc, String appInstanceInfo) {
          // 1.检查是否应用信息为空
          if (appDesc == null) {
              return ImportAppResult.fail("应用信息为空");
          }
          // 2.检查应用名是否重复
          String appName = appDesc.getName();
          AppDesc existAppDesc = appService.getAppByName(appName);
          if (existAppDesc != null) {
              return ImportAppResult.fail(appName + ", 应用名重复");
          }
          // 3.实例信息是否为空
          if (StringUtils.isBlank(appInstanceInfo)) {
              return ImportAppResult.fail("实例详情为空");
          }
  
          String[] appInstanceDetails = appInstanceInfo.split("\n");
  
          // 4.检查实例信息格式是否正确
          for (String appInstance : appInstanceDetails) {
              if (StringUtils.isBlank(appInstance)) {
                  return ImportAppResult.fail("应用实例信息有空行");
              }
              String[] instanceItems = appInstance.split(":");
              if (instanceItems.length != 3) {
                  return ImportAppResult.fail("应用实例信息" + appInstance + "格式错误，必须有2个冒号");
              }
              String ip = instanceItems[0];
              // 4.1.检查ip对应的机器是否存在
              try {
                  MachineInfo machineInfo = machineCenter.getMachineInfoByIp(ip);
                  if (machineInfo == null) {
                      return ImportAppResult.fail(appInstance + "中的ip不存在");
                  } else if (machineInfo.isOffline()) {
                      return ImportAppResult.fail(appInstance + "中的ip已经被删除");
                  }
              } catch (Exception e) {
                  return ImportAppResult.fail(appInstance + "中的ip不存在");
              }
              // 4.2.检查端口是否为整数
              String portStr = instanceItems[1];
              boolean portIsDigit = NumberUtils.isDigits(portStr);
              if (!portIsDigit) {
                  return ImportAppResult.fail(appInstance + "中的port不是整数");
              }
  
              int port = NumberUtils.toInt(portStr);
              // 4.3.检查ip:port是否已经在instance_info表和instance_statistics中
              int count = instanceDao.getCountByIpAndPort(ip, port);
              if (count > 0) {
                  return ImportAppResult.fail(appInstance + "中ip:port已经在instance_info存在");
              }
              InstanceStats instanceStats = instanceStatsDao.getInstanceStatsByHost(ip, port);
              if (instanceStats != null) { 
                  return ImportAppResult.fail(appInstance + "中ip:port已经在instance_statistics存在");
              }
              // 4.4.检查Redis实例是否存活
              String memoryOrMasterName = instanceItems[2];
              int memoryOrMasterNameInt = NumberUtils.toInt(memoryOrMasterName);
              boolean isRun;
              if (memoryOrMasterNameInt > 0) {
              		isRun = redisCenter.isRun(ip, port, appDesc.getPassword());
              } else {
              		isRun = redisCenter.isRun(ip, port);
              }
              if (!isRun) {
                  return ImportAppResult.fail(appInstance + "中的节点不是存活的");
              }
  
              // 4.5.检查内存是否为整数
              boolean isSentinelNode = memoryOrMasterNameInt <= 0;
              if (isSentinelNode) {
                  // 4.5.1 sentinel节点masterName判断
                  if (StringUtils.isEmpty(memoryOrMasterName)) {
                      return ImportAppResult.fail(appInstance + "中的sentinel节点master为空");
                  }
                  // 判断masterName
                  String masterName = getSentinelMasterName(ip, port);
                  if (StringUtils.isEmpty(masterName) || !memoryOrMasterName.equals(masterName)) {
                      return ImportAppResult.fail(ip + ":" + port + ", masterName:" + masterName + "与所填"
                              + memoryOrMasterName + "不一致");
                  }
              } else {
                  // 4.5.2 内存必须是整数
                  boolean maxMemoryIsDigit = NumberUtils.isDigits(memoryOrMasterName);
                  if (!maxMemoryIsDigit) {
                      return ImportAppResult.fail(appInstance + "中的maxmemory不是整数");
                  }
              }
          }
          
          // 5. 节点之间关系是否正确，这个比较麻烦，还是依赖于用户填写的正确性。
  
          return ImportAppResult.success();
      }
  
  
      @Override
      public boolean importAppAndInstance(AppDesc appDesc, String appInstanceInfo) {
          boolean isSuccess = true;
          try {
              // 1.1 保存应用信息
              appService.save(appDesc);
              long appId = appDesc.getAppId();
              // 1.2 更新appKey
              appService.updateAppKey(appId);
  
              int type = appDesc.getType();
              // 2.保存应用和用户的关系
              appService.saveAppToUser(appId, appDesc.getUserId());
              // 3.保存实例信息并开启统计
              String[] appInstanceDetails = appInstanceInfo.split("\n");
              // 4.检查实例信息格式是否正确
              for (String appInstance : appInstanceDetails) {
                  String[] instanceItems = appInstance.split(":");
                  String host = instanceItems[0];
                  int port = NumberUtils.toInt(instanceItems[1]);
  
                  String memoryOrMasterName = instanceItems[2];
                  boolean isSentinelNode = NumberUtils.toInt(memoryOrMasterName) <= 0;
                  if (isSentinelNode) {
                      saveInstance(appId, host, port, 0, ConstUtils.CACHE_REDIS_SENTINEL, memoryOrMasterName);
                  } else {
                      if (ConstUtils.CACHE_REDIS_STANDALONE == type || ConstUtils.CACHE_REDIS_SENTINEL == type) {
                          saveInstance(appId, host, port, NumberUtils.toInt(memoryOrMasterName), ConstUtils.CACHE_REDIS_STANDALONE, "");
                      } else if (ConstUtils.CACHE_TYPE_REDIS_CLUSTER == type) {
                          saveInstance(appId, host, port, NumberUtils.toInt(memoryOrMasterName), ConstUtils.CACHE_TYPE_REDIS_CLUSTER, "");
                      }
                      //deploy quartz
                      redisCenter.deployRedisCollection(appId, host, port);
                      redisCenter.deployRedisSlowLogCollection(appId, host, port);
                  }
              }
  
          } catch (Exception e) {
              logger.error(e.getMessage(), e);
              isSuccess = false;
          }
          return isSuccess;
      }
      
      /**
       * 获取sentinel的masterName
       * @param ip
       * @param port
       * @return
       */
      private String getSentinelMasterName(final String ip, final int port) {
          final StringBuilder masterName = new StringBuilder();
          new IdempotentConfirmer() {
              private int timeOutFactor = 1;
              @Override
              public boolean execute() {
                  Jedis jedis = null;
                  try {
                      // 预留
                      String password = null;
                      jedis = redisCenter.getJedis(ip, port, password);
                      jedis.getClient().setConnectionTimeout(Protocol.DEFAULT_TIMEOUT * (timeOutFactor++));
                      jedis.getClient().setSoTimeout(Protocol.DEFAULT_TIMEOUT * (timeOutFactor++));
                      List<Map<String, String>> mapList = jedis.sentinelMasters();
                      String targetKey = "name";
                      for (Map<String, String> map : mapList) {
                          if (map.containsKey(targetKey)) {
                              masterName.append(MapUtils.getString(map, targetKey, ""));
                          }
                      }
                      return true;
                  } catch (Exception e) {
                      logger.warn("{}:{} error message is {} ", ip, port, e.getMessage());
                      return false;
                  } finally {
                      if (jedis != null) {
                          jedis.close();
                      }
                  }
              }
          }.run();
          return masterName.toString();
      }
  
      /**
       * 保存实例信息
       * @param appId
       * @param host
       * @param port
       * @param maxMemory
       * @param type
       * @param cmd
       * @return
       */
      private InstanceInfo saveInstance(long appId, String host, int port, int maxMemory, int type,
              String cmd) {
          InstanceInfo instanceInfo = new InstanceInfo();
          instanceInfo.setAppId(appId);
          MachineInfo machineInfo = machineCenter.getMachineInfoByIp(host);
          instanceInfo.setHostId(machineInfo.getId());
          instanceInfo.setConn(0);
          instanceInfo.setMem(maxMemory);
          instanceInfo.setStatus(InstanceStatusEnum.GOOD_STATUS.getStatus());
          instanceInfo.setPort(port);
          instanceInfo.setType(type);
          instanceInfo.setCmd(cmd);
          instanceInfo.setIp(host);
          instanceDao.saveInstance(instanceInfo);
          return instanceInfo;
      }
  
      public void setAppService(AppService appService) {
          this.appService = appService;
      }
  
      public void setRedisCenter(RedisCenter redisCenter) {
          this.redisCenter = redisCenter;
      }
  
      public void setMachineCenter(MachineCenter machineCenter) {
          this.machineCenter = machineCenter;
      }
  
      public void setInstanceDao(InstanceDao instanceDao) {
          this.instanceDao = instanceDao;
      }
  
  
      public void setInstanceStatsDao(InstanceStatsDao instanceStatsDao) {
          this.instanceStatsDao = instanceStatsDao;
      }
  
  }
  