diff options
Diffstat (limited to 'src/main/src/SSSync.java')
-rw-r--r-- | src/main/src/SSSync.java | 208 |
1 files changed, 208 insertions, 0 deletions
diff --git a/src/main/src/SSSync.java b/src/main/src/SSSync.java new file mode 100644 index 0000000..422c31e --- /dev/null +++ b/src/main/src/SSSync.java @@ -0,0 +1,208 @@ +/* + * SSSync, a Simple and Stupid Synchronizer for data with multi-valued attributes + * Copyright (C) 2014 Ludovic Pouzenc <ludovic@pouzenc.fr> + * + * This file is part of SSSync. + * + * SSSync 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. + * + * SSSync 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 SSSync. If not, see <http://www.gnu.org/licenses/> + */ + +import java.io.IOException; +import java.util.List; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import org.apache.log4j.Level; +import org.apache.log4j.Logger; +import org.apache.log4j.PropertyConfigurator; + +import conf.ConfigConnectionsBean; +import conf.ConfigGlobalsBean; +import conf.ConfigRootBean; +import conf.SSSyncConfParser; +import conf.SSSyncConnectionsFactory; +import conf.SSSyncTasksFactory; +import data.io.ConnectionsHolder; + +import sync.BasicSyncTask; +import utils.JVMStatsDumper; + +/** + * Main class for Simple and Stupid Sync'er + * + * @author lpouzenc + */ +public class SSSync { + private static final Logger logger = Logger.getLogger(SSSync.class.getName()); + + private static final String LOG_PROPERTIES_FILE = "conf/log4j.properties"; + private static final String CONFIG_MAIN_FILE = "conf/sssync.yaml"; + private static final String CONFIG_CONN_FILE = "conf/connections.yaml"; + + private static final int ERR_SUCCESS = 0; + private static final int ERR_CONFIG_PARSE_ERROR = 1; + private static final int ERR_CONN_INIT_ERROR = 2; + private static final int ERR_TASK_INIT_ERROR = 3; + private static final int ERR_DRYRUN_FAILURE = 4; + private static final int ERR_REALRUN_FAILURE = 5; + //TODO private static final int ERR_MAXTIME_REACHED = 6; + + /** + * Main entry point. Takes care of cmdline parsing, config files interpretation, + * tasks setup and start. + * + * @param args + */ + public static void main(String[] args) { + // log4j setup (first thing to do) + PropertyConfigurator.configure(LOG_PROPERTIES_FILE); + logger.info("Program start (user: '" + System.getProperty("user.name") + + "', cwd: '" + System.getProperty("user.dir") + "')"); + + //TODO use cmdline args for config file path + String mainConfigFile = CONFIG_MAIN_FILE; + String connConfigFile = CONFIG_CONN_FILE; + + // Config parsing + ConfigRootBean confMain = null; + ConfigConnectionsBean confConn = null; + try { + confMain = SSSyncConfParser.loadMainConfig(mainConfigFile); + confConn = SSSyncConfParser.loadConnConfig(connConfigFile); + } catch (Exception e) { + logger.fatal("Exception while loading configuration", e); + end(ERR_CONFIG_PARSE_ERROR); + } + ConfigGlobalsBean confGlobals = confMain.getGlobals(); + + // Config dump if DEBUG level (or finer) + if ( !logger.getLevel().isGreaterOrEqual(Level.INFO) ) { + logger.debug("Current connection configuration :\n" + confConn); + logger.debug("Current main configuration :\n" + confMain); + } + + // Connections init + logger.info("Connections initialization"); + ConnectionsHolder connections = null; + try { + connections = SSSyncConnectionsFactory.setupConnections(confConn); + } catch (Exception e) { + logger.fatal("Exception while establishing connections", e); + end(ERR_CONN_INIT_ERROR); + } + + // Suggest garbage collector to forget our passwords since we are connected + confConn=null; + System.gc(); + JVMStatsDumper.logMemoryUsage(); + + + // Tasks init + logger.info("Tasks initialization"); + List<BasicSyncTask> tasks = null; + try { + tasks = SSSyncTasksFactory.setupTasks(connections, confMain); + } catch (Exception e) { + logger.fatal("Exception during tasks initialization", e); + end(ERR_TASK_INIT_ERROR); + } + + logger.info("Tasks are ready to start"); + JVMStatsDumper.logMemoryUsage(); + + + // Tasks first (dry) run + if ( ! SSSync.safeTaskRun(tasks, confGlobals.getMaxExecTime(), true) ) { + logger.error("Dry-run pass has shown problems, skipping real synchronization"); + end(ERR_DRYRUN_FAILURE); + } + + // Tasks second (real) run + if ( SSSync.safeTaskRun(tasks, confGlobals.getMaxExecTime(), false) ) { + logger.error("Real-run pass has shown problems, data could be messed up !"); + end(ERR_REALRUN_FAILURE); + } + + // Clean-up + try { + connections.close(); + } catch (IOException e) { + logger.info("Problem during connections closing"); + } + + // Normal exit + end(ERR_SUCCESS); + } + + /** + * Method to run safely a sequence of tasks within a given time period. + * In a separate thread, it runs all the tasks sequentially. + * + * @param list + * @param timeOutInMinute + * @return + * @throws ExecutionException + * @throws InterruptedException + */ + private static boolean safeTaskRun(List<BasicSyncTask> list, long timeOutInMinute, boolean dryRun) { + ExecutorService executor = Executors.newSingleThreadExecutor(); + List<Future<Boolean>> results; + boolean aborted = false; + + logger.info("Starting " + (dryRun?"dry-run":"real-run") + " synchronization pass"); + + for ( BasicSyncTask t : list ) { + t.setDryRun(dryRun); + } + + try { + results = executor.invokeAll(list, timeOutInMinute, TimeUnit.MINUTES); + // Join all tasks, seeking for an unsuccessful execution + for (Future<Boolean> r: results) { + if ( ! r.get() ) { + aborted = true; + } + } + } catch (CancellationException e) { + logger.fatal("Global maximum execution time exhausted, aborting tasks !"); + aborted = true; + } catch (InterruptedException e) { + logger.fatal("Worker thread for task execution was interrupted", e); + aborted = true; + } catch (ExecutionException e) { + logger.error("Exception during tasks execution", e.getCause()); + aborted = true; + } + + JVMStatsDumper.logMemoryUsage(); + executor.shutdown(); + + return !aborted; + } + + /** + * Helper function to always log the end of program + * @param result + */ + private static void end(int result) { + JVMStatsDumper.logGCStats(); + logger.info("Program end (result code: " + result + ")"); + System.exit(result); + } + +} |