Package org.syncany.operations.daemon

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

/*
* 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.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.syncany.config.ConfigException;
import org.syncany.config.DaemonConfigHelper;
import org.syncany.config.LocalEventBus;
import org.syncany.config.UserConfig;
import org.syncany.config.to.DaemonConfigTO;
import org.syncany.config.to.PortTO;
import org.syncany.config.to.UserTO;
import org.syncany.crypto.CipherUtil;
import org.syncany.operations.Operation;
import org.syncany.operations.OperationResult;
import org.syncany.operations.daemon.ControlServer.ControlCommand;
import org.syncany.operations.daemon.messages.ControlManagementRequest;
import org.syncany.operations.daemon.messages.ControlManagementResponse;
import org.syncany.operations.watch.WatchOperation;
import org.syncany.util.PidFileUtil;

import com.google.common.eventbus.Subscribe;

/**
* This operation is the central part of the daemon. It can manage many different
* {@link WatchOperation}s and exposes a web socket server to control and query the
* daemon. It furthermore offers a file-based control server to stop and reload the
* daemon.
*
* <p>When started via {@link #execute()}, the operation starts the following core
* components:
*
* <ul>
<li>The {@link WatchServer} starts a {@link WatchOperation} for every
*      folder registered in the <tt>daemon.xml</tt> file. It can be reloaded via
*      the <tt>syd reload</tt> command.</li>
<li>The {@link WebServer} starts a websocket and allows clients
*      (e.g. GUI, Web) to control the daemon (if authenticated).
*      TODO [medium] This is not yet implemented!</li>
<li>The {@link ControlServer} creates and watches the daemon control file
*      which allows the <tt>syd</tt> shell/batch script to write reload/shutdown
*      commands.</li> 
* </ul>
*
* @author Vincent Wiencek <vwiencek@gmail.com>
* @author Philipp C. Heckel <philipp.heckel@gmail.com>
* @author Pim Otte
*/
public class DaemonOperation extends Operation
  private static final Logger logger = Logger.getLogger(DaemonOperation.class.getSimpleName())
  public static final String PID_FILE = "daemon.pid";

  private File pidFile;
 
  private WebServer webServer;
  private WatchServer watchServer;
  private ControlServer controlServer;
  private LocalEventBus eventBus;
  private DaemonConfigTO daemonConfig;
  private PortTO portTO;

  public DaemonOperation() {
    super(null);   
    this.pidFile = new File(UserConfig.getUserConfigDir(), PID_FILE);   
  }

  @Override
  public OperationResult execute() throws Exception {   
    logger.log(Level.INFO, "Starting daemon operation ...");
   
    startOperation();
    return null;
  }

  private void startOperation() throws Exception {
    if (PidFileUtil.isProcessRunning(pidFile)) {
      throw new ServiceAlreadyStartedException("Syncany daemon already running.");
    }
   
    PidFileUtil.createPidFile(pidFile);
   
    initEventBus();   
    loadOrCreateConfig();
   
    startWebServer();
    startWatchServer();
   
    enterControlLoop(); // This blocks until SHUTDOWN is received!
  }

  @Subscribe
  public void onControlCommand(ControlCommand controlCommand) {
    switch (controlCommand) {
    case SHUTDOWN:
      logger.log(Level.INFO, "SHUTDOWN requested.");
      stopOperation();
      break;
     
    case RELOAD:
      logger.log(Level.INFO, "RELOAD requested.");
      reloadOperation();
      break;
    }
  } 

  @Subscribe
  public void onControlManagementRequest(ControlManagementRequest controlRequest) {
    onControlCommand(controlRequest.getControlCommand());
    eventBus.post(new ControlManagementResponse(200, controlRequest.getId(), "Command executed."));   
  }   
 
  // General initialization functions. These create the EventBus and control loop. 
 
  private void initEventBus() {
    eventBus = LocalEventBus.getInstance();
    eventBus.register(this);
  }

  private void enterControlLoop() throws IOException, ServiceAlreadyStartedException {
    logger.log(Level.INFO, "Starting daemon control server ...");

    controlServer = new ControlServer();
    controlServer.enterLoop(); // This blocks!
  }

  // General stopping and reloading functions

  private void stopOperation() {
    stopWebServer();
    stopWatchServer();
  }
 
  private void reloadOperation() {
    loadOrCreateConfig();   
    watchServer.reload(daemonConfig);
  }
 
  // Config related functions. Used on starting and reloading.
 
  private void loadOrCreateConfig() {
    try {
      File daemonConfigFile = new File(UserConfig.getUserConfigDir(), UserConfig.DAEMON_FILE);
      File daemonConfigFileExample = new File(UserConfig.getUserConfigDir(), UserConfig.DAEMON_EXAMPLE_FILE);
     
      if (daemonConfigFile.exists()) {
        daemonConfig = DaemonConfigTO.load(daemonConfigFile);
      }
      else {
        // Write example config to daemon-example.xml, and default config to daemon.xml
        DaemonConfigHelper.createAndWriteExampleDaemonConfig(daemonConfigFileExample);               
        daemonConfig = DaemonConfigHelper.createAndWriteDefaultDaemonConfig(daemonConfigFile);
      }
     
      // Add user and password for access from the CLI
      if (daemonConfig.getPortTO() == null && portTO == null) {
        // Access info has not been created yet, generate new user-password pair
        String accessToken = CipherUtil.createRandomAlphabeticString(20);
       
        UserTO cliUser = new UserTO();
        cliUser.setUsername(UserConfig.USER_CLI);
        cliUser.setPassword(accessToken);
       
        portTO = new PortTO();
       
        portTO.setPort(daemonConfig.getWebServer().getBindPort());
        portTO.setUser(cliUser);
       
        daemonConfig.setPortTO(portTO);
      }
      else if (daemonConfig.getPortTO() == null) {
        // Access info is not included in the daemon config, but exists. Happens when reloading.
        // We reload the information about the port, but keep the access token the same.
       
        portTO.setPort(daemonConfig.getWebServer().getBindPort());
        daemonConfig.setPortTO(portTO);
      }
    }
    catch (Exception e) {
      logger.log(Level.WARNING, "Cannot (re-)load config. Exception thrown.", e);
    }
  }   

  // Web server starting and stopping functions
 
  private void startWebServer() throws Exception {
    if (daemonConfig.getWebServer().isEnabled()) {
      logger.log(Level.INFO, "Starting web server ...");

      webServer = new WebServer(daemonConfig);
      webServer.start();
    }
    else {
      logger.log(Level.INFO, "Not starting web server (disabled in confi)");
    }
  }
 
  private void stopWebServer() {
    if (webServer != null) {
      logger.log(Level.INFO, "Stopping web server ...");
      webServer.stop();
    }
    else {
      logger.log(Level.INFO, "Not stopping web server (not running)");     
    }
  }
 
  // Watch server starting and stopping functions
 
  private void startWatchServer() throws ConfigException {
    logger.log(Level.INFO, "Starting watch server ...");

    watchServer = new WatchServer();
    watchServer.start(daemonConfig);
  }

  private void stopWatchServer() {
    logger.log(Level.INFO, "Stopping watch server ...");
    watchServer.stop();
  }
}
TOP

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

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.