Package org.syncany.operations.daemon

Source Code of org.syncany.operations.daemon.WatchServer

/*
* Syncany, www.syncany.org
* Copyright (C) 2011-2014 Philipp C. Heckel <philipp.heckel@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
package org.syncany.operations.daemon;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.syncany.config.Config;
import org.syncany.config.ConfigException;
import org.syncany.config.ConfigHelper;
import org.syncany.config.DaemonConfigHelper;
import org.syncany.config.LocalEventBus;
import org.syncany.config.to.DaemonConfigTO;
import org.syncany.config.to.FolderTO;
import org.syncany.operations.ChangeSet;
import org.syncany.operations.daemon.Watch.SyncStatus;
import org.syncany.operations.daemon.messages.AddWatchManagementRequest;
import org.syncany.operations.daemon.messages.AddWatchManagementResponse;
import org.syncany.operations.daemon.messages.BadRequestResponse;
import org.syncany.operations.daemon.messages.DaemonReloadedExternalEvent;
import org.syncany.operations.daemon.messages.DownEndSyncExternalEvent;
import org.syncany.operations.daemon.messages.ListWatchesManagementRequest;
import org.syncany.operations.daemon.messages.ListWatchesManagementResponse;
import org.syncany.operations.daemon.messages.api.FolderRequest;
import org.syncany.operations.watch.WatchOperation;
import org.syncany.operations.watch.WatchOperationOptions;
import org.syncany.util.StringUtil;

import com.google.common.collect.Maps;
import com.google.common.eventbus.Subscribe;

/**
* The watch server can manage many different {@link WatchOperation}s. When started
* with {@link #start()} or {@link #reload()}, it first reads the daemon configuration file
* and then runs new threads for each configured Syncany folder. Invalid or non-existing folders
* are ignored.
*
* @author Philipp C. Heckel <philipp.heckel@gmail.com>
*/
public class WatchServer
  private static final Logger logger = Logger.getLogger(WatchServer.class.getSimpleName());
 
  private DaemonConfigTO daemonConfig; 
  private Map<File, WatchRunner> watchOperations;
  private LocalEventBus eventBus;
 
  public WatchServer() {
    this.daemonConfig = null;
    this.watchOperations = new TreeMap<File, WatchRunner>();
   
    this.eventBus = LocalEventBus.getInstance();
    this.eventBus.register(this);
  }
 
  public void start(DaemonConfigTO daemonConfigTO) {   
    reload(daemonConfigTO);
  }
 
  public void reload(DaemonConfigTO daemonConfigTO) {
    logger.log(Level.INFO, "Starting/reloading watch server ... ");
   
    // Update config
    this.daemonConfig = daemonConfigTO;
   
    // Restart threads
    try {
      Map<File, FolderTO> watchedFolders = getFolderMap(daemonConfigTO.getFolders());
     
      stopAllWatchOperations();
      startWatchOperations(watchedFolders);
     
      fireDaemonReloadedEvent();
    }
    catch (Exception e) {
      logger.log(Level.WARNING, "Cannot (re-)load config. Exception thrown.", e);
    }
  }

  public void stop() {
    logger.log(Level.INFO, "Stopping watch server ...  ");   
    Map<File, WatchRunner> copyOfWatchOperations = Maps.newHashMap(watchOperations);
   
    for (Map.Entry<File, WatchRunner> folderEntry : copyOfWatchOperations.entrySet()) {
      File localDir = folderEntry.getKey();
      WatchRunner watchOperationThread = folderEntry.getValue();
         
      logger.log(Level.INFO, "- Stopping watch operation at " + localDir + " ...");
      watchOperationThread.stop();
     
      watchOperations.remove(localDir);
    }
  }

  private void startWatchOperations(Map<File, FolderTO> newWatchedFolderTOs) throws ConfigException, ServiceAlreadyStartedException {
    for (Map.Entry<File, FolderTO> folderEntry : newWatchedFolderTOs.entrySet()) {
      File localDir = folderEntry.getKey();

      try
        Config watchConfig = ConfigHelper.loadConfig(localDir);
       
        if (watchConfig != null) {
          logger.log(Level.INFO, "- Starting watch operation at " + localDir + " ...");         
         
          WatchOperationOptions watchOptions = folderEntry.getValue().getWatchOptions();
         
          if (watchOptions == null) {
            watchOptions = new WatchOperationOptions();
          }
         
          WatchRunner watchRunner = new WatchRunner(watchConfig, watchOptions, daemonConfig.getPortTO())
          watchRunner.start();
 
          watchOperations.put(localDir, watchRunner);
        }
        else {
          logger.log(Level.INFO, "- CANNOT start watch, because no config found at " + localDir + " ...");                   
        }
      }
      catch (Exception e) {
        logger.log(Level.SEVERE, "  + Cannot start watch operation at " + localDir + ". IGNORING.", e);
      }
    }
  }
 
  /**
   * Stops all watchOperations and verifies if
   * they actually have stopped.
   */
  private void stopAllWatchOperations() {
    for (File localDir : watchOperations.keySet()) {
      WatchRunner watchOperationThread = watchOperations.get(localDir);

      logger.log(Level.INFO, "- Stopping watch operation at " + localDir + " ...");
      watchOperationThread.stop();
    }
   
    // Check if watch operations actually have stopped.
    while (watchOperations.keySet().size() > 0) {
      Map<File, WatchRunner> watchOperationsCopy = new TreeMap<File, WatchRunner>(watchOperations);
     
      for (File localDir : watchOperationsCopy.keySet()) {
        WatchRunner watchOperationThread = watchOperationsCopy.get(localDir);
       
        if (watchOperationThread.hasStopped()) {
          logger.log(Level.INFO, "- Watch operation at " + localDir + " has stopped");
          watchOperations.remove(localDir);
        }
      }
    }
  }
 
  private Map<File, FolderTO> getFolderMap(List<FolderTO> watchedFolders) {
    Map<File, FolderTO> watchedFolderTOs = new TreeMap<File, FolderTO>();
   
    for (FolderTO folderTO : watchedFolders) {
      if (folderTO.isEnabled()) {
        watchedFolderTOs.put(new File(folderTO.getPath()), folderTO);
      }
    }
   
    return watchedFolderTOs;
  }

  private void fireDaemonReloadedEvent() {   
    logger.log(Level.INFO, "Firing daemon-reloaded event ...");   
    eventBus.post(new DaemonReloadedExternalEvent());
  }
 
  @Subscribe
  public void onFolderRequestReceived(FolderRequest folderRequest) {
    File rootFolder = new File(folderRequest.getRoot());
   
    if (!watchOperations.containsKey(rootFolder)) {
      eventBus.post(new BadRequestResponse(folderRequest.getId(), "Unknown root folder."));
    }
  }
 
  @Subscribe
  public void onListWatchesRequestReceived(ListWatchesManagementRequest request) {
    ArrayList<Watch> watchList = new ArrayList<>();
   
    for (File watchFolder : watchOperations.keySet()) {
      boolean syncRunning = watchOperations.get(watchFolder).isSyncRunning();
      SyncStatus syncStatus = (syncRunning) ? SyncStatus.SYNCING : SyncStatus.IN_SYNC;
     
      watchList.add(new Watch(watchFolder, syncStatus));
    }
   
    eventBus.post(new ListWatchesManagementResponse(request.getId(), watchList));
  }
 
  @Subscribe
  public void onAddWatchRequestReceived(AddWatchManagementRequest request) {
    File rootFolder = request.getWatch();
   
    if (watchOperations.containsKey(rootFolder)) {
      eventBus.post(new AddWatchManagementResponse(AddWatchManagementResponse.ERR_ALREADY_EXISTS, request.getId(), "Watch already exists."));
    }
    else {     
      try {
        boolean folderAdded = DaemonConfigHelper.addToDaemonConfig(rootFolder);
       
        if (folderAdded) {
          eventBus.post(new AddWatchManagementResponse(AddWatchManagementResponse.OKAY, request.getId(), "Successfully added."));       
        }
        else {
          eventBus.post(new AddWatchManagementResponse(AddWatchManagementResponse.ERR_ALREADY_EXISTS, request.getId(), "Watch already exists (inactive/disabled)."));
        }
      }
      catch (ConfigException e) {
        logger.log(Level.WARNING, "Error adding watch to daemon config.", e);
        eventBus.post(new AddWatchManagementResponse(AddWatchManagementResponse.ERR_OTHER, request.getId(), "Error adding to config: " + e.getMessage()));
      }
    }       
  }
 
  @Subscribe
  public void onPostDownOperation(DownEndSyncExternalEvent downEndSyncEvent) {
    if (daemonConfig.getHooks() != null) {
      String runAfterSyncCommand = daemonConfig.getHooks().getRunAfterDownCommand();
     
      if (runAfterSyncCommand != null) {
        ChangeSet changeSet = downEndSyncEvent.getChanges();
       
        List<String> changeMessageParts = new ArrayList<>();
       
        if (changeSet.getNewFiles().size() > 0) {
          changeMessageParts.add(changeSet.getNewFiles().size() + " file(s) added");
        }
       
        if (changeSet.getChangedFiles().size() > 0) {
          changeMessageParts.add(changeSet.getChangedFiles().size() + " file(s) changed");
        }
       
        if (changeSet.getDeletedFiles().size() > 0) {
          changeMessageParts.add(changeSet.getDeletedFiles().size() + " file(s) deleted");
        }
       
        String changedMessage = StringUtil.join(changeMessageParts, ", ");
       
        String escapedSubject = changedMessage.replace("\"", "\\\"");     
        runAfterSyncCommand = runAfterSyncCommand.replace("%subject", escapedSubject);
             
        try {
          logger.log(Level.INFO, "Running command: " + runAfterSyncCommand);
         
          List<String> commandArgsList = StringUtil.splitCommandLineArgs(runAfterSyncCommand);
          String[] commandArgs = commandArgsList.toArray(new String[0]);
           
          Runtime.getRuntime().exec(commandArgs);
        }
        catch (Exception e) {
          logger.log(Level.WARNING, "Cannot run sync after command: " + runAfterSyncCommand, e);
        }
      }
    }
  } 
}
TOP

Related Classes of org.syncany.operations.daemon.WatchServer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.