public synchronized void exit() {

    log.debug("exit() - identifyQueueSize: " + IdentifyQueue.getInstance().size());

    ComponentAPIWrapper.save();

    if (IdentifyQueue.getInstance().size() <= 0) {

      CacheableMgr.getInstance().saveToCache();

      UserRequestHandler.getInstance()
          .handle(
              UserRequestHandler.DELETE_IDENTIFICATION_TABLE,
              null,
              true, // progress
              false // result
              );

      log.debug("OSIT EXIT...");
      System.exit(0);
    }

    log.debug("show message dialog to confirm exit or not");

    String[] buttonList = {"Yes", "No"};
    int choice =
        JOptionPane.showOptionDialog(
            null,
            "Identification Queue is not empty.(size : "
                + IdentifyQueue.getInstance().size()
                + ")\n"
                + "If you close this application with non-empty queue.\n"
                + "identification process for this queue will start again.\n"
                + "But it's not recommended. (Data loss problem)\n"
                + "Do you really want to exit now?\n",
            "Exit",
            JOptionPane.YES_NO_OPTION,
            JOptionPane.QUESTION_MESSAGE,
            null,
            buttonList,
            "Yes");
    if (choice == JOptionPane.NO_OPTION) {
      return; // will not exit.
    }

    log.debug("user select yes option and create thread");

    JDlgExitMessage dlgExitMessage = new JDlgExitMessage();
    String message =
        "OSI try to sync with Protex Server.\n" + "It takes several minutes to finish.";
    DialogDisplayerThread aDialogDiaplayerThread =
        new DialogDisplayerThread(message, dlgExitMessage);
    CompleteSendingThread aCompleteSendingThread =
        new CompleteSendingThread(aDialogDiaplayerThread);

    log.debug("Thread start");

    aDialogDiaplayerThread.execute();

    aCompleteSendingThread.start();

    dlgExitMessage.setVisible(true); // block

    CacheableMgr.getInstance().saveToCache();

    log.debug("OSIT EXIT...");
    System.exit(0);
  }
  public void run() {

    log.debug("run() start");

    boolean loop = true;

    int queueSize = IdentifyQueue.getInstance().size();
    log.info("Trying to Sending item to Protex Server - Identify Queue Size: " + queueSize);

    long startTime = System.currentTimeMillis();
    while (loop) { // block

      BackgroundJobManager.getInstance().requestStopIdentifyThread();

      long endTime = System.currentTimeMillis();
      long timeDuration = endTime - startTime;

      if (timeDuration % 100 == 0) System.out.println("delayTime : " + timeDuration);

      if (timeDuration >= TIME_LIMIT) {
        log.error("TIME_LIMIT_EXCEED during completing sending items to Protex server ");
        loop = false;
        aDialogDiaplayerThread.closeDialog();
        String exitMessage =
            "OSI fails to sync with Protex Server.\n"
                + "Please contact to OSI Development Team to resolve this problem.";
        String[] button = {"OK"};
        JOptionPane.showOptionDialog( // block
            null,
            exitMessage,
            "Exit",
            JOptionPane.YES_OPTION,
            JOptionPane.QUESTION_MESSAGE,
            null,
            button,
            "OK");
        continue;

      } else {

        boolean isAllidentifyThreadStopped =
            BackgroundJobManager.getInstance().isAllIdentifyThreadReadyStatus();
        if (isAllidentifyThreadStopped) {
          queueSize = IdentifyQueue.getInstance().size();
          log.info(
              "OSI succeeds to sync with Protex Server. - Identify Queue Size: "
                  + queueSize
                  + " / "
                  + timeDuration
                  + " ms.");
          loop = false;
          aDialogDiaplayerThread.closeDialog();
        }
      }

      try {
        Thread.sleep(100);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
    log.debug("run() end");
  }