diff --git a/engine/src/main/java/com/arcadedb/GlobalConfiguration.java b/engine/src/main/java/com/arcadedb/GlobalConfiguration.java index 663371f54b..7093897ce5 100644 --- a/engine/src/main/java/com/arcadedb/GlobalConfiguration.java +++ b/engine/src/main/java/com/arcadedb/GlobalConfiguration.java @@ -209,8 +209,8 @@ public Object call(final Object value) { ""), SERVER_DEFAULT_DATABASES("arcadedb.server.defaultDatabases", - "The default databases created when the server starts. The format is '([(:[:])[,]*])[{import:}][;]*'. Pay attention on using ';'" - + " to separate databases and ',' to separate credentials. Example: 'Universe[elon:musk:admin];Amiga[Jay:Miner,Jack:Tramiel]{import:/tmp/movies.tgz}'", + "The default databases created when the server starts. The format is '([(:[:])[,]*])[{import|restore:}][;]*'. Pay attention on using ';'" + + " to separate databases and ',' to separate credentials. The supported actions are 'import' and 'restore'. Example: 'Universe[elon:musk:admin];Amiga[Jay:Miner,Jack:Tramiel]{import:/tmp/movies.tgz}'", String.class, ""), // SERVER HTTP diff --git a/integration/src/main/java/com/arcadedb/integration/restore/Restore.java b/integration/src/main/java/com/arcadedb/integration/restore/Restore.java index ad3f7ae5f2..dad080152d 100644 --- a/integration/src/main/java/com/arcadedb/integration/restore/Restore.java +++ b/integration/src/main/java/com/arcadedb/integration/restore/Restore.java @@ -34,7 +34,7 @@ public Restore(final String[] args) { } public Restore(final String file, final String databaseURL) { - settings.file = file; + settings.url = file; settings.databaseURL = databaseURL; } @@ -52,7 +52,7 @@ public void restoreDatabase() { formatImplementation.restoreDatabase(); } catch (Exception e) { - throw new RestoreException("Error during restore of database from file '" + settings.file + "'", e); + throw new RestoreException("Error during restore of database from file '" + settings.url + "'", e); } } diff --git a/integration/src/main/java/com/arcadedb/integration/restore/RestoreSettings.java b/integration/src/main/java/com/arcadedb/integration/restore/RestoreSettings.java index fb29906caa..e9bb16d494 100644 --- a/integration/src/main/java/com/arcadedb/integration/restore/RestoreSettings.java +++ b/integration/src/main/java/com/arcadedb/integration/restore/RestoreSettings.java @@ -21,7 +21,7 @@ public class RestoreSettings { public String format = "full"; public String databaseURL; - public String file; + public String url; public boolean overwriteDestination = false; public int verboseLevel = 2; public final Map options = new HashMap<>(); @@ -37,17 +37,17 @@ protected void parseParameters(final String[] args) { if (format == null) throw new IllegalArgumentException("Missing backup format"); - if (file == null) + if (url == null) // ASSIGN DEFAULT FILENAME switch (format) { case "full": - file = "arcadedb-backup-%s.zip"; + url = "arcadedb-backup-%s.zip"; break; } - if (file == null) { + if (url == null) { final DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd-HHmmssSSS"); - file = String.format(file, dateFormat.format(System.currentTimeMillis())); + url = String.format(url, dateFormat.format(System.currentTimeMillis())); } } @@ -55,7 +55,7 @@ public int parseParameter(final String name, final String value) { if ("format".equals(name)) format = value.toLowerCase(); else if ("f".equals(name)) - file = value; + url = value; else if ("d".equals(name)) databaseURL = value; else if ("o".equals(name)) { diff --git a/integration/src/main/java/com/arcadedb/integration/restore/format/FullRestoreFormat.java b/integration/src/main/java/com/arcadedb/integration/restore/format/FullRestoreFormat.java index 91686b2044..e3db7ffab2 100644 --- a/integration/src/main/java/com/arcadedb/integration/restore/format/FullRestoreFormat.java +++ b/integration/src/main/java/com/arcadedb/integration/restore/format/FullRestoreFormat.java @@ -23,20 +23,29 @@ import com.arcadedb.utility.FileUtils; import java.io.*; +import java.net.*; import java.util.zip.*; public class FullRestoreFormat extends AbstractRestoreFormat { private final byte[] BUFFER = new byte[8192]; + private static class RestoreInputSource { + public final InputStream inputStream; + public final long fileSize; + + public RestoreInputSource(final InputStream inputStream, final long fileSize) { + this.inputStream = inputStream; + this.fileSize = fileSize; + } + } + public FullRestoreFormat(final DatabaseInternal database, final RestoreSettings settings, final ConsoleLogger logger) { super(database, settings, logger); } @Override public void restoreDatabase() throws Exception { - final File file = new File(settings.file); - if (!file.exists()) - throw new RestoreException(String.format("The backup file '%s' not exist", settings.file)); + final RestoreInputSource inputSource = openInputFile(); final File databaseDirectory = new File(settings.databaseURL); if (databaseDirectory.exists()) { @@ -49,12 +58,9 @@ public void restoreDatabase() throws Exception { if (!databaseDirectory.mkdirs()) throw new RestoreException(String.format("Error on restoring database: the database directory '%s' cannot be created", settings.databaseURL)); - logger.logLine(0, "Executing full restore of database from file '%s' to '%s'...", settings.file, settings.databaseURL); - - final File backupFile = new File(settings.file); - final long databaseCompressedSize = backupFile.length(); + logger.logLine(0, "Executing full restore of database from file '%s' to '%s'...", settings.url, settings.databaseURL); - try (ZipInputStream zipFile = new ZipInputStream(new FileInputStream(backupFile), DatabaseFactory.getDefaultCharset())) { + try (ZipInputStream zipFile = new ZipInputStream(inputSource.inputStream, DatabaseFactory.getDefaultCharset())) { final long beginTime = System.currentTimeMillis(); long databaseOrigSize = 0L; @@ -70,7 +76,7 @@ public void restoreDatabase() throws Exception { final long elapsedInSecs = (System.currentTimeMillis() - beginTime) / 1000; logger.logLine(0, "Full restore completed in %d seconds %s -> %s (%,d%% compression)", elapsedInSecs, FileUtils.getSizeAsString(databaseOrigSize), - FileUtils.getSizeAsString((databaseCompressedSize)), databaseOrigSize > 0 ? (databaseOrigSize - databaseCompressedSize) * 100 / databaseOrigSize : 0); + FileUtils.getSizeAsString((inputSource.fileSize)), databaseOrigSize > 0 ? (databaseOrigSize - inputSource.fileSize) * 100 / databaseOrigSize : 0); } } @@ -94,4 +100,25 @@ private long uncompressFile(final ZipInputStream inputFile, ZipEntry compressedF return origSize; } + + private RestoreInputSource openInputFile() throws IOException { + if (settings.url.startsWith("http://") || settings.url.startsWith("https://")) { + final HttpURLConnection connection = (HttpURLConnection) new URL(settings.url).openConnection(); + connection.setRequestMethod("GET"); + connection.setDoOutput(true); + connection.connect(); + + return new RestoreInputSource(connection.getInputStream(), 0); + } + + String path = settings.url; + if (path.startsWith("file://")) + path = path.substring("file://".length()); + + final File file = new File(path); + if (!file.exists()) + throw new RestoreException(String.format("The backup file '%s' not exist", settings.url)); + + return new RestoreInputSource(new FileInputStream(file), file.length()); + } } diff --git a/server/src/main/java/com/arcadedb/server/ArcadeDBServer.java b/server/src/main/java/com/arcadedb/server/ArcadeDBServer.java index 4bffbe9647..a39b3a729b 100644 --- a/server/src/main/java/com/arcadedb/server/ArcadeDBServer.java +++ b/server/src/main/java/com/arcadedb/server/ArcadeDBServer.java @@ -23,6 +23,7 @@ import com.arcadedb.database.DatabaseInternal; import com.arcadedb.database.EmbeddedDatabase; import com.arcadedb.exception.ConfigurationException; +import com.arcadedb.integration.restore.Restore; import com.arcadedb.log.LogManager; import com.arcadedb.query.QueryEngineManager; import com.arcadedb.server.ha.HAServer; @@ -388,8 +389,9 @@ private synchronized Database getDatabase(final String databaseName, final boole DatabaseInternal db = databases.get(databaseName); if (db == null || !db.isOpen()) { - final DatabaseFactory factory = new DatabaseFactory( - configuration.getValueAsString(GlobalConfiguration.SERVER_DATABASE_DIRECTORY) + "/" + databaseName).setAutoTransaction(true); + final String path = configuration.getValueAsString(GlobalConfiguration.SERVER_DATABASE_DIRECTORY) + "/" + databaseName; + + final DatabaseFactory factory = new DatabaseFactory(path).setAutoTransaction(true); factory.setSecurity(getSecurity()); @@ -437,54 +439,9 @@ private void loadDefaultDatabases() { final int credentialEnd = db.indexOf(']', credentialBegin); final String credentials = db.substring(credentialBegin + 1, credentialEnd); - final String[] credentialPairs = credentials.split(","); - for (String credential : credentialPairs) { - - final String[] credentialParts = credential.split(":"); - - if (credentialParts.length < 2) { - if (!security.existsUser(credential)) { - LogManager.instance() - .log(this, Level.WARNING, "Cannot create user '%s' to access database '%s' because the user does not exist", null, credential, dbName); - } - //FIXME: else if user exists, should we give him access to the dbName? - } else { - final String userName = credentialParts[0]; - final String userPassword = credentialParts[1]; - final String userRole = credentialParts.length > 2 ? credentialParts[2] : null; - - if (security.existsUser(userName)) { - // EXISTING USER: CHECK CREDENTIALS - try { - final ServerSecurityUser user = security.authenticate(userName, userPassword, dbName); - if (!user.getAuthorizedDatabases().contains(dbName)) { - // UPDATE DB LIST - user.addDatabase(dbName, new String[] { userRole }); - security.saveUsers(); - } - - } catch (ServerSecurityException e) { - LogManager.instance() - .log(this, Level.WARNING, "Cannot create database '%s' because the user '%s' already exists with a different password", null, dbName, - userName); - } - } else { - // CREATE A NEW USER - security.createUser(new JSONObject().put("name", userName)// - .put("password", security.encodePassword(userPassword))// - .put("databases", new JSONObject().put(dbName, new JSONArray()))); - } - } - } + parseCredentials(dbName, credentials); - Database database; - if (existsDatabase(dbName)) { - database = getDatabase(dbName); - } else { - // CREATE THE DATABASE - LogManager.instance().log(this, Level.INFO, "Creating default database '%s'...", null, dbName); - database = createDatabase(dbName); - } + Database database = existsDatabase(dbName) ? getDatabase(dbName) : null; if (credentialEnd < db.length() - 1 && db.charAt(credentialEnd + 1) == '{') { // PARSE IMPORTS @@ -501,7 +458,22 @@ private void loadDefaultDatabases() { final String commandParams = command.substring(commandSeparator + 1); switch (commandType) { + case "restore": + // DROP THE DATABASE BECAUSE THE RESTORE OPERATION WILL TAKE CARE OF CREATING A NEW DATABASE + if (database != null) { + database.drop(); + databases.remove(dbName); + } + new Restore(commandParams, configuration.getValueAsString(GlobalConfiguration.SERVER_DATABASE_DIRECTORY) + "/" + dbName).restoreDatabase(); + getDatabase(dbName); + break; + case "import": + if (database == null) { + // CREATE THE DATABASE + LogManager.instance().log(this, Level.INFO, "Creating default database '%s'...", null, dbName); + database = createDatabase(dbName); + } database.command("sql", "import database " + commandParams); break; @@ -509,6 +481,53 @@ private void loadDefaultDatabases() { LogManager.instance().log(this, Level.SEVERE, "Unsupported command %s in startup command: '%s'", null, commandType); } } + } else { + if (database == null) { + // CREATE THE DATABASE + LogManager.instance().log(this, Level.INFO, "Creating default database '%s'...", null, dbName); + createDatabase(dbName); + } + } + } + } + } + + private void parseCredentials(final String dbName, final String credentials) { + final String[] credentialPairs = credentials.split(","); + for (String credential : credentialPairs) { + + final String[] credentialParts = credential.split(":"); + + if (credentialParts.length < 2) { + if (!security.existsUser(credential)) { + LogManager.instance() + .log(this, Level.WARNING, "Cannot create user '%s' to access database '%s' because the user does not exist", null, credential, dbName); + } + //FIXME: else if user exists, should we give him access to the dbName? + } else { + final String userName = credentialParts[0]; + final String userPassword = credentialParts[1]; + final String userRole = credentialParts.length > 2 ? credentialParts[2] : null; + + if (security.existsUser(userName)) { + // EXISTING USER: CHECK CREDENTIALS + try { + final ServerSecurityUser user = security.authenticate(userName, userPassword, dbName); + if (!user.getAuthorizedDatabases().contains(dbName)) { + // UPDATE DB LIST + user.addDatabase(dbName, new String[] { userRole }); + security.saveUsers(); + } + + } catch (ServerSecurityException e) { + LogManager.instance() + .log(this, Level.WARNING, "Cannot create database '%s' because the user '%s' already exists with a different password", null, dbName, userName); + } + } else { + // CREATE A NEW USER + security.createUser(new JSONObject().put("name", userName)// + .put("password", security.encodePassword(userPassword))// + .put("databases", new JSONObject().put(dbName, new JSONArray()))); } } }