--- /dev/null
+package org.simantics;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+import org.simantics.utils.FileUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author Tuukka Lehtonen
+ * @since 1.34.0
+ */
+public class DatabaseBaselines {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseBaselines.class);
+
+ private static final boolean REQUIRE_INDEX_IN_BASELINE = false;
+
+ private static final String DB_DIRECTORY = "db"; //$NON-NLS-1$
+ private static final String INDEX_DIRECTORY = ".metadata/.plugins/org.simantics.db.indexing"; //$NON-NLS-1$
+
+ private static final DateTimeFormatter TIMESTAMP_FORMAT = DateTimeFormatter.ofPattern("d. MMM yyyy HH:mm:ss");
+
+ public static Path packageBaseline(Path fromWorkspace, Path packageFile) throws IOException {
+ return compressZip(fromWorkspace, collectBaselinePaths(fromWorkspace), packageFile);
+ }
+
+ private static List<Path> collectBaselinePaths(Path workspace) throws IOException {
+ Path dbPath = workspace.resolve(DB_DIRECTORY);
+ Path indexPath = workspace.resolve(INDEX_DIRECTORY);
+
+ if (!Files.isDirectory(dbPath))
+ throw new IllegalArgumentException("workspace database directory " + dbPath + " does not exist");
+
+ List<Path> paths = Files.walk(dbPath).collect(Collectors.toList());
+ if (Files.isDirectory(indexPath)) {
+ List<Path> indexPaths = Files.walk(indexPath).collect(Collectors.toList());
+ paths.addAll(indexPaths);
+ } else {
+ if (REQUIRE_INDEX_IN_BASELINE)
+ throw new IllegalArgumentException("workspace database index directory " + indexPath + " does not exist");
+ }
+ return paths;
+ }
+
+ private static Path compressZip(Path relativeRoot, List<Path> paths, Path zipFile) throws IOException {
+ if (LOGGER.isDebugEnabled())
+ LOGGER.debug("Compressing " + paths.size() + " path entries into ZIP file " + zipFile);
+ try (ZipOutputStream zout = new ZipOutputStream(Files.newOutputStream(zipFile))) {
+ compressZip(relativeRoot, zout, paths);
+ return zipFile;
+ } finally {
+ if (LOGGER.isDebugEnabled())
+ LOGGER.debug("Compressed " + paths.size() + " entries into " + zipFile);
+ }
+ }
+
+ private static void compressZip(Path relativeRoot, ZipOutputStream zout, List<Path> paths) throws IOException {
+ for (Path p : paths) {
+ Path rp = relativeRoot.relativize(p);
+ String name = rp.toString();
+ if (Files.isDirectory(p)) {
+ name = name.endsWith("/") ? name : name + "/";
+ zout.putNextEntry(new ZipEntry(name));
+ } else {
+ zout.putNextEntry(new ZipEntry(name));
+ FileUtils.copy(p.toFile(), zout);
+ zout.closeEntry();
+ }
+ }
+ }
+
+ public static byte[] baselineIndicatorContents(Path path) throws IOException {
+ return String.format("%s%n%s%n",
+ path.toString(),
+ Instant.now().atZone(ZoneId.systemDefault()).format(TIMESTAMP_FORMAT))
+ .getBytes("UTF-8");
+ }
+
+ public static void validateWorkspaceForBaselineInitialization(Path workspaceLocation) throws PlatformException {
+ try {
+ Path db = workspaceLocation.resolve(DB_DIRECTORY);
+ if (Files.exists(db))
+ throw new PlatformException("Database location " + db + " already exists. Cannot re-initialize workspace from baseline.");
+ if (REQUIRE_INDEX_IN_BASELINE) {
+ Path index = workspaceLocation.resolve(INDEX_DIRECTORY);
+ if (!Files.exists(index) || !isEmptyDirectory(index))
+ throw new PlatformException("Index location " + index + " already exists. Cannot re-initialize workspace from baseline.");
+ }
+ } catch (IOException e) {
+ throw new PlatformException("Failed to validate workspace for baseline initialization", e);
+ }
+ }
+
+ private static boolean isEmptyDirectory(Path dir) throws IOException {
+ return Files.walk(dir).count() == 1;
+ }
+
+ public static void validateBaselineFile(Path baseline) throws PlatformException {
+ try (ZipFile zip = new ZipFile(baseline.toFile())) {
+ ZipEntry db = zip.getEntry(DB_DIRECTORY);
+ if (db == null)
+ throw new PlatformException("Baseline archive does not contain database directory '" + DB_DIRECTORY + "'");
+
+ if (REQUIRE_INDEX_IN_BASELINE) {
+ ZipEntry index = zip.getEntry(INDEX_DIRECTORY);
+ if (index == null)
+ throw new PlatformException("Baseline archive does not contain database index directory '" + INDEX_DIRECTORY + "'");
+ }
+ } catch (IOException e) {
+ throw new PlatformException("Failed to validate baseline archive " + baseline, e);
+ }
+ }
+
+ public static void main(String[] args) throws IOException {
+ packageBaseline(Paths.get("D:/temp/desktop/workspace"), Paths.get("d:/temp/desktop/workspace/baseline.zip"));
+ }
+
+}