diff --git a/core/src/main/java/com/orientechnologies/common/io/OIOUtils.java b/core/src/main/java/com/orientechnologies/common/io/OIOUtils.java index 2ee24527b95..f8a78da1e04 100755 --- a/core/src/main/java/com/orientechnologies/common/io/OIOUtils.java +++ b/core/src/main/java/com/orientechnologies/common/io/OIOUtils.java @@ -171,6 +171,25 @@ public static String readStreamAsString(final InputStream iStream, Charset iChar } + public static void writeFile(final File iFile, final String iContent) throws IOException { + final FileOutputStream fos = new FileOutputStream(iFile); + try { + final OutputStreamWriter os = new OutputStreamWriter(fos); + try { + final BufferedWriter writer = new BufferedWriter(os); + try { + writer.write(iContent); + } finally { + writer.close(); + } + } finally { + os.close(); + } + } finally { + fos.close(); + } + } + public static long copyStream(final InputStream in, final OutputStream out, long iMax) throws IOException { if (iMax < 0) iMax = Long.MAX_VALUE; diff --git a/server/src/main/java/com/orientechnologies/orient/server/handler/OAutomaticBackup.java b/server/src/main/java/com/orientechnologies/orient/server/handler/OAutomaticBackup.java index 1092eb857f1..d44990bbc02 100755 --- a/server/src/main/java/com/orientechnologies/orient/server/handler/OAutomaticBackup.java +++ b/server/src/main/java/com/orientechnologies/orient/server/handler/OAutomaticBackup.java @@ -33,6 +33,7 @@ import com.orientechnologies.orient.core.db.tool.ODatabaseExport; import com.orientechnologies.orient.core.exception.OConfigurationException; import com.orientechnologies.orient.core.metadata.security.OSecurityNull; +import com.orientechnologies.orient.core.record.impl.ODocument; import com.orientechnologies.orient.server.OServer; import com.orientechnologies.orient.server.config.OServerParameterConfiguration; import com.orientechnologies.orient.server.plugin.OServerPluginAbstract; @@ -58,6 +59,8 @@ */ public class OAutomaticBackup extends OServerPluginAbstract { + private ODocument configuration; + public enum VARIABLES { DBNAME, DATE } @@ -66,6 +69,7 @@ public enum MODE { FULL_BACKUP, INCREMENTAL_BACKUP, EXPORT } + private String configFile = "${ORIENTDB_HOME}/config/backup.json"; private Date firstTime = null; private long delay = -1; private int bufferSize = 1048576; @@ -83,46 +87,46 @@ public enum MODE { public void config(final OServer iServer, final OServerParameterConfiguration[] iParams) { serverInstance = iServer; + configuration = new ODocument(); + for (OServerParameterConfiguration param : iParams) { - if (param.name.equalsIgnoreCase("enabled")) { - if (!Boolean.parseBoolean(param.value)) - // DISABLE IT - return; + if (param.name.equalsIgnoreCase("config") && param.value.trim().length() > 0) { + configFile = param.value.trim(); + + final File f = new File(OSystemVariableResolver.resolveSystemVariables(configFile)); + if (!f.exists()) + throw new OConfigurationException( + "Automatic backup configuration file '" + configFile + "' not found. Automatic Backup will be disabled"); + break; + + // LEGACY 0) - for (String db : param.value.split(",")) - includeDatabases.add(db); + configuration.field("dbInclude", param.value); else if (param.name.equalsIgnoreCase("db.exclude") && param.value.trim().length() > 0) - for (String db : param.value.split(",")) - excludeDatabases.add(db); + configuration.field("dbExclude", param.value); else if (param.name.equalsIgnoreCase("target.fileName")) - targetFileName = param.value; + configuration.field("targetFileName", param.value); else if (param.name.equalsIgnoreCase("bufferSize")) - bufferSize = Integer.parseInt(param.value); + configuration.field("bufferSize", param.value); else if (param.name.equalsIgnoreCase("compressionLevel")) - compressionLevel = Integer.parseInt(param.value); + configuration.field("compressionLevel", Integer.parseInt(param.value)); else if (param.name.equalsIgnoreCase("mode")) - mode = MODE.valueOf(param.value.toUpperCase()); + configuration.field("mode", param.value); else if (param.name.equalsIgnoreCase("exportOptions")) - exportOptions = param.value; + configuration.field("exportOptions", param.value); } + // LOAD CFG FROM JSON FILE. THIS FILE, IF SPECIFIED, OVERWRITE DEFAULT AND XML SETTINGS + configure(); + if (delay <= 0) throw new OConfigurationException("Cannot find mandatory parameter 'delay'"); if (!targetDirectory.endsWith("/")) @@ -218,16 +222,92 @@ public void run() { Orient.instance().scheduleTask(timerTask, firstTime, delay); } + private void configure() { + final File f = new File(OSystemVariableResolver.resolveSystemVariables(configFile)); + if (f.exists()) { + // READ THE FILE + try { + final String configurationContent = OIOUtils.readFileAsString(f); + configuration = new ODocument().fromJSON(configurationContent); + } catch (IOException e) { + OException.wrapException(new OConfigurationException( + "Cannot load Automatic backup configuration file '" + configFile + "'. Automatic Backup will be disabled"), e); + } + + } else { + // AUTO CONVERT XML CONFIGURATION ( 0) + for (String db : settingValueAsString.split(",")) + includeDatabases.add(db); + else if (settingName.equalsIgnoreCase("dbExclude") && settingValueAsString.trim().length() > 0) + for (String db : settingValueAsString.split(",")) + excludeDatabases.add(db); + else if (settingName.equalsIgnoreCase("targetFileName")) + targetFileName = settingValueAsString; + else if (settingName.equalsIgnoreCase("bufferSize")) + bufferSize = (Integer) settingValue; + else if (settingName.equalsIgnoreCase("compressionLevel")) + compressionLevel = (Integer) settingValue; + else if (settingName.equalsIgnoreCase("mode")) + mode = MODE.valueOf(settingValueAsString.toUpperCase()); + else if (settingName.equalsIgnoreCase("exportOptions")) + exportOptions = settingValueAsString; + } + } + protected void incrementalBackupDatabase(final String dbURL, String iPath, final ODatabaseDocumentTx db) throws IOException { // APPEND DB NAME TO THE DIRECTORY NAME if (!iPath.endsWith("/")) iPath += "/"; iPath += db.getName(); + OLogManager.instance().info(this, "AutomaticBackup: executing incremental backup of database '%s' to %s", dbURL, iPath); + db.incrementalBackup(iPath); } protected void fullBackupDatabase(final String dbURL, final String iPath, final ODatabaseDocumentTx db) throws IOException { + OLogManager.instance().info(this, "AutomaticBackup: executing full backup of database '%s' to %s", dbURL, iPath); + db.backup(new FileOutputStream(iPath), null, null, new OCommandOutputListener() { @Override public void onMessage(String iText) { @@ -238,6 +318,8 @@ public void onMessage(String iText) { protected void exportDatabase(final String dbURL, final String iPath, final ODatabaseDocumentTx db) throws IOException { + OLogManager.instance().info(this, "AutomaticBackup: executing export of database '%s' to %s", dbURL, iPath); + final ODatabaseExport exp = new ODatabaseExport(db, iPath, new OCommandOutputListener() { @Override public void onMessage(String iText) { diff --git a/server/src/test/java/com/orientechnologies/orient/server/handler/AutomaticBackupTest.java b/server/src/test/java/com/orientechnologies/orient/server/handler/AutomaticBackupTest.java index d2f3134ac10..944901e62ec 100644 --- a/server/src/test/java/com/orientechnologies/orient/server/handler/AutomaticBackupTest.java +++ b/server/src/test/java/com/orientechnologies/orient/server/handler/AutomaticBackupTest.java @@ -1,6 +1,7 @@ package com.orientechnologies.orient.server.handler; import com.orientechnologies.common.io.OFileUtils; +import com.orientechnologies.common.parser.OSystemVariableResolver; import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; import com.orientechnologies.orient.core.db.tool.ODatabaseImport; import com.orientechnologies.orient.core.record.impl.ODocument; @@ -33,11 +34,18 @@ public class AutomaticBackupTest { private final static String BACKUPDIR = "target/backup"; private static final String URL = "plocal:target/" + DBNAME; private static final String URL2 = "plocal:target/" + DBNAME + "2"; + private final String tempDirectory; private ODatabaseDocumentTx database; private final OServer server; public AutomaticBackupTest() throws ClassNotFoundException, MalformedObjectNameException, InstanceAlreadyExistsException, - NotCompliantMBeanException, MBeanRegistrationException { + NotCompliantMBeanException, MBeanRegistrationException, IOException { + + // SET THE ORIENTDB_HOME DIRECTORY TO CHECK JSON FILE CREATION + final File f = File.createTempFile("testBackup", ".test"); + tempDirectory = f.getParent(); + System.setProperty("ORIENTDB_HOME", tempDirectory); + server = new OServer() { @Override public Map getAvailableStorageNames() { @@ -56,6 +64,10 @@ public static final void clean() { @Before public void init() { + final File f = new File(OSystemVariableResolver.resolveSystemVariables("${ORIENTDB_HOME}/config/backup.json")); + if (f.exists()) + f.delete(); + database = new ODatabaseDocumentTx(URL); if (database.exists()) database.open("admin", "admin").drop(); @@ -67,6 +79,10 @@ public void init() { @After public void deinit() { + Assert.assertTrue(new File(tempDirectory + "/config/backup.json").exists()); + + new File(tempDirectory + "/config/backup.json").delete(); + database.activateOnCurrentThread(); database.drop(); } @@ -74,6 +90,9 @@ public void deinit() { @Test public void testFullBackup() throws IOException, ClassNotFoundException, MalformedObjectNameException, InstanceAlreadyExistsException, NotCompliantMBeanException, MBeanRegistrationException { + if (new File(BACKUPDIR + "/fullBackup.zip").exists()) + new File(BACKUPDIR + "/fullBackup.zip").delete(); + final OAutomaticBackup aBackup = new OAutomaticBackup(); final OServerParameterConfiguration[] config = new OServerParameterConfiguration[] { @@ -106,6 +125,9 @@ public void testFullBackup() throws IOException, ClassNotFoundException, Malform @Test public void testIncrementalBackup() throws IOException, ClassNotFoundException, MalformedObjectNameException, InstanceAlreadyExistsException, NotCompliantMBeanException, MBeanRegistrationException { + if (new File(BACKUPDIR + "/" + DBNAME).exists()) + OFileUtils.deleteRecursively(new File(BACKUPDIR + "/" + DBNAME)); + final OAutomaticBackup aBackup = new OAutomaticBackup(); final OServerParameterConfiguration[] config = new OServerParameterConfiguration[] { @@ -137,6 +159,9 @@ public void testIncrementalBackup() throws IOException, ClassNotFoundException, @Test public void testExport() throws IOException, ClassNotFoundException, MalformedObjectNameException, InstanceAlreadyExistsException, NotCompliantMBeanException, MBeanRegistrationException { + if (new File(BACKUPDIR + "/fullExport.json.gz").exists()) + new File(BACKUPDIR + "/fullExport.json.gz").delete(); + final OAutomaticBackup aBackup = new OAutomaticBackup(); final OServerParameterConfiguration[] config = new OServerParameterConfiguration[] { @@ -144,7 +169,7 @@ public void testExport() throws IOException, ClassNotFoundException, MalformedOb new SimpleDateFormat("HH:mm:ss").format(new Date(System.currentTimeMillis() + 2000))), new OServerParameterConfiguration("delay", "1d"), new OServerParameterConfiguration("mode", "EXPORT"), new OServerParameterConfiguration("target.directory", BACKUPDIR), - new OServerParameterConfiguration("target.fileName", "fullBackup.json.gz") }; + new OServerParameterConfiguration("target.fileName", "fullExport.json.gz") }; aBackup.config(server, config); @@ -161,7 +186,7 @@ public void testExport() throws IOException, ClassNotFoundException, MalformedOb database2.open("admin", "admin").drop(); database2.create(); - new ODatabaseImport(database2, BACKUPDIR + "/" + "fullBackup.json.gz", null).importDatabase(); + new ODatabaseImport(database2, BACKUPDIR + "/fullExport.json.gz", null).importDatabase(); Assert.assertEquals(database2.countClass("TestBackup"), 1); }