summaryrefslogtreecommitdiff
path: root/src/main/src/SSSync.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/src/SSSync.java')
-rw-r--r--src/main/src/SSSync.java208
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);
+ }
+
+}