/** ソケットの再接続 */
  private synchronized void reconnectNotificationSocket() {
    apnsInputMonitoringThread = null;

    // 既存の接続をクローズ
    ApnsUtil.close(apnsNotificationSocket);

    // パケット送信量をリセット
    packetSize = 0;

    // Socket取得
    apnsNotificationSocket = null;
    apnsNotificationSocket = ApnsUtil.createAPNSGatewaySocket(isProduction, factory, socksProxy);

    // APNsからの入力待ちスレッドを開始
    try {
      apnsInputMonitoringThread =
          new ApnsInputMonitoringThread(apnsNotificationSocket.getInputStream());
      apnsInputMonitoringThread.start();
    } catch (IOException e) {
      logger.info("APNs入力ストリーム取得エラー", e);
    }
    logger.debug("APNsへのSocket通信を構築または再構築しました。");
  }
  /**
   * PUSH通知(複数件送信)
   *
   * @param apnsNotificationList 送信データリスト
   * @return 送信結果
   */
  public synchronized ApnsResult push(List<ApnsNotification> apnsNotificationList) {
    logger.info("PUSH通知 - 開始");
    try {

      // Socketの生成
      reconnectNotificationSocket();

      // 通知リスト読み込み位置
      int pos = 0;
      packetSize = 0;
      while (true) {
        int i;
        for (i = pos; i < apnsNotificationList.size(); i++) {
          pos = i;

          // 通知情報取り出し
          ApnsNotification apnsNotification = apnsNotificationList.get(i);

          // 実行Mapに格納
          executeApnsNotificationMapIdentifier.put(
              apnsNotification.getIdentifier(), apnsNotification);
          executeIndexMapApnsNotification.put(apnsNotification, i);

          logger.trace(
              "deviceToken:{}, payload:{}",
              apnsNotification.getToken(),
              apnsNotification.getPayload());

          // バイナリデータに変換
          byte[] pushData = apnsNotification.getNotificationBytes();
          if (pushData == null) {
            logger.info(
                "PUSHデータを生成できませんでした。token={}, payload={}",
                apnsNotification.getToken(),
                apnsNotification.getPayload());
            // エラーにステータス変更
            apnsNotification.setPushStatus(PushStatus.ERROR);
            // 読み飛ばし
            continue;
          }

          // パケット量制限を超える場合は再接続
          if (this.maxPacket != 0 && this.packetSize > this.maxPacket) {
            reconnectNotificationSocket();
          }

          try {
            // PUSH通知
            OutputStream os = apnsNotificationSocket.getOutputStream();
            os.write(pushData);
            os.flush();

            // パケット量加算
            packetSize += pushData.length;

            // 実行済にステータス設定
            apnsNotification.setPushStatus(PushStatus.DONE);
          } catch (IOException e) {
            // OutputStream書き込みエラー
            logger.debug("APNs通知情報送信エラー。リトライを試行します。", e);
            pos = processPushError(i, apnsNotificationList);
            // forループを抜けてリトライ
            break;
          }

          // 待機
          ApnsUtil.sleep(pushIntervalMs);

          // エラー確認
          if (apnsInputMonitoringThread.hasError()) {
            pos = processPushError(i, apnsNotificationList);
            // forループを抜けてリトライ
            break;
          }
          pos++;
        }

        // 終端まで達していない場合は残りを処理
        if (pos < apnsNotificationList.size()) {
          continue;
        }

        // 1秒間待機(最終通信後のAPNsからの入力を待つ)
        ApnsUtil.sleep(1000);

        // エラー確認
        if (apnsInputMonitoringThread.hasError()) {
          pos = processPushError(i, apnsNotificationList);
          // リトライ
          continue;
        }
        // 全件処理完了
        break;
      }

      return createApnsSendResult(apnsNotificationList, true, null);
    } catch (Exception e) {
      logger.error("PUSH通知処理中にException発生。", e);
      return createApnsSendResult(apnsNotificationList, false, e);
    } finally {
      ApnsUtil.close(apnsNotificationSocket);
      logger.info("PUSH通知 - 終了");
    }
  }