/**
 * 通信部分のサーバ側の実装。
 *
 * @author eriguchi
 */
public class CommunicationServerImpl implements Runnable, CommunicationServer, TelegramConstants {
  /** ロガークラス */
  private static final ENdoSnipeLogger LOGGER =
      ENdoSnipeLogger.getLogger(
          CommunicationServerImpl.class, ENdoSnipeCommunicatorPluginProvider.INSTANCE);

  private static final int MAX_SOCKET = 1000;

  /** ポート番号の最大値 */
  private static final int MAX_PORT = 65535;

  /** サーバソケット */
  ServerSocket objServerSocket_ = null;

  /** クライアントのリスト */
  protected List<JavelinClientThread> clientList_ = new ArrayList<JavelinClientThread>();

  /** スレッド処理中かどうかを表すフラグ */
  boolean isRunning_ = false;

  /** 通信中かを表すフラグ */
  private boolean isListening_ = false;

  /** Javelinと通信を行うポート */
  private int port_;

  /** Javelinと通信を行う初期ポート番号 */
  private int startPort_;

  /** 通信用スレッド名 */
  private String acceptThreadName_ = "JavelinAcceptThread";

  /** 通信用スレッド */
  private Thread acceptThread_;

  /** 通信に使用するポートを範囲指定するか、のフラグ */
  private boolean isRange_ = false;

  /** 通信に使用するポートを範囲指定する際の最大値 */
  private int rangeMax_;

  private long waitForThreadStart_;

  private int bindInterval_;

  private boolean discard_;

  private String[] listeners_;

  /** CommunicationServerの状態変化を通知するリスナのリスト */
  private final List<CommunicatorListener> listenerList_;

  /** Javelinかどうか */
  protected boolean isJavelin_ = false;

  /** @return discard */
  public boolean isDiscard() {
    return this.discard_;
  }

  /** @return listeners */
  public String[] getListeners() {
    return this.listeners_;
  }

  /**
   * サーバインスタンスを生成します。
   *
   * @param isRange 接続ポートに範囲指定を利用する場合は <code>true</code>
   * @param rangeMax 接続ポートに範囲指定を利用する場合の範囲の最大値
   * @param waitForThreadStart スレッド開始までの待ち時間(ミリ秒)
   * @param bindInterval ポートオープンの試行間隔(秒)
   * @param listeners 利用するTelegramListener名
   */
  public CommunicationServerImpl(
      boolean isRange,
      int rangeMax,
      long waitForThreadStart,
      int bindInterval,
      String[] listeners) {
    this.isRange_ = isRange;
    this.rangeMax_ = rangeMax;
    this.waitForThreadStart_ = waitForThreadStart;
    this.bindInterval_ = bindInterval;
    this.listeners_ = listeners;
    this.listenerList_ = new ArrayList<CommunicatorListener>();
  }

  /**
   * サーバインスタンスを生成します。
   *
   * @param isRange 接続ポートに範囲指定を利用する場合は <code>true</code>
   * @param rangeMax 接続ポートに範囲指定を利用する場合の範囲の最大値
   * @param waitForThreadStart スレッド開始までの待ち時間(ミリ秒)
   * @param bindInterval ポートオープンの試行間隔(秒)
   * @param listeners 利用するTelegramListener名
   * @param threadName 通信用スレッド名
   */
  public CommunicationServerImpl(
      boolean isRange,
      int rangeMax,
      long waitForThreadStart,
      int bindInterval,
      String[] listeners,
      String threadName) {
    this(isRange, rangeMax, waitForThreadStart, bindInterval, listeners);
    this.acceptThreadName_ = threadName;
  }

  /** {@inheritDoc} */
  public int getActiveClient() {
    return this.clientList_.size();
  }

  /** {@inheritDoc} */
  public void init() {
    // 何もしない。
  }

  /** {@inheritDoc} */
  @SuppressWarnings("deprecation")
  public void start(int port) {
    if (this.objServerSocket_ != null) {
      return;
    }

    this.startPort_ = port;
    this.port_ = this.startPort_;

    if (this.isRange_ == true) {
      String key = "";
      String message = "";
      if (isPortNumValid(this.port_, this.rangeMax_) == true) {
        key = "javelin.communicate.JavelinAcceptThread.initRange";
        message = CommunicatorMessages.getMessage(key, this.startPort_, this.rangeMax_);
        LOGGER.info(message);
      } else {
        key = "javelin.communicate.JavelinAcceptThread.rangeError";
        message = CommunicatorMessages.getMessage(key, this.startPort_);
        LOGGER.warn(message);
        this.isRange_ = false;
      }
    }

    // クライアント接続の受付を開始する。
    try {
      this.acceptThread_ = new Thread(this, acceptThreadName_);
      this.acceptThread_.setDaemon(true);
      this.acceptThread_.start();
    } catch (Exception objException) {
      LOGGER.warn(objException);
    }
  }

  /** {@inheritDoc} */
  @SuppressWarnings("deprecation")
  public void stop() {
    this.isRunning_ = false;

    if (this.isListening_ == false) {
      try {
        //  通信用ポートBind待ち状態のために、割り込みを行う
        Thread acceptThread = this.acceptThread_;
        if (acceptThread != null) {
          acceptThread.interrupt();
        }
      } catch (Exception ex) {
        LOGGER.warn(ex);
      }
    }

    if (this.isListening_) {
      // 待ち受けソケットを閉じることにより、accept()でSocketExceptionが
      // 発生し、待ち受けスレッドが停止する。
      if (this.objServerSocket_ != null) {
        try {
          this.objServerSocket_.close();
        } catch (Exception ex) {
          LOGGER.warn(ex);
        }
      }

      this.isListening_ = false;
    }
  }

  /** {@inheritDoc} */
  public boolean isConnected() {
    return false;
  }

  /**
   * クライアントにTelegramを送信する。
   *
   * @param telegram 送信する電文。
   */
  public void sendTelegram(Telegram telegram) {

    if (telegram == null) {
      return;
    }

    boolean isSweep = false;

    List<byte[]> byteList = TelegramUtil.createTelegram(telegram);
    for (byte[] bytes : byteList) {
      List<JavelinClientThread> clientList = this.clientList_;
      int size = clientList.size();
      for (int index = size - 1; index >= 0; index--) {
        JavelinClientThread client = null;
        synchronized (clientList) {
          if (index < clientList.size()) {
            client = clientList.get(index);
          } else {
            continue;
          }
        }

        if (client.isClosed()) {
          isSweep = true;
          continue;
        }

        client.sendAlarm(bytes);
        if (LOGGER.isDebugEnabled()) {
          client.logTelegram(telegram, bytes);
        }
      }
    }

    if (isSweep == true) {
      sweepClient();
    }
  }

  /** {@inheritDoc} */
  public void addTelegramListener(TelegramListener listener) {
    // Do nothing.
  }

  /** 通信用スレッドを実行する。 */
  @SuppressWarnings("deprecation")
  public void run() {
    try {
      Thread.sleep(this.waitForThreadStart_);
    } catch (Exception ex) {
      // Do nothing.
    }

    ThreadGroup group = new ThreadGroup("JavelinThreadGroup");
    String key = "";
    String message = "";

    this.isRunning_ = true;

    while (this.isRunning_ == true && this.isListening_ == false) {
      try {
        this.objServerSocket_ = new ServerSocket(this.port_);

        key = "javelin.communicate.JavelinAcceptThread.start";
        message = CommunicatorMessages.getMessage(key, this.port_);
        LOGGER.info(message);
        this.isListening_ = true;
      } catch (IOException objIOException) {
        int interval = this.bindInterval_;
        key = "javelin.communicate.JavelinAcceptThread.restart";
        message = CommunicatorMessages.getMessage(key, this.port_, interval);
        LOGGER.warn(message);
        if (this.isRange_ == true) {
          // ポート番号を1増やして再接続を行う。
          // 接続範囲を超えた場合には、javelin.bind.intervalの間スリープした後、処理を再度実行する。
          this.port_++;
          if (this.port_ > this.rangeMax_) {
            key = "javelin.communicate.JavelinAcceptThread.overRange";
            message = CommunicatorMessages.getMessage(key, this.rangeMax_, this.startPort_);
            LOGGER.info(message);
            this.port_ = this.startPort_;
          }
        }
        sleep();
      }
    }

    while (this.isRunning_) {
      try {
        try {
          accept(group);
        } catch (RuntimeException re) {
          key = "javelin.communicate.snmp.TrapListener.SendingTelegramErrorMessage";
          message = CommunicatorMessages.getMessage(key);
          LOGGER.warn(message, re);
        }
      } catch (Throwable th) {
        LOGGER.warn(th);
      }
    }

    synchronized (this.clientList_) {
      for (int index = this.clientList_.size() - 1; index >= 0; index--) {
        JavelinClientThread client = this.clientList_.get(index);
        client.stop();
      }
    }

    try {
      if (this.objServerSocket_ != null && this.isConnected()) {
        this.objServerSocket_.close();
      }
    } catch (IOException ioe) {
      key = "javelin.communicate.commonMessage.serverSocketCloseError";
      message = CommunicatorMessages.getMessage(key);
      LOGGER.warn(message, ioe);
    }
  }

  @SuppressWarnings("deprecation")
  private void accept(final ThreadGroup group) throws SocketException {
    Socket clientSocket = null;

    String key = "";
    String message = "";
    try {
      // モニター
      clientSocket = this.objServerSocket_.accept();
    } catch (SocketException se) {
      // stop()でソケットを閉じた場合にSocketExceptionが発生する。
      throw se;
    } catch (IOException ioe) {
      key = "javelin.communicate.commonMessage.serverSocketAcceptError";
      message = CommunicatorMessages.getMessage(key);
      LOGGER.warn(message, ioe);
      return;
    }

    int clientCount = sweepClient();
    if (clientCount > MAX_SOCKET) {
      LOGGER.info("接続数が最大数[" + MAX_SOCKET + "]を超えたため、接続を拒否します。");
      try {
        clientSocket.close();
      } catch (IOException ioe) {
        key = "javelin.communicate.commonMessage.clientSocketCloseError";
        message = CommunicatorMessages.getMessage(key);
        LOGGER.warn(message, ioe);
      }
      return;
    }

    InetAddress clientIP = clientSocket.getInetAddress();
    key = "javelin.communicate.commonMessage.clientConnect";
    message = CommunicatorMessages.getMessage(key, clientIP);
    LOGGER.info(message);

    // クライアントからの要求受付用に、処理スレッドを起動する。
    JavelinClientThread clientRunnable;
    try {
      clientRunnable = createJavelinClientThread(clientSocket);
      Thread objHandleThread =
          new Thread(
              group, clientRunnable, acceptThreadName_ + "-JavelinClientThread-" + clientCount);
      objHandleThread.setDaemon(true);
      objHandleThread.start();

      // 通知のためのクライアントリストに追加する。
      synchronized (this.clientList_) {
        this.clientList_.add(clientRunnable);
      }
    } catch (IOException ioe) {
      LOGGER.warn("クライアント通信スレッドの生成に失敗しました。", ioe);
    }

    // 接続完了をリスナに通知
    String hostName = clientIP.getHostName();
    String ip = clientIP.getHostAddress();
    int port = clientSocket.getPort();
    notifyClientConnected(hostName, ip, port);
  }

  /**
   * JavelinClientコネクションオブジェクトを生成します。
   *
   * @param clientSocket ソケット
   * @return JavelinClientコネクションオブジェクト
   * @throws IOException 入出力例外が発生した場合
   */
  protected JavelinClientThread createJavelinClientThread(final Socket clientSocket)
      throws IOException {
    JavelinClientThreadListener listener =
        new JavelinClientThreadListener() {
          public void disconnected(boolean forceDisconnected) {
            notifyClientDisconnected(forceDisconnected);
          }
        };
    return new JavelinClientThread(clientSocket, this.discard_, this.listeners_, listener);
  }

  /** ポートが既に開かれている場合に待機する。 */
  @SuppressWarnings("deprecation")
  private void sleep() {
    int interval = this.bindInterval_;

    try {
      Thread.sleep(interval);
    } catch (InterruptedException iex) {
      LOGGER.warn(iex);
    }
  }

  private int sweepClient() {
    int size;
    synchronized (this.clientList_) {
      for (int index = this.clientList_.size() - 1; index >= 0; index--) {
        JavelinClientThread client = this.clientList_.get(index);
        if (client.isClosed()) {
          this.clientList_.remove(index);
        }
      }
      size = this.clientList_.size();
    }

    return size;
  }

  /**
   * 初期ポート、ポート最大値が正常な範囲の値になっているかを判定する。
   *
   * @param port 初期ポート
   * @param portMax ポート最大値
   * @return true 初期ポート、ポート最大値が正常な範囲の値になっている場合、<code>true</code>
   */
  private static boolean isPortNumValid(final int port, final int portMax) {
    if (portMax < 0 || portMax > MAX_PORT) {
      return false;
    }
    if (port > portMax || port < 0) {
      return false;
    }
    return true;
  }

  /** {@inheritDoc} */
  public void addCommunicatorListener(CommunicatorListener listener) {
    synchronized (this.listenerList_) {
      this.listenerList_.add(listener);
    }
  }

  /**
   * 切断されたことを各リスナへ通知します。<br>
   *
   * @param forceDisconnected 強制切断された場合は <code>true</code>
   */
  private void notifyClientDisconnected(boolean forceDisconnected) {
    synchronized (this.listenerList_) {
      for (CommunicatorListener listener : this.listenerList_) {
        listener.clientDisconnected(forceDisconnected);
      }
    }
  }

  /**
   * 接続されたことを各リスナへ通知します。<br>
   *
   * @param hostName ホスト名( <code>null</code> の可能性あり)
   * @param ipAddr IP アドレス
   * @param port ポート番号
   */
  private void notifyClientConnected(final String hostName, final String ipAddr, final int port) {
    synchronized (this.listenerList_) {
      for (CommunicatorListener listener : this.listenerList_) {
        listener.clientConnected(hostName, ipAddr, port);
      }
    }
  }
}
/**
 * コメットイベントのラッパークラスです。
 *
 * @author fujii
 */
public class CometEventWrapper {
  /** ロガー */
  private static final ENdoSnipeLogger LOGGER = ENdoSnipeLogger.getLogger(CometEventWrapper.class);

  /** HTTPサーブレットリクエスト。 */
  private final HttpServletResponse response_;

  /** コメットイベント。 */
  private final CometEvent event_;

  /**
   * コンストラクタです。
   *
   * @param event {@link CometEvent}オブジェクト
   * @param response {@link HttpServletResponse}オブジェクト
   */
  public CometEventWrapper(final CometEvent event, final HttpServletResponse response) {
    this.event_ = event;
    this.response_ = response;
  }

  /** クローズ処理を行う。 */
  public void close() {
    try {
      this.event_.close();
    } catch (IOException ex) {
      LOGGER.log(LogMessageCodes.COMET_ERROR, ex);
    } catch (IllegalStateException ex) {
      LOGGER.log(LogMessageCodes.COMET_ERROR, ex);
    }
  }

  /**
   * イベントタイプを取得します。
   *
   * @return イベントタイプ(BEGIN,READ,END,ERROR)
   */
  public EventType getEventType() {
    return this.event_.getEventType();
  }

  /**
   * レスポンスを取得します。
   *
   * @return イベントタイプ(BEGIN,READ,END,ERROR)
   */
  public HttpServletResponse getResponse() {
    return this.response_;
  }

  /**
   * レスポンスが有効かどうかを返します。
   *
   * @return レスポンスが有効な場合はtrue、そうでない場合はfalse
   */
  public boolean isValidResponse() {
    boolean valid = true;
    try {
      this.event_.getHttpServletResponse();
    } catch (Exception ex) {
      LOGGER.log(LogMessageCodes.COMET_ERROR, ex);
      valid = false;
    }
    return valid;
  }
}
/**
 * ルールの管理(追加、変更、削除、参照)を行うクラス。
 *
 * @author tanimoto
 */
public class RuleManager {
  private static final ENdoSnipeLogger LOGGER = ENdoSnipeLogger.getLogger(RuleManager.class);

  /** ルール定義ファイルの読み込みに失敗した際に投げられる例外に渡す文字列。 */
  private static final String RULE_CREATE_ERROR = "RuleCreateError";

  /** デフォルトのルールセットのID。 */
  public static final String DEFAULT_RULESET_ID = "PERFDOCTOR_DEFAULT";

  /** デフォルトのルールセットの名前。 */
  private static final String DEFAULT_RULESET_NAME =
      Messages.getMessage("endosnipe.perfdoctor.rule.RuleManager.DefaultRuleSetName");

  /** デフォルトのルールセットのファイル名。 */
  private static final String DEFAULT_RULESET_FILE = "/perfdoctor_rule.xml";

  /** Java用のデフォルトのルールセットのID。 */
  public static final String DEFAULT_JAVA_RULESET_ID = "PERFDOCTOR_JAVA_DEFAULT";

  /** Java用のデフォルトのルールセットの名前。 */
  private static final String DEFAULT_JAVA_RULESET_NAME =
      Messages.getMessage("endosnipe.perfdoctor.rule.RuleManager.DefaultJavaRuleSetName");

  /** Java用のデフォルトのルールセットのファイル名。 */
  private static final String DEFAULT_JAVA_RULESET_FILE = "/perfdoctor_Java_rule.xml";

  /** DB用のデフォルトのルールセットのID。 */
  public static final String DEFAULT_DB_RULESET_ID = "PERFDOCTOR_DB_DEFAULT";

  /** DB用のデフォルトのルールセットの名前。 */
  private static final String DEFAULT_DB_RULESET_NAME =
      Messages.getMessage("endosnipe.perfdoctor.rule.RuleManager.DefaultDBRuleSetName");

  /** DB用のデフォルトのルールセットのファイル名。 */
  private static final String DEFAULT_DB_RULESET_FILE = "/perfdoctor_DB_rule.xml";

  /** HP-UX用のデフォルトのルールセットのID。 */
  public static final String DEFAULT_HP_UX_RULESET_ID = "PERFDOCTOR_HP_UX_DEFAULT";

  /** HP-UX用のデフォルトのルールセットの名前。 */
  private static final String DEFAULT_HP_UX_RULESET_NAME =
      Messages.getMessage("endosnipe.perfdoctor.rule.RuleManager.DefaultHPUXRuleSetName");

  /**  HP-UX用のデフォルトのルールセットのファイル名。 */
  private static final String DEFAULT_HP_UX_RULESET_FILE = "/perfdoctor_HP_UX_rule.xml";

  /** ルールの管理を行うインスタンス。 */
  private static RuleManager instance__;

  /** ルール定義のインタフェース。 */
  private final RuleDefAccessor accessor_ = new XmlRuleDefAccessor();

  /** リスナーのセット */
  private final Set<RuleChangeListener> listenerSet_ = new LinkedHashSet<RuleChangeListener>();

  // ※ロールバック対象。
  /** 利用可能なRuleSetConfigを保持するMap。 キーはRuleSetConfigのID、値はRuleSetConfig本体。 */
  private HashMap<String, RuleSetConfig> ruleSetConfigMap_;

  /** ファイル削除対象ルール */
  private List<RuleSetConfig> removeList_;

  // ※ロールバック対象。
  /** RuleSetDefを保持するMap。 キーはRuleSetDefのID、値はRuleSetDef本体。 */
  private HashMap<String, RuleSetDef> ruleSetMap_;

  /** 有効となっているルールセットのID。 */
  private String activeRuleSetId_;

  /** 設定が変更されたルールセットのIDのリスト。 */
  private Set<String> dirtyRuleSetIds_;

  /**
   * RuleManagerインスタンスの取得。
   *
   * @return RuleManagerインスタンス。
   */
  public static synchronized RuleManager getInstance() {
    if (instance__ == null) {
      instance__ = new RuleManager();
    }
    return instance__;
  }

  /** コンストラクタ。外部からの呼び出しを禁止する。 */
  private RuleManager() {
    initialize();
  }

  /** インスタンスの初期化を行う。 */
  private void initialize() {
    this.ruleSetMap_ = new HashMap<String, RuleSetDef>();
    this.ruleSetConfigMap_ = loadConfigurations();
    this.activeRuleSetId_ = loadActiveRuleSetId();
    this.dirtyRuleSetIds_ = new HashSet<String>();
    this.removeList_ = Collections.synchronizedList(new ArrayList<RuleSetConfig>());
  }

  /**
   * ルールセット定義を読み込む。 プリファレンスストアにルールセットのIDが一つも保存されていない場合には デフォルトのルールセット定義マップを返す。
   *
   * @return ルールセット定義マップ(定義読み込み済み)
   */
  private HashMap<String, RuleSetConfig> loadConfigurations() {
    HashMap<String, RuleSetConfig> map = createDefaultConfigMap();

    String[] ruleSetIds = RulePreferenceUtil.loadRuleSetIds();
    for (String ruleSetId : ruleSetIds) {
      RuleSetConfig config = RulePreferenceUtil.loadRuleSet(ruleSetId);
      map.put(ruleSetId, config);
    }

    return map;
  }

  /**
   * 初期ルールセット定義マップを作成する。
   *
   * @return ルールセット定義マップ(デフォルト定義のみ)
   */
  private HashMap<String, RuleSetConfig> createDefaultConfigMap() {
    HashMap<String, RuleSetConfig> map = new LinkedHashMap<String, RuleSetConfig>();
    RuleSetConfig config = new RuleSetConfig();
    RuleSetConfig hpUxRuleSetConfig = new RuleSetConfig();
    RuleSetConfig javaRuleSetConfig = new RuleSetConfig();
    RuleSetConfig dbRuleSetConfig = new RuleSetConfig();

    // デフォルトのルールセットを定義する。
    config.setId(DEFAULT_RULESET_ID);
    config.setName(DEFAULT_RULESET_NAME);
    config.setFileName(DEFAULT_RULESET_FILE);
    map.put(DEFAULT_RULESET_ID, config);

    // HP_UX用のルールセットを定義する。
    hpUxRuleSetConfig.setId(DEFAULT_HP_UX_RULESET_ID);
    hpUxRuleSetConfig.setName(DEFAULT_HP_UX_RULESET_NAME);
    hpUxRuleSetConfig.setFileName(DEFAULT_HP_UX_RULESET_FILE);
    map.put(DEFAULT_HP_UX_RULESET_ID, hpUxRuleSetConfig);

    // java用のルールセットを定義する。
    javaRuleSetConfig.setId(DEFAULT_JAVA_RULESET_ID);
    javaRuleSetConfig.setName(DEFAULT_JAVA_RULESET_NAME);
    javaRuleSetConfig.setFileName(DEFAULT_JAVA_RULESET_FILE);
    map.put(DEFAULT_JAVA_RULESET_ID, javaRuleSetConfig);

    // DB用のルールセットを定義する。
    dbRuleSetConfig.setId(DEFAULT_DB_RULESET_ID);
    dbRuleSetConfig.setName(DEFAULT_DB_RULESET_NAME);
    dbRuleSetConfig.setFileName(DEFAULT_DB_RULESET_FILE);
    map.put(DEFAULT_DB_RULESET_ID, dbRuleSetConfig);

    return map;
  }

  /**
   * 現在アクティブなルールセットIDを取得する。 プリファレンスストアに保存されていたルールセットのIDがnullであるか、 長さ0であった場合には、デフォルトのルールセットのIDを返す。
   *
   * @return アクティブなルールセットID
   */
  private String loadActiveRuleSetId() {
    String str = RulePreferenceUtil.loadActiveRuleSetId();

    if (str == null || str.length() == 0) {
      str = DEFAULT_RULESET_ID;
    }

    return str;
  }

  /**
   * ルールセット定義(RuleSetConfigインスタンス)を利用可能なルールセットに追加する。
   *
   * @param config ルールセット定義
   */
  public void addRuleSetConfig(final RuleSetConfig config) {
    this.ruleSetConfigMap_.put(config.getId(), config);
  }

  /**
   * ルールセット定義一覧(利用可能なルール一覧)を取得する。
   *
   * @return ルールセット定義一覧
   */
  public RuleSetConfig[] getRuleSetConfigs() {
    RuleSetConfig[] array = new RuleSetConfig[this.ruleSetConfigMap_.size()];
    return this.ruleSetConfigMap_.values().toArray(array);
  }

  /**
   * ルールセット定義を利用可能なルールセット一覧から削除する。
   *
   * @param id 削除するルールセットID
   */
  public synchronized void removeRuleSetConfig(final String id) {
    RuleSetConfig removeConfig = this.ruleSetConfigMap_.get(id);
    this.removeList_.add(removeConfig);
    this.ruleSetConfigMap_.remove(id);
  }

  /**
   * 現在アクティブなルールセット定義(RuleSetConfigインスタンス)を取得する。
   *
   * @return アクティブなルールセット定義
   */
  public RuleSetConfig getActiveRuleSetConfig() {
    return this.ruleSetConfigMap_.get(this.activeRuleSetId_);
  }

  /**
   * アクティブなルールセットを設定する。
   *
   * @param ruleSetConfig ルールセット定義
   */
  public void setActiveRuleSetConfig(final RuleSetConfig ruleSetConfig) {
    this.activeRuleSetId_ = ruleSetConfig.getId();
  }

  /** ルールセット定義などをプリファレンスストア、xmlファイルに保存する。 */
  @SuppressWarnings("deprecation")
  public synchronized void commit() {
    // アクティブなルールセットIDの保存。
    RulePreferenceUtil.saveActiveRuleSetId(this.activeRuleSetId_);

    // ルールセット詳細一覧の保存。
    List<String> ruleSetIdList = new ArrayList<String>();
    Collection<RuleSetConfig> ruleSetConfigs = this.ruleSetConfigMap_.values();

    for (RuleSetConfig config : ruleSetConfigs) {
      String id = config.getId();
      if (isDefaultRuleSet(id)) {
        continue;
      }

      RulePreferenceUtil.saveRuleSet(config);

      //
      ruleSetIdList.add(id);
    }

    // ルールセットID一覧の保存。
    String[] ruleSetIds = ruleSetIdList.toArray(new String[ruleSetIdList.size()]);
    RulePreferenceUtil.saveRuleSetIds(ruleSetIds);

    // ルールセットの保存。
    // 変更があったルールセットのみ保存する。
    for (String ruleId : this.dirtyRuleSetIds_) {
      if (isDefaultRuleSet(ruleId)) {
        continue;
      }

      RuleSetConfig config = this.ruleSetConfigMap_.get(ruleId);
      if (config == null) {
        continue;
      }
      RuleSetDef def = this.ruleSetMap_.get(ruleId);
      this.accessor_.updateRuleSet(def, config.getFileName());
    }

    // ルールセットの保存。
    // ファイルが存在しないルールセットについて、
    // デフォルトのルールを元にファイルを作成する。
    for (RuleSetConfig config : ruleSetConfigs) {
      String id = config.getId();
      if (isDefaultRuleSet(id)) {
        continue;
      }

      File file = new File(config.getFileName());
      if (file.exists() && file.isFile()) {
        continue;
      }

      File parentFile = file.getParentFile();

      if (parentFile != null && parentFile.exists() == false) {
        try {
          parentFile.mkdirs();
        } catch (SecurityException ex) {
          LOGGER.error(ex.getMessage(), ex);
        }
      }

      // デフォルトのルールをコピーして保存する
      try {
        RuleSetDef defaultRuleSetClone = new RuleSetDef(getRuleSetDef(DEFAULT_RULESET_ID));
        defaultRuleSetClone.setName(config.getName());
        this.accessor_.updateRuleSet(defaultRuleSetClone, config.getFileName());
      } catch (RuleCreateException ex) {
        LOGGER.error(ex.getMessage(), ex);
      }
    }

    // ルールファイルを削除する。
    for (RuleSetConfig config : this.removeList_) {
      File file = new File(config.getFileName());
      if (file.exists()) {
        try {
          file.delete();
        } catch (SecurityException ex) {
          LOGGER.error(ex.getMessage(), ex);
        }
      }
    }
    this.removeList_ = Collections.synchronizedList(new ArrayList<RuleSetConfig>());
  }

  /**
   * ルールセット定義を取得する。<br>
   * 指定されたルールセットIDに対応する設定ファイルが見つからない場合は、<br>
   * デフォルトのルールセット定義を取得する。
   *
   * @param id ルールセットID
   * @return ルールセット定義
   * @throws RuleCreateException ルールセット定義ファイル読み込みに失敗した場合
   */
  public RuleSetDef getRuleSetDef(final String id) throws RuleCreateException {
    RuleSetDef def = this.ruleSetMap_.get(id);
    if (def != null) {
      return def;
    }

    if (id.equals(DEFAULT_RULESET_ID)) {
      def = this.accessor_.findRuleSet(DEFAULT_RULESET_FILE);
      def.setName(this.ruleSetConfigMap_.get(id).getName());
    } else if (id.equals(DEFAULT_HP_UX_RULESET_ID)) {
      def = this.accessor_.findRuleSet(DEFAULT_HP_UX_RULESET_FILE);
      def.setName(this.ruleSetConfigMap_.get(id).getName());
    } else if (id.equals(DEFAULT_JAVA_RULESET_ID)) {
      def = this.accessor_.findRuleSet(DEFAULT_JAVA_RULESET_FILE);
      def.setName(this.ruleSetConfigMap_.get(id).getName());
    } else if (id.equals(DEFAULT_DB_RULESET_ID)) {
      def = this.accessor_.findRuleSet(DEFAULT_DB_RULESET_FILE);
      def.setName(this.ruleSetConfigMap_.get(id).getName());
    } else {
      RuleSetConfig config = this.ruleSetConfigMap_.get(id);
      if (config == null) {
        throw new RuleCreateException("InvalidRuleSetDef", new Object[] {id});
      }
      String fileName = config.getFileName();
      def = this.accessor_.findRuleSet(fileName);
    }

    this.ruleSetMap_.put(id, def);

    return def;
  }

  /**
   * ルールをコピーします。<br>
   *
   * @param orgId コピー元 ID
   * @param dstId コピー先 ID
   * @param dstName コピー先ルールセットの名前
   * @throws RuleCreateException ルールセット定義ファイル読み込みに失敗した場合
   */
  public void copyRuleSetDef(final String orgId, final String dstId, final String dstName)
      throws RuleCreateException {
    RuleSetDef orgDef = getRuleSetDef(orgId);
    RuleSetDef dstDef = new RuleSetDef(orgDef);
    dstDef.setName(dstName);
    this.ruleSetMap_.put(dstId, dstDef);
  }

  /**
   * ルールセット定義を一時的に保存する。<br>
   * rollbackRuleSetメソッドが実行された際に、ルールセット定義を巻き戻すために利用する。
   *
   * @return シリアライズ化されたルールデータ
   */
  public synchronized SerializedRules saveRuleSet() {
    byte[] ruleSetConfigMapData = SerializationUtils.serialize(this.ruleSetConfigMap_);
    byte[] ruleSetMapData = SerializationUtils.serialize(this.ruleSetMap_);
    return new SerializedRules(ruleSetConfigMapData, ruleSetMapData);
  }

  /**
   * ルールセット定義をロールバックする。<br>
   *
   * @param serializedRules シリアライズ化されたルールデータ
   */
  @SuppressWarnings("unchecked")
  public synchronized void rollbackRuleSet(final SerializedRules serializedRules) {
    byte[] ruleSetConfigMapData = serializedRules.getRuleSetConfigMapData();
    byte[] ruleMapData = serializedRules.getRuleMapData();
    if (ruleSetConfigMapData == null
        || ruleSetConfigMapData.length == 0
        || ruleMapData == null
        || ruleMapData.length == 0) {
      return;
    }

    this.ruleSetConfigMap_ =
        (HashMap<String, RuleSetConfig>) SerializationUtils.deserialize(ruleSetConfigMapData);
    this.ruleSetMap_ = (HashMap<String, RuleSetDef>) SerializationUtils.deserialize(ruleMapData);
    this.removeList_ = Collections.synchronizedList(new ArrayList<RuleSetConfig>());
  }

  /**
   * 変更があったルールセットIDを保存する。<br>
   * commitメソッドが実行された際に、このメソッドで指定した<br>
   * ルールセットIDに対するルールセット定義のみ保存する。
   *
   * @param ruleSetId ルールセットID
   */
  public void addDirty(final String ruleSetId) {
    this.dirtyRuleSetIds_.add(ruleSetId);
  }

  /**
   * アクティブなルールセット定義を取得する。
   *
   * @return ルールセット定義
   * @throws RuleCreateException ルール定義ファイルの読み込みに失敗した際に発生する。
   */
  public RuleSetDef getActiveRuleSetDef() throws RuleCreateException {
    return getRuleSetDef(this.activeRuleSetId_);
  }

  /**
   * アクティブなルールセットに含まれる、ルールインスタンスの一覧を取得する。 アクティブなルールセット中の要素中にあるルール名が不正であるために、
   * インスタンス生成に失敗した場合には、RuleCreateExceptionをスローする。
   *
   * @return ルールインスタンスの一覧
   * @throws RuleCreateException ルール定義ファイルの読み込みに失敗した場合
   */
  public List<PerformanceRule> getActiveRules() throws RuleCreateException {
    List<PerformanceRule> ruleList = new ArrayList<PerformanceRule>();
    List<String> errorMessageList = new ArrayList<String>();

    RuleSetDef ruleSetDef = getActiveRuleSetDef();

    for (RuleDef ruleDef : ruleSetDef.getRuleDefs()) {
      try {
        PerformanceRule rule = RuleInstanceUtil.createRuleInstance(ruleDef);
        if (rule != null) {
          ruleList.add(rule);
        }
      } catch (RuleCreateException ex) {
        errorMessageList.add(ex.getMessage());
      }
    }

    if (errorMessageList.size() > 0) {
      String[] messages = errorMessageList.toArray(new String[errorMessageList.size()]);
      throw new RuleCreateException(RULE_CREATE_ERROR, null, messages);
    }

    return ruleList;
  }

  /**
   * ユニークなルールセットIDを取得する。
   *
   * @return ルールセットID
   */
  public String createUniqueId() {
    String[] ruleSetIds = RulePreferenceUtil.loadRuleSetIds();

    String ruleSetId;
    do {
      ruleSetId = UUID.randomUUID().toString();
    } while (ArrayUtils.contains(ruleSetIds, ruleSetId));

    return ruleSetId;
  }

  /** デフォルトルールセットをアクティブにする。 */
  public void setActiveRuleSetDefault() {
    this.activeRuleSetId_ = RuleManager.DEFAULT_RULESET_ID;
    RuleSetConfig config = this.ruleSetConfigMap_.get(RuleManager.DEFAULT_RULESET_ID);
    setActiveRuleSetConfig(config);
  }

  /**
   * 有効となっているルールセットのIDを取得する。
   *
   * @return 有効となっているルールセットID
   */
  public String getActiveRuleSetID() {
    return this.activeRuleSetId_;
  }

  /**
   * ルールセットIDを指定して、有効となっているルールセットを切り換える。
   *
   * @param ruleSetID 有効化するルールセットID
   */
  public void changeActiveRuleSetByID(final String ruleSetID) {
    RuleSetConfig config = this.ruleSetConfigMap_.get(ruleSetID);

    if (config != null) {
      setActiveRuleSetConfig(config);
    }
  }

  /**
   * ルール変更リスナを追加する。
   *
   * @param listener リスナ
   */
  public void addListener(final RuleChangeListener listener) {
    this.listenerSet_.add(listener);
  }

  /**
   * ルール変更リスナを削除する。
   *
   * @param listener リスナ
   */
  public void removeListener(final RuleChangeListener listener) {
    this.listenerSet_.remove(listener);
  }

  /** 更新を通知する。 */
  public void notifyChanged() {
    for (RuleChangeListener listener : this.listenerSet_) {
      listener.ruleChangePerformed();
    }
  }

  /**
   * 指定されたルール ID のルールがデフォルトルールかどうかをチェックします。<br>
   *
   * @param ruleId ルール ID
   * @return デフォルトルールの場合は <code>true</code> 、デフォルトルールでない場合は <code>false</code>
   */
  private boolean isDefaultRuleSet(final String ruleId) {
    if (DEFAULT_RULESET_ID.equals(ruleId)
        || DEFAULT_HP_UX_RULESET_ID.equals(ruleId)
        || DEFAULT_JAVA_RULESET_ID.equals(ruleId)
        || DEFAULT_DB_RULESET_ID.equals(ruleId)) {
      return true;
    }
    return false;
  }
}
/**
 * 指定した期間のJvnファイルを取得し、計測情報として変換するクラスです。
 *
 * @author fujii
 */
public class TermAlarmNotifyProcessor implements EventProcessor {
  /** ロガー */
  private static final ENdoSnipeLogger LOGGER =
      ENdoSnipeLogger.getLogger(TermAlarmNotifyProcessor.class);

  /**
   * 指定した期間Jvnファイルを取得し、計測情報として変換する処理をします。
   *
   * @param request {@link HttpServletRequest}オブジェクト
   * @param response {@link HttpServletResponse}オブジェクト
   */
  public void process(HttpServletRequest request, HttpServletResponse response) {
    String clientId = request.getParameter(EventConstants.CLIENT_ID);
    String agentIds = request.getParameter(EventConstants.AGENT_IDS);
    String alarmLevelStr = request.getParameter(EventConstants.ALARM_LEVEL);
    String spanStr = request.getParameter(EventConstants.SPAN);
    String alarmCntStr = request.getParameter(EventConstants.ALARM_COUNT);

    if (clientId == null) {
      LOGGER.log(LogMessageCodes.NO_CLIENT_ID);
      return;
    }
    List<Integer> agentIdList = RequestUtil.getAgentIdList(agentIds);
    if (agentIdList == null || agentIdList.size() == 0) {
      LOGGER.log(LogMessageCodes.UNKNOWN_AGENT_ID, agentIds);
      return;
    }
    int alarmLevel = EventUtil.getAlarmLevel(alarmLevelStr);
    int alarmCount = EventUtil.getAlarmCount(alarmCntStr);

    Date[] spanArray = RequestUtil.getSpanList(spanStr);
    if (spanArray == null || spanArray.length == 0) {
      LOGGER.log(LogMessageCodes.UNKNOWN_SPAN, spanStr);
      return;
    }

    if (spanStr == null) {
      LOGGER.log(LogMessageCodes.UNKNOWN_SPAN);
      return;
    }

    // 検索
    Timestamp startTime = new Timestamp(spanArray[0].getTime());
    Timestamp endTime = new Timestamp(spanArray[1].getTime());
    DatabaseManager dbMmanager = DatabaseManager.getInstance();

    JvnFileEntryJudge judge = new JvnFileEntryJudge();
    List<AlarmNotifyEntity> entityList = new ArrayList<AlarmNotifyEntity>();
    List<WarningUnit> warningUnitList = new ArrayList<WarningUnit>();

    for (Integer agentId : agentIdList) {
      try {
        String dbName = dbMmanager.getDataBaseName(agentId.intValue());
        if (dbName == null) {
          LOGGER.log(LogMessageCodes.FAIL_READ_DB_NAME);
          continue;
        }
        List<JavelinLog> jvnLogList = JavelinLogDao.selectByTermWithLog(dbName, startTime, endTime);

        warningUnitList.addAll(judge.judge(jvnLogList, true, true));
        createAlarmEntity(warningUnitList, alarmLevel, agentId.intValue(), entityList);
      } catch (SQLException ex) {
        LOGGER.log(LogMessageCodes.SQL_EXCEPTION);
        return;
      }
    }
    Collections.sort(entityList, new AlarmEntityComparator());

    if (entityList.size() >= alarmCount) {
      entityList = entityList.subList(0, alarmCount);
    }
    if (entityList != null && entityList.size() > 0) {
      ResponseUtil.sendMessageOfJSONCode(response, entityList, clientId);
    }
  }

  /**
   * PerformanceDoctorの結果より、{@link AlarmNotifyEntity}のリストを作成します。
   *
   * @param warningUnitList パフォーマンスドクターの結果
   * @param alarmLevel アラームのレベル
   * @param agentId エージェントID
   * @param entityList {@link AlarmNotifyEntity}のリスト
   */
  private void createAlarmEntity(
      List<WarningUnit> warningUnitList,
      int alarmLevel,
      int agentId,
      List<AlarmNotifyEntity> entityList) {
    for (WarningUnit unit : warningUnitList) {
      String level = unit.getLevel();
      if (EventUtil.compareLevel(level, alarmLevel) == false) {
        continue;
      }

      int eventId = EventConstants.EVENT_TERM_NOTIFY_ALARM_RESPONSE;

      AlarmNotifyEntity alarmNotifyEntity =
          DaoUtil.createAlarmEntity(agentId, unit, level, eventId);
      entityList.add(alarmNotifyEntity);
    }
  }
}
Beispiel #5
0
/**
 * Map用サービスクラス。
 *
 * @author fujii
 */
@Service
public class MapService {
  /** ロガー */
  private static final ENdoSnipeLogger LOGGER = ENdoSnipeLogger.getLogger(MapService.class);

  /** マップ情報Dao */
  @Autowired protected MapInfoDao mapInfoDao_;

  /** コンストラクタ */
  public MapService() {}

  /**
   * 全てのマップデータを返す。
   *
   * @return マップデータ
   */
  public List<Map<String, String>> getAllMap() {
    List<MapInfo> mapList = null;
    try {
      mapList = mapInfoDao_.selectAll();
    } catch (PersistenceException pe) {
      Throwable cause = pe.getCause();
      if (cause instanceof SQLException) {
        SQLException sqlEx = (SQLException) cause;
        LOGGER.log(LogMessageCodes.SQL_EXCEPTION, sqlEx, sqlEx.getMessage());
      }
      LOGGER.log(LogMessageCodes.SQL_EXCEPTION);
      return new ArrayList<Map<String, String>>();
    }

    List<Map<String, String>> resultList = new ArrayList<Map<String, String>>();
    for (MapInfo mapInfo : mapList) {
      Map<String, String> dataMap = this.convertDataMap(mapInfo);
      resultList.add(dataMap);
    }
    return resultList;
  }

  /**
   * マップを登録する。
   *
   * @param mapInfo 登録するマップ情報
   * @return 登録結果電文
   */
  public ResponseDto insert(final MapInfo mapInfo) {
    ResponseDto responseDto = new ResponseDto();
    if (getByName(mapInfo.name).size() > 0) {
      String errorMessage = MessageUtil.getMessage("WEWD0160", new Object[] {});
      responseDto.setMessage(errorMessage);
      responseDto.setResult(ResponseConstants.RESULT_FAIL);
      return responseDto;
    }

    // 最終更新日時を設定
    mapInfo.lastUpdate = new Timestamp(Calendar.getInstance().getTimeInMillis());
    int count = 0;
    try {
      count = mapInfoDao_.insert(mapInfo);
      if (count > 0) {
        responseDto.setResult(ResponseConstants.RESULT_SUCCESS);
      } else {
        String errorMessage = MessageUtil.getMessage("WEWD0161", new Object[] {});
        responseDto.setResult(ResponseConstants.RESULT_FAIL);
        responseDto.setMessage(errorMessage);
      }

      return responseDto;
    } catch (DuplicateKeyException dkEx) {
      LOGGER.log(LogMessageCodes.SQL_EXCEPTION, dkEx, dkEx.getMessage());
      String errorMessage = MessageUtil.getMessage("WEWD0160", new Object[] {});
      responseDto.setMessage(errorMessage);
      responseDto.setResult(ResponseConstants.RESULT_FAIL);
      return responseDto;
    }
  }

  /**
   * マップを更新する。
   *
   * @param mapInfo マップ情報
   * @return 更新結果電文
   */
  public ResponseDto update(final MapInfo mapInfo) {
    // 最終更新日時を設定
    mapInfo.lastUpdate = new Timestamp(Calendar.getInstance().getTimeInMillis());
    int count = 0;
    ResponseDto responseDto = new ResponseDto();
    try {
      count = mapInfoDao_.update(mapInfo);
      if (count > 0) {
        responseDto.setResult(ResponseConstants.RESULT_SUCCESS);
      } else {
        String errorMessage = MessageUtil.getMessage("WEWD0162", new Object[] {});
        responseDto.setResult(ResponseConstants.RESULT_FAIL);
        responseDto.setMessage(errorMessage);
      }

      return responseDto;
    } catch (PersistenceException pEx) {
      Throwable cause = pEx.getCause();
      if (cause instanceof SQLException) {
        SQLException sqlEx = (SQLException) cause;
        LOGGER.log(LogMessageCodes.SQL_EXCEPTION, sqlEx, sqlEx.getMessage());
      } else {
        LOGGER.log(LogMessageCodes.SQL_EXCEPTION, pEx, pEx.getMessage());
      }

      String errorMessage = MessageUtil.getMessage("WEWD0163", new Object[] {});
      responseDto.setMessage(errorMessage);
      responseDto.setResult(ResponseConstants.RESULT_FAIL);
      return responseDto;
    }
  }

  /**
   * マップを取得する。
   *
   * @param mapId Target mapId
   * @return 取得結果
   */
  public ResponseDto getById(final long mapId) {
    ResponseDto responseDto = new ResponseDto();
    try {
      MapInfo mapInfo = mapInfoDao_.selectById(mapId);
      if (mapInfo == null) {
        String errorMessage = MessageUtil.getMessage("WEWD0164", new Object[] {});
        responseDto.setMessage(errorMessage);
        responseDto.setResult(ResponseConstants.RESULT_FAIL);
        return responseDto;
      }

      Map<String, String> convertData = this.convertDataMap(mapInfo);
      responseDto.setResult(ResponseConstants.RESULT_SUCCESS);
      responseDto.setData(convertData);

      return responseDto;
    } catch (PersistenceException pEx) {
      Throwable cause = pEx.getCause();
      if (cause instanceof SQLException) {
        SQLException sqlEx = (SQLException) cause;
        LOGGER.log(LogMessageCodes.SQL_EXCEPTION, sqlEx, sqlEx.getMessage());
      } else {
        LOGGER.log(LogMessageCodes.SQL_EXCEPTION, pEx, pEx.getMessage());
      }

      String errorMessage = MessageUtil.getMessage("WEWD0165", new Object[] {});
      responseDto.setMessage(errorMessage);
      responseDto.setResult(ResponseConstants.RESULT_FAIL);
      return responseDto;
    }
  }

  /**
   * マップを取得する。
   *
   * @param name マップ名
   * @return 取得結果
   */
  public List<MapInfo> getByName(final String name) {
    try {
      return mapInfoDao_.selectByName(name);
    } catch (PersistenceException pEx) {
      Throwable cause = pEx.getCause();
      if (cause instanceof SQLException) {
        SQLException sqlEx = (SQLException) cause;
        LOGGER.log(LogMessageCodes.SQL_EXCEPTION, sqlEx, sqlEx.getMessage());
      } else {
        LOGGER.log(LogMessageCodes.SQL_EXCEPTION, pEx, pEx.getMessage());
      }
      return new ArrayList<MapInfo>();
    }
  }

  /**
   * マップを削除する。
   *
   * @param mapId マップID
   * @return 削除結果
   */
  public ResponseDto removeMapById(final long mapId) {
    int count = 0;
    ResponseDto responseDto = new ResponseDto();
    try {
      count = mapInfoDao_.deleteById(mapId);
      if (count == 0) {
        String errorMessage = MessageUtil.getMessage("WEWD0166", new Object[] {});
        responseDto.setMessage(errorMessage);
        responseDto.setResult(ResponseConstants.RESULT_FAIL);
        return responseDto;
      }

      responseDto.setResult(ResponseConstants.RESULT_SUCCESS);
      return responseDto;
    } catch (PersistenceException pEx) {
      Throwable cause = pEx.getCause();
      if (cause instanceof SQLException) {
        SQLException sqlEx = (SQLException) cause;
        LOGGER.log(LogMessageCodes.SQL_EXCEPTION, sqlEx, sqlEx.getMessage());
      } else {
        LOGGER.log(LogMessageCodes.SQL_EXCEPTION, pEx, pEx.getMessage());
      }

      String errorMessage = MessageUtil.getMessage("WEWD0167", new Object[] {});
      responseDto.setMessage(errorMessage);
      responseDto.setResult(ResponseConstants.RESULT_FAIL);
      return responseDto;
    }
  }

  /**
   * マップ情報をMap形式に変換する。
   *
   * @param mapInfo マップ情報
   * @return Map形式のマップ情報
   */
  private Map<String, String> convertDataMap(final MapInfo mapInfo) {
    Map<String, String> dataMap = new HashMap<String, String>();
    dataMap.put("id", String.valueOf(mapInfo.mapId));
    dataMap.put("parentTreeId", "");
    dataMap.put("data", mapInfo.name);
    dataMap.put("treeId", String.valueOf(mapInfo.mapId));
    dataMap.put("mapData", mapInfo.data);
    return dataMap;
  }

  /**
   * 背景画像のリストを取得する。
   *
   * @param directoryPath 背景画像格納フォルダ
   * @param relativePath 相対パス
   * @return 背景画像のリスト取得結果
   */
  public ResponseDto getImageList(final String directoryPath, final String relativePath) {
    Map<String, String> imageMap = new HashMap<String, String>();
    File directory = new File(directoryPath);
    if (directory.isDirectory()) {
      File[] imageFile = directory.listFiles();

      for (File image : imageFile) {
        String imageName = image.getName();
        if (imageName.endsWith(".png")) {
          String imagePath = image.getPath();
          imageMap.put(imageName, relativePath + "/" + imageName);
        }
      }
    }

    ResponseDto responseDto = new ResponseDto();
    responseDto.setResult(ResponseConstants.RESULT_SUCCESS);
    responseDto.setData(imageMap);
    return responseDto;
  }
}
/**
 * Dao処理に関するユーティリティクラスです。
 *
 * @author tsukano
 */
public class DaoUtil {
  /** ロガーオブジェクト */
  private static final ENdoSnipeLogger LOGGER = ENdoSnipeLogger.getLogger(DaoUtil.class);

  /** どのレベルのアラームレベルまでアラームとして含めるかを定める定数 */
  private static final int ALARM_LEVEL = 20;

  /** agentIDを定めた定数 */
  private static final int AGENT_ID = 1;

  /** イベントの終了を表す文字列 */
  private static final String EVENT_INFO_END = "<<javelin.EventInfo_END>>";

  /** インスタンス化を防止するプライベートコンストラクタです。 */
  private DaoUtil() {
    // Do Nothing.
  }

  /**
   * agentId、logFileNameを指定して、JavelinLogを取得する。
   *
   * @param agentId AgentId
   * @param logFileName JVNファイル名
   * @return JavelinLogオブジェクト
   */
  public static JavelinLog getJavelinLog(final String agentId, final String logFileName) {
    // パラメータチェック
    if (agentId == null) {
      LOGGER.log(LogMessageCodes.UNKNOWN_AGENT_ID);
      return null;
    }
    if (logFileName == null) {
      LOGGER.log(LogMessageCodes.UNKNOWN_FILE_NAME);
      return null;
    }

    // DB名を特定する
    DatabaseManager dbMmanager = DatabaseManager.getInstance();
    String dbName = dbMmanager.getDataBaseName(Integer.valueOf(agentId));
    if (dbName == null) {
      LOGGER.log(LogMessageCodes.FAIL_READ_DB_NAME);
      return null;
    }

    try {
      // Javalinログを取得する
      JavelinLog jvnLog = JavelinLogDao.selectByLogFileNameWithBinary(dbName, logFileName);
      return jvnLog;
    } catch (SQLException ex) {
      LOGGER.log(LogMessageCodes.SQL_EXCEPTION);
      return null;
    }
  }

  /**
   * agentId、logFileNameを指定して、JavelinLogを取得する。
   *
   * @param agentId AgentId
   * @param logFileName JVNファイル名
   * @param logFileLineNumber ログファイルの行数
   * @return JavelinLog(文字列)
   */
  public static String getJavelinLogString(
      final String agentId, final String logFileName, final int logFileLineNumber) {
    JavelinLog javelinLog = getJavelinLog(agentId, logFileName);

    if (javelinLog == null || javelinLog.javelinLog == null) {
      return null;
    }

    // JavelinLogを取得する
    BufferedReader reader = new BufferedReader(new InputStreamReader(javelinLog.javelinLog));
    StringBuilder sb = new StringBuilder();
    String line;
    int lineNum = 0;
    try {
      while ((line = reader.readLine()) != null) {
        lineNum++;
        // 開始行以下であれば、読み込まない
        if (lineNum < logFileLineNumber) {
          continue;
        }
        // 1Event分読み込んだら終了する
        if (EVENT_INFO_END.equals(line)) {
          sb.append(line).append("\n");
          break;
        }

        // Eventを読み込む
        sb.append(line).append("\n");
      }
      return sb.toString();
    } catch (IOException ex) {
      LOGGER.log(LogMessageCodes.COMMUNICATION_ERROR, ex);
    } finally {
      try {
        reader.close();
      } catch (IOException ex) {
        LOGGER.log(LogMessageCodes.COMMUNICATION_ERROR, ex);
      }
    }
    return null;
  }

  /**
   * Java文字列をHTML表示用に変換する。
   *
   * @param before Java文字列
   * @return HTMLに変換した文字列
   */
  public static String convertReturnString(final String before) {
    String after = before.replace("<", "&lt;");
    after = after.replace(">", "&gt;");
    return after;
  }

  /**
   * JavelinLogをAlarmNotifyEntityに変換する
   *
   * @param javelinLog AlarmNotifyEntityに変換するJavelinLog
   * @param logFileLineNumber JavelinLogの中の開始行
   * @param ruleId PerformanceDoctorの警告ID
   * @return 変換されたAlarmNotifyEntity
   */
  public static AlarmNotifyEntity convertJavelinLogToAlarmNotifyEntity(
      final JavelinLog javelinLog, final int logFileLineNumber, final String ruleId) {
    JvnFileEntryJudge judge = new JvnFileEntryJudge();
    List<AlarmNotifyEntity> entityList = new ArrayList<AlarmNotifyEntity>();
    List<WarningUnit> warningUnitList = new ArrayList<WarningUnit>();

    List<JavelinLog> jvnLogList = new ArrayList<JavelinLog>();
    jvnLogList.add(javelinLog);
    warningUnitList.addAll(judge.judge(jvnLogList, true, true));

    createAlarmEntity(warningUnitList, ALARM_LEVEL, AGENT_ID, entityList, javelinLog.javelinLog);
    for (AlarmNotifyEntity entry : entityList) {
      if (logFileLineNumber == entry.logFileLineNumber_ && entry.ruleId_.equals(ruleId)) {
        return entry;
      }
    }
    return null;
  }

  /**
   * PerformanceDoctorの結果より、{@link AlarmNotifyEntity}のリストを作成します。
   *
   * @param warningUnitList パフォーマンスドクターの結果
   * @param alarmLevel アラームのレベル
   * @param agentId エージェントID
   * @param entityList {@link AlarmNotifyEntity}のリスト
   * @param javelinLog JavelinLog本体。ここから必要な部分を切りだす
   */
  private static void createAlarmEntity(
      final List<WarningUnit> warningUnitList,
      final int alarmLevel,
      final int agentId,
      final List<AlarmNotifyEntity> entityList,
      final InputStream javelinLog) {
    for (WarningUnit unit : warningUnitList) {
      String level = unit.getLevel();
      if (EventUtil.compareLevel(level, alarmLevel) == false) {
        continue;
      }

      int eventId = EventConstants.EVENT_TERM_NOTIFY_ALARM_RESPONSE;
      AlarmNotifyEntity alarmNotifyEntity = createAlarmEntity(agentId, unit, level, eventId);

      entityList.add(alarmNotifyEntity);
    }
  }

  /**
   * {@link AlarmNotifyEntity}オブジェクトを作成します。
   *
   * @param agentId エージェントID
   * @param unit {@link WarningUnit}オブジェクト
   * @param level レベル
   * @param eventId イベントID
   * @return {@link AlarmNotifyEntity}オブジェクト
   */
  public static AlarmNotifyEntity createAlarmEntity(
      final int agentId, final WarningUnit unit, final String level, final int eventId) {
    AlarmNotifyEntity alarmNotifyEntity = new AlarmNotifyEntity();
    alarmNotifyEntity.eventId_ = eventId;
    alarmNotifyEntity.agentId_ = agentId;
    alarmNotifyEntity.timestamp_ = new Date(unit.getStartTime());
    alarmNotifyEntity.level_ = level;
    alarmNotifyEntity.className_ = unit.getClassName();
    alarmNotifyEntity.methodName_ = unit.getMethodName();
    alarmNotifyEntity.description_ = unit.getDescription();
    alarmNotifyEntity.fileName_ = unit.getLogFileName();
    alarmNotifyEntity.logFileLineNumber_ = unit.getLogFileLineNumber();
    alarmNotifyEntity.ruleId_ = unit.getId();
    return alarmNotifyEntity;
  }
}
/**
 * 初期化を複数回行っていないかチェックするルールです。<br>
 * 通常、クラス名とメソッド名を指定しない場合は、 他の {@link InitDupulicationRule} に登録されていないクラス・メソッドで閾値を下回ったものすべてを、
 * IntervalError として出力します。<br>
 *
 * @author fujii
 * @author sakamoto
 */
public class InitDupulicationRule extends SingleElementRule implements JavelinConstants {
  /** ロガー */
  private static final ENdoSnipeLogger LOGGER =
      ENdoSnipeLogger.getLogger(InitDupulicationRule.class);

  private static final String ID_LEVEL_SEPARATOR = ":";

  /** 閾値 */
  public long threshold;

  /** クラス名(カンマ区切りで複数指定可能/メソッド名と対応する) */
  public String classNameList;

  /** メソッド名(カンマ区切りで複数指定可能/クラス名と対応する) */
  public String methodNameList;

  /** 「ID + ":" + レベル」をキー、メソッド一覧を値に持つマップ */
  private static Map<String, ClassMethodPairs> classMethodPairsMap__ =
      new ConcurrentHashMap<String, ClassMethodPairs>();

  /**
   * このオブジェクトにセットされているクラス・メソッドをマップに登録します。<br>
   * クラス・メソッドが登録されていない {@link InitDupulicationRule} オブジェクトは、 マップに登録されていないクラス・メソッドを IntervalError
   * として出力します。<br>
   */
  @Override
  public void init() {
    if (this.classNameList != null && this.methodNameList != null) {
      String key = getId() + ID_LEVEL_SEPARATOR + getLevel();
      ClassMethodPairs pairs = new ClassMethodPairs(this.classNameList, this.methodNameList);
      classMethodPairsMap__.put(key, pairs);
    }
  }

  /** {@inheritDoc} */
  @SuppressWarnings("deprecation")
  @Override
  protected void doJudgeElement(final JavelinLogElement element) {
    // 識別子が"Event"でない場合は、処理しない。
    String type = element.getBaseInfo().get(JavelinLogColumnNum.ID);
    boolean isEvent = MSG_EVENT.equals(type);

    if (isEvent == false) {
      return;
    }

    String eventName = element.getBaseInfo().get(JavelinLogColumnNum.EVENT_NAME);

    // イベント名が "IntervalError" の場合、検出を行う。
    if (EventConstants.NAME_INTERVALERROR.equals(eventName) == false) {
      return;
    }

    Map<String, String> eventInfoMap =
        JavelinLogUtil.parseDetailInfo(element, JavelinParser.TAG_TYPE_EVENTINFO);
    String actual = eventInfoMap.get(EventConstants.PARAM_INTERVALERROR_ACTUAL_INTERVAL);

    // 実際にかかった時間が閾値以下の場合は、処理を終了する。
    long actualTime = Long.MAX_VALUE;
    try {
      actualTime = Long.parseLong(actual);
    } catch (NumberFormatException ex) {
      LOGGER.warn(ex);
    }
    if (actualTime > this.threshold) {
      return;
    }

    classMethodMatching(element, eventInfoMap, actualTime);
  }

  /**
   * クラス名とメソッド名をマッチングし、IntervalError を出力します。<br>
   *
   * @param element {@link JavelinLogElement} オブジェクト
   * @param eventInfoMap イベント情報
   * @param actualTime 実際にかかった時間(ミリ秒)
   */
  private void classMethodMatching(
      final JavelinLogElement element,
      final Map<String, String> eventInfoMap,
      final long actualTime) {
    String eventClassName = eventInfoMap.get(EventConstants.PARAM_INTERVALERROR_CLASSNAME);
    String eventMethodName = eventInfoMap.get(EventConstants.PARAM_INTERVALERROR_METHODNAME);

    if (this.classNameList == null || this.methodNameList == null) {
      // ルールでクラス名もメソッド名も指定されていなければ、
      // マップに登録されていないクラス・メソッドの場合は IntervalError を出力する。
      for (Map.Entry<String, ClassMethodPairs> entry : classMethodPairsMap__.entrySet()) {
        ClassMethodPairs pairs = entry.getValue();
        if (pairs.contains(eventClassName, eventMethodName)) {
          // マップに登録されている場合は、何もしない。
          return;
        }
      }
      // マップに登録されていなかったので、IntervalError を出力する。
      String stackTrace = eventInfoMap.get(EventConstants.PARAM_INTERVALERROR_STACKTRACE);
      addError(
          true,
          stackTrace,
          element,
          false,
          new Object[] {this.threshold, actualTime, eventClassName, eventMethodName});
      return;
    }

    String[] classArray = this.classNameList.split(",");
    String[] methodArray = this.methodNameList.split(",");
    int repeatTime = Math.min(classArray.length, methodArray.length);

    for (int num = 0; num < repeatTime; num++) {
      // クラス名、メソッド名がリストと一致したときのみ出力する。
      if (classArray[num].equals(eventClassName) && methodArray[num].equals(eventMethodName)) {
        String stackTrace = eventInfoMap.get(EventConstants.PARAM_INTERVALERROR_STACKTRACE);
        addError(
            true,
            stackTrace,
            element,
            false,
            new Object[] {this.threshold, actualTime, eventClassName, eventMethodName});
        return;
      }
    }
  }

  /**
   * メソッドのリストを保持するクラス。<br>
   * いずれかの {@link InitDupulicationRule} オブジェクトのフィールドに持っているクラス・メソッドは、 このクラスに登録されます。<br>
   *
   * @author Sakamoto
   */
  private static class ClassMethodPairs {
    private final Set<String> classAndMethodSet_;

    private static final String SEPARATOR = "###";

    /**
     * 登録するメソッドを指定してオブジェクトを初期化します。<br>
     *
     * @param classNameList クラス名をカンマで区切った文字列
     * @param methodNameList メソッド名をカンマで区切った文字列
     */
    public ClassMethodPairs(final String classNameList, final String methodNameList) {
      String[] classNameArray = classNameList.split(",");
      String[] methodNameArray = methodNameList.split(",");
      int count = Math.min(classNameArray.length, methodNameArray.length);
      this.classAndMethodSet_ = new HashSet<String>();
      for (int index = 0; index < count; index++) {
        String className = classNameArray[index];
        String methodName = methodNameArray[index];
        this.classAndMethodSet_.add(className + SEPARATOR + methodName);
      }
    }

    /**
     * 指定されたクラスのメソッドが、このオブジェクトに登録されているかどうかを調べます。<br>
     *
     * @param className 検索するメソッドのクラス名
     * @param methodName 検索するメソッド
     * @return 登録されている場合は <code>true</code> 、登録されていない場合は <code>false</code>
     */
    public boolean contains(final String className, final String methodName) {
      return this.classAndMethodSet_.contains(className + SEPARATOR + methodName);
    }
  }
}
/**
 * Commons Poolのサイズのレポートを生成するレポートプロセッサ。
 *
 * @author iida
 */
public class ServerPoolReportProcessor extends ReportPublishProcessorBase {
  /** ロガー */
  private static final ENdoSnipeLogger LOGGER =
      ENdoSnipeLogger.getLogger(ServerPoolReportProcessor.class);

  /**
   * ReportProcessorを生成する。<br>
   *
   * @param type レポート種別
   */
  public ServerPoolReportProcessor(ReportType type) {
    super(type);
  }

  /**
   * {@inheritDoc}
   *
   * @param reportContainer
   */
  @Override
  protected Object getReportPlotData(
      ReportSearchCondition cond, ReportProcessReturnContainer reportContainer) {
    // 検索条件を取得する。
    String database = cond.getDatabases().get(0);
    Timestamp startTime = cond.getStartDate();
    Timestamp endTime = cond.getEndDate();

    // DBからデータを検索する。
    List<ItemData> commonsPoolSizeData = null;
    try {
      commonsPoolSizeData =
          GraphItemAccessUtil.findItemData(
              database,
              Constants.ITEMNAME_POOL_SIZE,
              CompressOperator.SIMPLE_AVERAGE,
              startTime,
              endTime);
    } catch (SQLException ex) {
      LOGGER.log(
          LogIdConstants.EXCEPTION_IN_READING,
          ex,
          ReporterConfigAccessor.getReportName(getReportType()));
      return null;
    }

    return commonsPoolSizeData;
  }

  /**
   * {@inheritDoc}
   *
   * @param reportContainer
   */
  @SuppressWarnings("unchecked")
  @Override
  protected Object convertPlotData(
      Object rawData, ReportSearchCondition cond, ReportProcessReturnContainer reportContainer) {
    // データ変換は特に行いません。
    return rawData;
  }

  /**
   * {@inheritDoc}
   *
   * @param cond
   * @param reportContainer
   */
  @SuppressWarnings("unchecked")
  @Override
  protected void outputReport(
      Object plotData, ReportSearchCondition cond, ReportProcessReturnContainer reportContainer) {
    List<ItemData> commonsPoolSizeData = (List<ItemData>) plotData;

    // 項目名が"Max:ConnectionPoolImpl@56ff3d","Num:ConnectionPoolImpl@56ff3d"となっているので、
    // ":"の後が等しいものをまとめ、同一のグラフに出力する。
    Map<String, List<ServerPoolRecord>> serverPoolRecordMap =
        new HashMap<String, List<ServerPoolRecord>>();
    for (ItemData itemData : commonsPoolSizeData) {
      String[] itemName = StringUtils.split(itemData.getItemName(), ":");
      if (!serverPoolRecordMap.containsKey(itemName[1])) {
        List<ServerPoolRecord> serverPoolRecords = new ArrayList<ServerPoolRecord>(200);
        for (ItemRecord itemRecord : itemData.getRecords()) {
          ServerPoolRecord serverPoolRecord = new ServerPoolRecord();
          serverPoolRecord.setMeasurementTime(itemRecord.getMeasurementTime());
          serverPoolRecords.add(serverPoolRecord);
        }
        serverPoolRecordMap.put(itemName[1], serverPoolRecords);
      }
      for (int index = 0; index < itemData.getRecords().size(); index++) {
        ItemRecord itemRecord = itemData.getRecords().get(index);
        ServerPoolRecord serverPoolRecord = serverPoolRecordMap.get(itemName[1]).get(index);
        if ("Max".equals(itemName[0])) {
          serverPoolRecord.setServerPoolMax(itemRecord.getValue());
        } else if ("Num".equals(itemName[0])) {
          serverPoolRecord.setServerPoolNum(itemRecord.getValue());
        }
      }
    }

    // 出力するレポートの種類に応じて、テンプレートのファイルパスを取得する。
    String templateFilePath;
    try {
      templateFilePath = TemplateFileManager.getInstance().getTemplateFile(ReportType.SERVER_POOL);
    } catch (IOException exception) {
      reportContainer.setHappendedError(exception);
      return;
    }

    // レポート出力の引数情報を取得する。
    String outputFolderPath =
        getOutputFolderName()
            + File.separator
            + ReporterConfigAccessor.getProperty(super.getReportType().getId() + ".outputFile");
    Timestamp startTime = cond.getStartDate();
    Timestamp endTime = cond.getEndDate();

    // レポート出力を実行する。
    RecordReporter<ServerPoolRecord> reporter =
        new RecordReporter<ServerPoolRecord>(getReportType());

    for (Map.Entry<String, List<ServerPoolRecord>> serverPoolRecordEntry :
        serverPoolRecordMap.entrySet()) {
      ServerPoolRecord[] records =
          serverPoolRecordEntry.getValue().toArray(new ServerPoolRecord[] {});
      String itemName = serverPoolRecordEntry.getKey();
      String[] graphTitles = {"Commons Poolのサイズ(" + itemName + ")"};
      reporter.outputReport(
          templateFilePath, outputFolderPath, itemName, graphTitles, records, startTime, endTime);
    }
  }
}