@Override
  public void handleException(Exception e) {
    _logger.error(e.getMessage(), e);

    if (!(e instanceof HttpResponseException)) {
      super.handleException(e);

      return;
    }

    HttpResponseException hre = (HttpResponseException) e;

    int statusCode = hre.getStatusCode();

    if (statusCode != HttpStatus.SC_NOT_FOUND) {
      super.handleException(e);

      return;
    }

    SyncAccount syncAccount = SyncAccountService.fetchSyncAccount(getSyncAccountId());

    if (syncAccount.getState() == SyncAccount.STATE_DISCONNECTED) {
      super.handleException(e);

      return;
    }

    SyncFile syncFile = (SyncFile) getParameterValue("syncFile");

    SyncFileService.deleteSyncFile(syncFile, false);
  }
  public static synchronized Session getSession(long syncAccountId) {
    Session session = _sessions.get(syncAccountId);

    if (session != null) {
      return session;
    }

    try {
      SyncAccount syncAccount = SyncAccountService.fetchSyncAccount(syncAccountId);

      URL url = new URL(syncAccount.getUrl());

      session =
          new Session(
              url,
              syncAccount.getLogin(),
              Encryptor.decrypt(syncAccount.getPassword()),
              syncAccount.isTrustSelfSigned(),
              syncAccount.getMaxConnections());

      _sessions.put(syncAccountId, session);

      return session;
    } catch (Exception e) {
      _logger.error(e.getMessage(), e);

      return null;
    }
  }
  public static SyncAccount synchronizeSyncAccount(long syncAccountId) {
    GetSyncContextEvent getSyncContextEvent =
        new GetSyncContextEvent(syncAccountId, Collections.<String, Object>emptyMap());

    getSyncContextEvent.run();

    return SyncAccountService.fetchSyncAccount(syncAccountId);
  }
  @Test
  public void testAddAccount() throws Exception {
    SyncAccount syncAccount =
        SyncAccountService.addSyncAccount(
            filePathName, "*****@*****.**", "test", "http://localhost:8080/api/jsonws");

    _syncAccount = SyncAccountService.fetchSyncAccount(syncAccount.getSyncAccountId());

    Assert.assertNotNull(_syncAccount);
  }
  public static SyncAccount synchronizeSyncAccount(long syncAccountId) {
    Map<String, Object> parameters = new HashMap<String, Object>();

    parameters.put("uuid", null);

    GetSyncContextEvent getSyncContextEvent = new GetSyncContextEvent(syncAccountId, parameters);

    getSyncContextEvent.run();

    return SyncAccountService.fetchSyncAccount(syncAccountId);
  }
  @Override
  protected void processResponse(String response) throws Exception {
    Set<Long> remoteSyncSiteIds = new HashSet<Long>();

    ObjectMapper objectMapper = new ObjectMapper();

    List<SyncSite> remoteSyncSites =
        objectMapper.readValue(response, new TypeReference<List<SyncSite>>() {});

    for (SyncSite remoteSyncSite : remoteSyncSites) {
      SyncSite localSyncSite =
          SyncSiteService.fetchSyncSite(remoteSyncSite.getGroupId(), getSyncAccountId());

      if (localSyncSite == null) {
        SyncAccount syncAccount = SyncAccountService.fetchSyncAccount(getSyncAccountId());

        remoteSyncSite.setFilePathName(
            syncAccount.getFilePathName() + "/" + remoteSyncSite.getName());

        remoteSyncSite.setSyncAccountId(getSyncAccountId());

        SyncSiteService.update(remoteSyncSite);

        remoteSyncSiteIds.add(remoteSyncSite.getSyncSiteId());
      } else {
        localSyncSite.setDescription(remoteSyncSite.getDescription());
        localSyncSite.setFriendlyURL(remoteSyncSite.getFriendlyURL());
        localSyncSite.setName(remoteSyncSite.getName());
        localSyncSite.setType(remoteSyncSite.getType());
        localSyncSite.setTypeSettings(remoteSyncSite.getTypeSettings());
        localSyncSite.setSite(remoteSyncSite.getSite());

        SyncSiteService.update(localSyncSite);

        remoteSyncSiteIds.add(localSyncSite.getSyncSiteId());
      }
    }

    List<SyncSite> localSyncSites = SyncSiteService.findSyncSites(getSyncAccountId());

    for (SyncSite localSyncSite : localSyncSites) {
      if (remoteSyncSiteIds.contains(localSyncSite.getSyncSiteId())) {
        continue;
      }

      SyncSiteService.deleteSyncSite(localSyncSite.getSyncSiteId());
    }
  }
  @Override
  public void run() {
    try {
      SyncAccount syncAccount = SyncAccountService.fetchSyncAccount(_syncAccountId);

      if (syncAccount.getState() != SyncAccount.STATE_CONNECTED) {
        return;
      }

      doRun();
    } catch (Exception e) {
      _logger.error(e.getMessage(), e);
    }

    BatchEventManager.fireBatchEvents();
  }
  protected void addSyncWatchEvent(String eventType, Path filePath) {
    try {
      String filePathName = filePath.toString();

      Path parentFilePath = filePath.getParent();

      String parentFilePathName = parentFilePath.toString();

      SyncAccount syncAccount = SyncAccountService.fetchSyncAccount(getSyncAccountId());

      if (isDuplicateEvent(eventType, filePath.toString(), getSyncAccountId())) {

        return;
      }

      if (filePathName.equals(syncAccount.getFilePathName())
          || parentFilePathName.equals(syncAccount.getFilePathName())) {

        return;
      }

      long repositoryId = getRepositoryId(filePath);

      if (repositoryId <= 0) {
        return;
      }

      SyncSite syncSite = SyncSiteService.fetchSyncSite(repositoryId, getSyncAccountId());

      Set<Long> activeSyncSiteIds = SyncSiteService.getActiveSyncSiteIds(getSyncAccountId());

      if (!activeSyncSiteIds.contains(syncSite.getSyncSiteId())) {
        return;
      }

      SyncWatchEventService.addSyncWatchEvent(
          eventType, filePathName, getFileType(eventType, filePath), getSyncAccountId());
    } catch (Exception e) {
      _logger.error(e.getMessage(), e);
    }
  }
  protected synchronized void processSyncWatchEvent(SyncWatchEvent syncWatchEvent)
      throws Exception {

    SyncAccount syncAccount = SyncAccountService.fetchSyncAccount(_syncAccountId);

    if (syncAccount.getState() != SyncAccount.STATE_CONNECTED) {
      return;
    }

    if (_processedSyncWatchEventIds.contains(syncWatchEvent.getSyncWatchEventId())) {

      SyncWatchEventService.deleteSyncWatchEvent(syncWatchEvent.getSyncWatchEventId());

      return;
    }

    String eventType = syncWatchEvent.getEventType();

    if (eventType.equals(SyncWatchEvent.EVENT_TYPE_RENAME_FROM)) {
      eventType = SyncWatchEvent.EVENT_TYPE_DELETE;

      syncWatchEvent.setEventType(eventType);

      SyncWatchEventService.update(syncWatchEvent);
    }

    if (_logger.isDebugEnabled()) {
      _logger.debug("Processing Sync watch event {}", syncWatchEvent.toString());
    }

    String fileType = syncWatchEvent.getFileType();

    if (eventType.equals(SyncWatchEvent.EVENT_TYPE_CREATE)) {
      if (fileType.equals(SyncFile.TYPE_FILE)) {
        SyncWatchEvent duplicateSyncWatchEvent = null;

        if (OSDetector.isApple()) {
          duplicateSyncWatchEvent =
              SyncWatchEventService.fetchDuplicateSyncWatchEvent(syncWatchEvent);
        }

        if (duplicateSyncWatchEvent != null) {
          if (_logger.isDebugEnabled()) {
            _logger.debug("Skipping outdated Sync watch event");
          }
        } else {
          addFile(syncWatchEvent);
        }
      } else {
        addFolder(syncWatchEvent);
      }
    } else if (eventType.equals(SyncWatchEvent.EVENT_TYPE_DELETE)) {
      deleteFile(syncWatchEvent);
    } else if (eventType.equals(SyncWatchEvent.EVENT_TYPE_MODIFY)) {
      SyncWatchEvent duplicateSyncWatchEvent =
          SyncWatchEventService.fetchDuplicateSyncWatchEvent(syncWatchEvent);

      if (duplicateSyncWatchEvent != null) {
        if (_logger.isDebugEnabled()) {
          _logger.debug("Skipping outdated Sync watch event");
        }
      } else {
        modifyFile(syncWatchEvent);
      }
    } else if (eventType.equals(SyncWatchEvent.EVENT_TYPE_MOVE)) {
      moveFile(syncWatchEvent);
    } else if (eventType.equals(SyncWatchEvent.EVENT_TYPE_RENAME)) {
      renameFile(syncWatchEvent);
    }

    syncAccount = SyncAccountService.fetchSyncAccount(_syncAccountId);

    if (syncAccount.getState() == SyncAccount.STATE_CONNECTED) {
      SyncWatchEventService.deleteSyncWatchEvent(syncWatchEvent.getSyncWatchEventId());
    }
  }
  protected void addFolder(SyncWatchEvent syncWatchEvent) throws Exception {
    Path targetFilePath = Paths.get(syncWatchEvent.getFilePathName());

    if (sanitizeFileName(targetFilePath) || isInErrorState(targetFilePath)) {

      return;
    }

    Path parentTargetFilePath = targetFilePath.getParent();

    SyncFile parentSyncFile = SyncFileService.fetchSyncFile(parentTargetFilePath.toString());

    if ((parentSyncFile == null)
        || (!parentSyncFile.isSystem() && (parentSyncFile.getTypePK() == 0))) {

      queueSyncWatchEvent(parentTargetFilePath.toString(), syncWatchEvent);

      return;
    }

    SyncFile syncFile = SyncFileService.fetchSyncFile(targetFilePath.toString());

    if (syncFile == null) {
      syncFile = SyncFileService.fetchSyncFile(FileKeyUtil.getFileKey(targetFilePath));

      if (!verifySite(syncFile, parentSyncFile)) {
        syncFile = null;
      }
    }

    if (syncFile == null) {
      SyncFileService.addFolderSyncFile(
          targetFilePath,
          parentSyncFile.getTypePK(),
          parentSyncFile.getRepositoryId(),
          _syncAccountId);

      return;
    }

    Path sourceFilePath = Paths.get(syncFile.getFilePathName());

    if (targetFilePath.equals(sourceFilePath)) {
      FileKeyUtil.writeFileKey(targetFilePath, String.valueOf(syncFile.getSyncFileId()), true);
    } else if (FileUtil.exists(sourceFilePath)) {
      SyncFileService.addFolderSyncFile(
          targetFilePath,
          parentSyncFile.getTypePK(),
          parentSyncFile.getRepositoryId(),
          _syncAccountId);

      return;
    } else if (parentTargetFilePath.equals(sourceFilePath.getParent())) {
      if (isPendingTypePK(syncFile)) {
        queueSyncWatchEvent(syncFile.getFilePathName(), syncWatchEvent);

        return;
      }

      SyncFileService.updateFolderSyncFile(targetFilePath, _syncAccountId, syncFile);
    } else {
      if (isPendingTypePK(syncFile)) {
        queueSyncWatchEvent(syncFile.getFilePathName(), syncWatchEvent);

        return;
      }

      SyncFileService.moveFolderSyncFile(
          targetFilePath, parentSyncFile.getTypePK(), _syncAccountId, syncFile);

      Path sourceFileNameFilePath = sourceFilePath.getFileName();

      if (!sourceFileNameFilePath.equals(targetFilePath.getFileName())) {
        SyncFileService.updateFolderSyncFile(targetFilePath, _syncAccountId, syncFile);
      }
    }

    SyncAccount syncAccount = SyncAccountService.fetchSyncAccount(_syncAccountId);

    if (syncAccount.getState() == SyncAccount.STATE_CONNECTED) {
      SyncWatchEvent relatedSyncWatchEvent =
          SyncWatchEventService.fetchSyncWatchEvent(
              SyncWatchEvent.EVENT_TYPE_DELETE,
              syncWatchEvent.getFilePathName(),
              syncWatchEvent.getTimestamp());

      if (relatedSyncWatchEvent != null) {
        _processedSyncWatchEventIds.add(relatedSyncWatchEvent.getSyncWatchEventId());
      }
    }
  }
  protected void addFile(SyncWatchEvent syncWatchEvent) throws Exception {
    final Path targetFilePath = Paths.get(syncWatchEvent.getFilePathName());

    if (FileUtil.notExists(targetFilePath)
        || sanitizeFileName(targetFilePath)
        || isInErrorState(targetFilePath)) {

      return;
    }

    Path parentTargetFilePath = targetFilePath.getParent();

    final SyncFile parentSyncFile = SyncFileService.fetchSyncFile(parentTargetFilePath.toString());

    if ((parentSyncFile == null)
        || (!parentSyncFile.isSystem() && (parentSyncFile.getTypePK() == 0))) {

      queueSyncWatchEvent(parentTargetFilePath.toString(), syncWatchEvent);

      return;
    }

    SyncFile syncFile = SyncFileService.fetchSyncFile(targetFilePath.toString());

    if (syncFile == null) {
      syncFile = SyncFileService.fetchSyncFile(FileKeyUtil.getFileKey(targetFilePath));

      if (!verifySite(syncFile, parentSyncFile)) {
        syncFile = null;
      }
    }

    if (syncFile == null) {
      Runnable runnable =
          new Runnable() {

            @Override
            public void run() {
              try {
                SyncSite syncSite =
                    SyncSiteService.fetchSyncSite(parentSyncFile.getRepositoryId(), _syncAccountId);

                if ((syncSite == null)
                    || !syncSite.isActive()
                    || !FileUtil.checkFilePath(targetFilePath)) {

                  return;
                }

                SyncFileService.addFileSyncFile(
                    targetFilePath,
                    parentSyncFile.getTypePK(),
                    parentSyncFile.getRepositoryId(),
                    _syncAccountId);
              } catch (Exception e) {
                if (SyncFileService.fetchSyncFile(targetFilePath.toString()) == null) {

                  _logger.error(e.getMessage(), e);
                }
              }
            }
          };

      _executorService.execute(runnable);

      return;
    }

    Path sourceFilePath = Paths.get(syncFile.getFilePathName());

    if (targetFilePath.equals(sourceFilePath)) {
      if (isPendingTypePK(syncFile) || (syncFile.getState() == SyncFile.STATE_IN_PROGRESS)) {

        queueSyncWatchEvent(syncFile.getFilePathName(), syncWatchEvent);

        return;
      }

      if (FileUtil.isModified(syncFile)) {
        SyncFileService.updateFileSyncFile(targetFilePath, _syncAccountId, syncFile);
      }
    } else if (FileUtil.exists(sourceFilePath)) {
      try {
        if ((Files.size(targetFilePath) == 0)
            || FileUtil.isModified(syncFile, targetFilePath)
            || isInErrorState(sourceFilePath)) {

          SyncFileService.addFileSyncFile(
              targetFilePath,
              parentSyncFile.getTypePK(),
              parentSyncFile.getRepositoryId(),
              _syncAccountId);
        } else {
          SyncFileService.copySyncFile(
              syncFile,
              targetFilePath,
              parentSyncFile.getTypePK(),
              parentSyncFile.getRepositoryId(),
              _syncAccountId);
        }
      } catch (Exception e) {
        if (SyncFileService.fetchSyncFile(targetFilePath.toString()) == null) {

          _logger.error(e.getMessage(), e);
        }
      }

      return;
    } else if (parentTargetFilePath.equals(sourceFilePath.getParent())) {
      if (isPendingTypePK(syncFile) || (syncFile.getState() == SyncFile.STATE_IN_PROGRESS)) {

        queueSyncWatchEvent(syncFile.getFilePathName(), syncWatchEvent);

        return;
      }

      SyncFileService.updateFileSyncFile(targetFilePath, _syncAccountId, syncFile);
    } else {
      if (isPendingTypePK(syncFile) || (syncFile.getState() == SyncFile.STATE_IN_PROGRESS)) {

        queueSyncWatchEvent(syncFile.getFilePathName(), syncWatchEvent);

        return;
      }

      SyncFileService.moveFileSyncFile(
          targetFilePath, parentSyncFile.getTypePK(), _syncAccountId, syncFile);

      Path sourceFileNameFilePath = sourceFilePath.getFileName();

      if (!sourceFileNameFilePath.equals(targetFilePath.getFileName())) {
        SyncFileService.updateFileSyncFile(targetFilePath, _syncAccountId, syncFile);
      }
    }

    SyncAccount syncAccount = SyncAccountService.fetchSyncAccount(_syncAccountId);

    if (syncAccount.getState() == SyncAccount.STATE_CONNECTED) {
      SyncWatchEvent relatedSyncWatchEvent =
          SyncWatchEventService.fetchSyncWatchEvent(
              SyncWatchEvent.EVENT_TYPE_DELETE,
              syncWatchEvent.getFilePathName(),
              syncWatchEvent.getTimestamp());

      if (relatedSyncWatchEvent != null) {
        _processedSyncWatchEventIds.add(relatedSyncWatchEvent.getSyncWatchEventId());
      }
    }
  }