Skip to content

Commit

Permalink
Automatic backup -> config json file (Issue #5329)
Browse files Browse the repository at this point in the history
  • Loading branch information
lvca committed Nov 18, 2015
1 parent a8f0c47 commit 6a2490f
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 30 deletions.
19 changes: 19 additions & 0 deletions core/src/main/java/com/orientechnologies/common/io/OIOUtils.java
Expand Up @@ -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;
Expand Down
Expand Up @@ -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;
Expand All @@ -58,6 +59,8 @@
*/
public class OAutomaticBackup extends OServerPluginAbstract {

private ODocument configuration;

public enum VARIABLES {
DBNAME, DATE
}
Expand All @@ -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;
Expand All @@ -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 <v2.2: CONVERT ALL SETTINGS IN JSON
} else if (param.name.equalsIgnoreCase("enabled")) {
configuration.field("enabled", Boolean.parseBoolean(param.value));
} else if (param.name.equalsIgnoreCase("delay"))
delay = OIOUtils.getTimeAsMillisecs(param.value);
configuration.field("delay", param.value);
else if (param.name.equalsIgnoreCase("firstTime")) {
try {
firstTime = OIOUtils.getTodayWithTime(param.value);
if (firstTime.before(new Date())) {
Calendar cal = Calendar.getInstance();
cal.setTime(firstTime);
cal.add(Calendar.DAY_OF_MONTH, 1);
firstTime = cal.getTime();
}
} catch (ParseException e) {
throw OException
.wrapException(new OConfigurationException("Parameter 'firstTime' has invalid format, expected: HH:mm:ss"), e);
}
configuration.field("firstTime", param.value);
} else if (param.name.equalsIgnoreCase("target.directory"))
targetDirectory = param.value;
configuration.field("targetDirectory", param.value);
else if (param.name.equalsIgnoreCase("db.include") && param.value.trim().length() > 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("/"))
Expand Down Expand Up @@ -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 (<v2.2) TO JSON FILE
try {
f.getParentFile().mkdirs();
f.createNewFile();
OIOUtils.writeFile(f, configuration.toJSON());
} catch (IOException e) {
OException
.wrapException(
new OConfigurationException(
"Cannot create Automatic backup configuration file '" + configFile + "'. Automatic Backup will be disabled"),
e);
}
}

// PARSE THE JSON FILE
for (String settingName : configuration.fieldNames()) {
final Object settingValue = configuration.field(settingName);
final String settingValueAsString = settingValue != null ? settingValue.toString() : null;

if (settingName.equalsIgnoreCase("enabled")) {
if (!(Boolean) settingValue)
// DISABLE IT
return;
} else if (settingName.equalsIgnoreCase("delay"))
delay = OIOUtils.getTimeAsMillisecs(settingValue);
else if (settingName.equalsIgnoreCase("firstTime")) {
try {
firstTime = OIOUtils.getTodayWithTime(settingValueAsString);
if (firstTime.before(new Date())) {
Calendar cal = Calendar.getInstance();
cal.setTime(firstTime);
cal.add(Calendar.DAY_OF_MONTH, 1);
firstTime = cal.getTime();
}
} catch (ParseException e) {
throw OException
.wrapException(new OConfigurationException("Parameter 'firstTime' has invalid format, expected: HH:mm:ss"), e);
}
} else if (settingName.equalsIgnoreCase("targetDirectory"))
targetDirectory = settingValueAsString;
else if (settingName.equalsIgnoreCase("dbInclude") && settingValueAsString.trim().length() > 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) {
Expand All @@ -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) {
Expand Down
@@ -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;
Expand Down Expand Up @@ -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<String, String> getAvailableStorageNames() {
Expand All @@ -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();
Expand All @@ -67,13 +79,20 @@ 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();
}

@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[] {
Expand Down Expand Up @@ -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[] {
Expand Down Expand Up @@ -137,14 +159,17 @@ 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[] {
new OServerParameterConfiguration("firstTime",
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);

Expand All @@ -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);
}
Expand Down

0 comments on commit 6a2490f

Please sign in to comment.