/**
     * time에 해당하는 TPS테이블의 90%지점의 데이터만 APM_TPS_SUMMARY테이블에 입력한다.<br>
     * <br>
     *
     * @param before5min
     * @author KIMHYERI
     * @since 2015. 10. 7.
     */
    public void summarizeResTime(FrameOneDataset dsAgent, String now) {

      SqlManager sqlManager = SqlManagerFactory.getSqlManager();
      Parameters inParam = ParametersFactory.createParameters(HtmlParameters.class);
      if (dsAgent != null) {
        for (int j = 0; j < dsAgent.getRowCount(); j++) {
          dsAgent.setActiveRow(j);
          inParam.put("AGENT_ID", dsAgent.getColumnAsString(j, "AGENT_ID"));
          // APM_RES_TIMEPS 테이블의 REQ_DTM MAX 값을 조회
          String currentLastInserted =
              (String) sqlManager.queryForObject(inParam, "fiveMinuteSummary.getResTimepsMaxUdt");

          // 조회한 값이 없거나, 현재시간과의 차이가 5분이상이라면 Summary
          if (currentLastInserted == null
              || DateUtil.getDiffMinCount(currentLastInserted, now) >= 5) {

            Date before5min = DateUtil.getOperationTime(now, -5);
            inParam.setVariable("REQ_DTM", now);
            inParam.setVariable("BEFORE", before5min);

            FrameOneDataset dataset =
                sqlManager.queryForFrameOneDataset(
                    inParam, "fiveMinuteSummary.getResSummaryFromRes");
            if (dataset != null) {
              if (dataset.getRowCount() == 0) {
                inParam.setVariable("RES_TIME", 0);
              } else {
                inParam.setVariable("RES_TIME", dataset.get("RES_TIME"));
              }
              sqlManager.insert(inParam, "fiveMinuteSummary.insertResTimepsSummary");
            }
          }
        }
      }
    }
    /**
     * APM_TPS_SUMMARY 테이블에서 최근 데이터를 조회하여 FDGS에 PUT한다.<br>
     * <br>
     *
     * @param dsAgent
     * @param now
     * @author Kim Ji Hye
     * @since 2015. 10. 20.
     */
    public void makeTodayTpsData(FrameOneDataset dsAgent, String now) {

      SqlManager sqlManager = SqlManagerFactory.getSqlManager();
      Parameters inParam = ParametersFactory.createParameters(HtmlParameters.class);

      String today = now.substring(0, 8); // yyyyMMdd
      String tomarrow = DateUtil.formatDateString(DateUtil.getOperationDay(today, 1));
      inParam.put("REQ_DTM_TODAY", today);
      inParam.put("REQ_DTM_TOMARROW", tomarrow);

      if (dsAgent != null) {
        for (int j = 0; j < dsAgent.getRowCount(); j++) {
          dsAgent.setActiveRow(j);

          // APM_TPS_SUMMARY 테이블에서 오늘 데이터 조회
          inParam.put("AGENT_ID", dsAgent.getColumnAsString(j, "AGENT_ID"));
          FrameOneDataset dataset =
              sqlManager.queryForFrameOneDataset(
                  inParam, "fiveMinuteSummary.getTodayRecentTpsSummary");

          List<TPS> tpsList = new ArrayList<TPS>();

          if (dataset != null) {
            for (int i = 0; i < dataset.getRowCount(); i++) {
              dataset.setActiveRow(i);

              // 에이전트 별로 FDGS에 put 할 List를 만든다.
              TPS tps = new TPS();
              tps.setAgentId(dataset.getColumnAsString(i, "AGENT_ID"));
              tps.setReqDtm(dataset.getColumnAsString(i, "REQ_DTM"));
              tps.setTxCnt(dataset.getColumnAsInteger(i, "TX_CNT"));

              tpsList.add(tps);
            }
            // FDGS에 put 한다.
            FDGSUtil.putObject(
                APMConstants.CHART_TPS_TODAY, dsAgent.getColumnAsString(j, "AGENT_ID"), tpsList);

            // 차트 데이터 로더 데몬에게 변경사항 공지
            ChartDataStorageDaemon.noticeChangedState(APMConstants.CHART_TPS_TODAY);
          }
        }
      }
    }
    /**
     * time에 해당하는 RESOURCE테이블의 90%지점의 데이터만 APM_RESOURCE_SUMMARY테이블에 입력한다.<br>
     * 단, GC_OCCR_CNT의 경우 최근 데이터 1개를 입력한다. <br>
     *
     * @param before5min
     * @author KIMHYERI
     * @since 2015. 10. 7.
     */
    public void summarizeResource(FrameOneDataset dsAgent, String now) {
      SqlManager sqlManager = SqlManagerFactory.getSqlManager();
      Parameters inParam = ParametersFactory.createParameters(HtmlParameters.class);
      if (dsAgent != null) {
        for (int j = 0; j < dsAgent.getRowCount(); j++) {
          dsAgent.setActiveRow(j);
          inParam.put("AGENT_ID", dsAgent.getColumnAsString(j, "AGENT_ID"));
          // APM_RESOURCE 테이블의 RESOURCE_CHK_DTM MAX 값을 조회
          String currentLastInserted =
              (String) sqlManager.queryForObject(inParam, "fiveMinuteSummary.getResourceMaxUdt");

          // 조회한 값이 없거나, 현재시간과의 차이가 5분이상이라면 Summary
          if (currentLastInserted == null
              || DateUtil.getDiffMinCount(currentLastInserted, now) >= 5) {

            Date before5min = DateUtil.getOperationTime(now, -5);
            inParam.setVariable("RESOURCE_CHK_DTM", now);
            inParam.setVariable("BEFORE", before5min);

            // 메모리사이즈 조회
            Object selectedValue =
                (Object) sqlManager.queryForObject(inParam, "fiveMinuteSummary.getCurrMemorySize");
            if (selectedValue == null) {
              inParam.put("CURR_MEMORY_SIZE", 0);
            } else {
              inParam.put("CURR_MEMORY_SIZE", selectedValue);
            }

            // 메모리사용률 조회
            selectedValue =
                (Object)
                    sqlManager.queryForObject(inParam, "fiveMinuteSummary.getMemoryUsageRatio");
            if (selectedValue == null) {
              inParam.put("MEMORY_USAGE_RATIO", 0);
            } else {
              inParam.put("MEMORY_USAGE_RATIO", selectedValue);
            }

            // CPU사용률 조회
            selectedValue =
                (Object) sqlManager.queryForObject(inParam, "fiveMinuteSummary.getCpuUsageRatio");
            if (selectedValue == null) {
              inParam.put("CPU_USAGE_RATIO", 0);
            } else {
              inParam.put("CPU_USAGE_RATIO", selectedValue);
            }

            // 디스크 I/O사용률
            selectedValue =
                (Object)
                    sqlManager.queryForObject(inParam, "fiveMinuteSummary.getDiskIoUsageRatio");
            if (selectedValue == null) {
              inParam.put("DISK_IO_USAGE_RATIO", 0);
            } else {
              inParam.put("DISK_IO_USAGE_RATIO", selectedValue);
            }

            // 세션 카운트
            selectedValue =
                (Object) sqlManager.queryForObject(inParam, "fiveMinuteSummary.getSessionCnt");
            if (selectedValue == null) {
              inParam.put("SESSION_CNT", 0);
            } else {
              inParam.put("SESSION_CNT", selectedValue);
            }

            // GC 발생 건수
            selectedValue =
                (Object) sqlManager.queryForObject(inParam, "fiveMinuteSummary.getGcOccrCnt");
            if (selectedValue == null) {
              inParam.put("GC_OCCR_CNT", 0);
            } else {
              inParam.put("GC_OCCR_CNT", selectedValue);
            }

            // GC 소요 시간 차이
            selectedValue =
                (Object) sqlManager.queryForObject(inParam, "fiveMinuteSummary.getGcElapsedGap");
            if (selectedValue == null) {
              inParam.put("GC_ELAPSED_GAP", 0);
            } else {
              inParam.put("GC_ELAPSED_GAP", selectedValue);
            }
            // APM_RESOURCE_SUMMARY 테이블에 값을 insert한다.
            sqlManager.insert(inParam, "fiveMinuteSummary.insertResourceSummary");
          }
        }
      }
    }
    @Override
    public void run() {

      // 현재 Thread에 invoker의 ClassLoader를 셋팅해 주어서, 동일한 ClassLoader에서 로딩한 Config가 동기화의 대상이 되도록 한다.
      Thread.currentThread().setContextClassLoader(classLoader);

      synchronized (FiveMinuteSummaryDaemon.class) {
        if (LOG.isDebugEnabled()) {
          LOG.debug("FiveMinuteSummaryDaemon started...");
        }
        // 현재시간
        now = DateUtil.getDateTime("yyyyMMddHHmmss");
        long nowLong = Long.parseLong(now);

        // 모든 Agent Id 조회
        SqlManager sqlManager = SqlManagerFactory.getSqlManager();
        Parameters inParam = ParametersFactory.createParameters(HtmlParameters.class);
        FrameOneDataset dsAgent =
            sqlManager.queryForFrameOneDataset(inParam, "fiveMinuteSummary.getAgentList");

        try {
          // 현재 시간(요약 시간) > 마지막 요약 시간 -> 실행
          if (nowLong > tpsSummaryLastInserted) {
            summarizeTps(dsAgent, now);
            tpsSummaryLastInserted = nowLong;
          }
        } catch (Exception e) {
          LOG.error("Exception", e);
        }

        try {
          // 현재 시간(요약 시간) > 마지막 요약 시간 -> 실행
          if (nowLong > resSummaryTimeLastInserted) {
            summarizeResTime(dsAgent, now);
            resSummaryTimeLastInserted = nowLong;
          }
        } catch (Exception e) {
          LOG.error("Exception", e);
        }

        try {
          // 현재 시간(요약 시간) > 마지막 요약 시간 -> 실행
          if (nowLong > resourceSummaryTimeLastInserted) {
            summarizeResource(dsAgent, now);
            resourceSummaryTimeLastInserted = nowLong;
          }
        } catch (Exception e) {
          LOG.error("Exception", e);
        }

        try {
          // 현재 시간(요약 시간) > 마지막 요약 시간 -> 실행
          if (nowLong > todayTpsSummaryLastPut) {
            makeTodayTpsData(dsAgent, now);
            todayTpsSummaryLastPut = nowLong;
          }
        } catch (Exception e) {
          LOG.error("Exception", e);
        }
      }
    }
  /**
   * TODO 쓰레드 정보 조회.<br>
   * <br>
   *
   * @param request
   * @param response
   * @param inParams
   * @return
   * @author Park Woon Kyung
   * @since 2015. 10.18
   */
  @RequestMapping(value = "/threadSearch")
  public Parameters threadSearch(
      HttpServletRequest request, HttpServletResponse response, Parameters inParams) {

    Parameters outParam = ParametersFactory.createParameters(inParams);

    // 수집 쓰레드덤프 데이터
    ThreadDumpData threadDumpData = null;
    // 호출시간 날짜형태로 표현
    SimpleDateFormat time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    List<ThreadState> threadStateList = null;

    // FDGS 조회 시작 시간
    String startTime = null;
    // FDGS 조회 순간 시간
    String now = null;

    // 쓰레드 정보 저장 DataSet 컬럼 구성
    FrameOneDataset dsThreadDump = new FrameOneHtmlDataset();
    dsThreadDump.addStringColumn("AGENT_ID");
    dsThreadDump.addStringColumn("CLIENT_IP");
    dsThreadDump.addStringColumn("REQ_TIME");
    dsThreadDump.addIntegerColumn("RES_TIME");
    dsThreadDump.addIntegerColumn("THREAD_ID");
    dsThreadDump.addStringColumn("THREAD_STATE");
    dsThreadDump.addStringColumn("APPLICATION");
    dsThreadDump.addStringColumn("THREAD_STACKTRC");
    dsThreadDump.addStringColumn("SQL");

    // 선택된 Agent 처리(반드시 1개의 Agent만 선택가능)
    String agentId = inParams.getVariableAsString("AGENTLIST");
    if (null != agentId && !"".equals(agentId)) {

      // 쓰레드 덤프를 요청하는 모델 클래스
      ThreadDumpData td = new ThreadDumpData();
      td.setAgentId(agentId);
      td.setTypeCd(APMConstants.THREADDUMP_DATA);
      td.setWeightValue(APMConstants.THREADDUMP_WEIGHT_VALUE);
      td.setThreadDumpReqDtm(DateUtil.getDateTime("yyyyMMddHHmmssSSS"));

      // 선택한 에이전트의 쓰레드 덤프 요청
      sendThreadDumpRequest(td);

      // FDGS 조회 시작 시간
      startTime = DateUtil.getDateTime("yyyyMMddHHmmss");

      // FDGS 캐시에 요청한 키에 해당하는 쓰레드 덤프 데이터가 있는지 확인
      while (threadDumpData == null) {
        // FDGS 조회 시간이 10초이상이면 break
        now = DateUtil.getDateTime("yyyyMMddHHmmss");
        if (DateUtil.getDiffSecCount(startTime, now) >= 10) {
          break;
        }

        threadDumpData =
            FDGSUtil.getObject(
                APMConstants.THREAD_DUMP_DATA,
                td.getThreadDumpReqDtm() + "|" + td.getAgentId(),
                ThreadDumpData.class);

        // 에이전트에서 받은 데이터가 있을 때
        if (threadDumpData != null) {
          threadStateList = threadDumpData.getThreadStateList();
          break;
        }

        try {
          // 0.5초 쉼
          Thread.sleep(500);
        } catch (InterruptedException e) {
          LOG.error("InterruptedException", e);
        }
      }

      if (threadStateList != null) {
        for (ThreadState ts : threadStateList) {
          int row = dsThreadDump.appendRow();
          dsThreadDump.setColumn(row, "AGENT_ID", agentId);
          dsThreadDump.setColumn(row, "CLIENT_IP", ts.getClientIp());
          dsThreadDump.setColumn(
              row,
              "REQ_TIME",
              time.format(
                  DateUtil.parseDate(String.valueOf(ts.getReqStartDtm()).substring(0, 14))));
          dsThreadDump.setColumn(row, "RES_TIME", ts.getElapsedTime());
          dsThreadDump.setColumn(row, "THREAD_ID", ts.getThreadId());
          dsThreadDump.setColumn(row, "THREAD_STATE", ts.getThreadState());
          dsThreadDump.setColumn(row, "APPLICATION", ts.getCallUri());
          dsThreadDump.setColumn(row, "THREAD_STACKTRC", ts.getStackTrc());
          dsThreadDump.setColumn(row, "SQL", ts.getSqlString());
        }
      }
    }

    if (LOG.isDebugEnabled()) {
      LOG.debug("[dsThreadDump]");
      LOG.debug(dsThreadDump);
    }

    outParam.setFrameOneDataset("ds_threadDump", dsThreadDump);
    return outParam;
  }