4 import java.io.IOException;
5 import java.nio.file.Files;
6 import java.nio.file.Path;
7 import java.nio.file.Paths;
8 import java.time.Instant;
9 import java.time.ZoneId;
10 import java.time.format.DateTimeFormatter;
11 import java.util.List;
12 import java.util.stream.Collectors;
13 import java.util.zip.ZipEntry;
14 import java.util.zip.ZipFile;
15 import java.util.zip.ZipOutputStream;
17 import org.eclipse.core.runtime.Platform;
18 import org.simantics.utils.FileUtils;
19 import org.slf4j.Logger;
20 import org.slf4j.LoggerFactory;
23 * @author Tuukka Lehtonen
26 public class DatabaseBaselines {
28 private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseBaselines.class);
30 private static final boolean REQUIRE_INDEX_IN_BASELINE = false;
32 private static final String DB_DIRECTORY = "db"; //$NON-NLS-1$
33 private static final String INDEX_DIRECTORY = ".metadata/.plugins/org.simantics.db.indexing"; //$NON-NLS-1$
35 private static final DateTimeFormatter TIMESTAMP_FORMAT = DateTimeFormatter.ofPattern("d. MMM yyyy HH:mm:ss");
37 public static boolean handleBaselineDatabase(Path installLocation, boolean databaseExists) throws PlatformException {
38 Path workspaceLocation = Platform.getLocation().toFile().toPath();
39 Path baselineIndicatorFile = workspaceLocation.resolve(".baselined");
40 if (Files.isRegularFile(baselineIndicatorFile)) {
41 // This means that the workspace has already been initialized from
42 // a database baseline and further initialization is not necessary.
46 Path baseline = resolveBaselineFile(installLocation);
47 if (baseline == null) {
48 baseline = getAutomaticBaselinePath();
53 if(!existsAutomaticBaseline())
57 DatabaseBaselines.validateBaselineFile(baseline);
58 DatabaseBaselines.validateWorkspaceForBaselineInitialization(workspaceLocation);
59 DatabaseBaselines.initializeWorkspaceWithBaseline(baseline, workspaceLocation, baselineIndicatorFile);
64 private static Path resolveBaselineFile(Path installLocation) throws PlatformException {
65 String dbBaselineArchive = System.getProperty("org.simantics.db.baseline", null);
66 if (dbBaselineArchive == null)
69 Path baseline = Paths.get(dbBaselineArchive);
70 if (baseline.isAbsolute()) {
71 if (!Files.isRegularFile(baseline))
72 throw new PlatformException("Specified database baseline archive " + baseline
73 + " does not exist. Cannot initialize workspace database from baseline.");
77 // Relative path resolution order:
78 // 1. from the platform "install location"
79 // 2. from working directory
80 //Path installLocation = tryGetInstallLocation();
81 if (installLocation != null) {
82 Path installedBaseline = installLocation.resolve(dbBaselineArchive);
83 if (Files.isRegularFile(installedBaseline))
84 return installedBaseline;
86 if (!Files.isRegularFile(baseline))
87 throw new PlatformException("Specified database baseline archive " + baseline
88 + " does not exist in either the install location (" + installLocation
89 + ") or the working directory (" + Paths.get(".").toAbsolutePath()
90 + "). Cannot initialize workspace database.");
94 private static boolean useAutomaticBaseline() {
95 return getAutomaticBaselinePath() != null;
98 private static Path getAutomaticBaselinePath() {
99 if("true".equals(System.getProperty("org.simantics.db.baseline.automatic")))
100 return Paths.get("automatic_baseline", "baseline.zip");
105 private static boolean existsAutomaticBaseline() {
106 Path baselineFile = getAutomaticBaselinePath();
107 if(baselineFile == null)
109 return Files.exists(baselineFile);
112 public static boolean shouldCreateAutomaticBaseline(boolean existsDatabase) throws PlatformException {
113 if(!useAutomaticBaseline()) {
114 // Are we using this feature?
118 // Baseline can only be created after db initialization
121 if(existsAutomaticBaseline()) {
122 // Existing baselines should not be automatically overridden
128 public static void createAutomaticBaseline(Path dbLocation) throws PlatformException {
130 if(existsAutomaticBaseline())
134 DatabaseBaselines.packageBaseline(dbLocation.getParent(), getAutomaticBaselinePath());
135 } catch (IOException e) {
136 LOGGER.error("Error while creating automatic baseline", e);
141 public static Path packageBaseline(Path fromWorkspace, Path packageFilePath) throws IOException {
142 File packageFile = packageFilePath.toFile();
143 packageFile.getParentFile().mkdirs();
144 return compressZip(fromWorkspace, collectBaselinePaths(fromWorkspace), packageFilePath);
147 private static List<Path> collectBaselinePaths(Path workspace) throws IOException {
148 Path dbPath = workspace.resolve(DB_DIRECTORY);
149 Path indexPath = workspace.resolve(INDEX_DIRECTORY);
151 if (!Files.isDirectory(dbPath))
152 throw new IllegalArgumentException("workspace database directory " + dbPath + " does not exist");
154 List<Path> paths = Files.walk(dbPath).collect(Collectors.toList());
155 if (Files.isDirectory(indexPath)) {
156 List<Path> indexPaths = Files.walk(indexPath).collect(Collectors.toList());
157 paths.addAll(indexPaths);
159 if (REQUIRE_INDEX_IN_BASELINE)
160 throw new IllegalArgumentException("workspace database index directory " + indexPath + " does not exist");
165 private static Path compressZip(Path relativeRoot, List<Path> paths, Path zipFile) throws IOException {
166 if (LOGGER.isDebugEnabled())
167 LOGGER.debug("Compressing " + paths.size() + " path entries into ZIP file " + zipFile);
168 try (ZipOutputStream zout = new ZipOutputStream(Files.newOutputStream(zipFile))) {
169 compressZip(relativeRoot, zout, paths);
172 if (LOGGER.isDebugEnabled())
173 LOGGER.debug("Compressed " + paths.size() + " entries into " + zipFile);
177 private static void compressZip(Path relativeRoot, ZipOutputStream zout, List<Path> paths) throws IOException {
178 for (Path p : paths) {
179 Path rp = relativeRoot.relativize(p);
180 String name = rp.toString();
181 if (Files.isDirectory(p)) {
182 name = name.endsWith("/") ? name : name + "/";
183 zout.putNextEntry(new ZipEntry(name));
185 zout.putNextEntry(new ZipEntry(name));
186 FileUtils.copy(p.toFile(), zout);
192 public static byte[] baselineIndicatorContents(Path path) throws IOException {
193 return String.format("%s%n%s%n",
195 Instant.now().atZone(ZoneId.systemDefault()).format(TIMESTAMP_FORMAT))
199 public static void validateWorkspaceForBaselineInitialization(Path workspaceLocation) throws PlatformException {
201 Path db = workspaceLocation.resolve(DB_DIRECTORY);
202 if (Files.exists(db))
203 throw new PlatformException("Database location " + db + " already exists. Cannot re-initialize workspace from baseline.");
204 if (REQUIRE_INDEX_IN_BASELINE) {
205 Path index = workspaceLocation.resolve(INDEX_DIRECTORY);
206 if (Files.exists(index) || !isEmptyDirectory(index))
207 throw new PlatformException("Index location " + index + " already exists. Cannot re-initialize workspace from baseline.");
209 } catch (IOException e) {
210 throw new PlatformException("Failed to validate workspace for baseline initialization", e);
214 private static boolean isEmptyDirectory(Path dir) throws IOException {
215 return Files.walk(dir).count() == 1;
218 public static void validateBaselineFile(Path baseline) throws PlatformException {
219 try (ZipFile zip = new ZipFile(baseline.toFile())) {
220 ZipEntry db = zip.getEntry(DB_DIRECTORY);
222 throw new PlatformException("Baseline archive does not contain database directory '" + DB_DIRECTORY + "'");
224 if (REQUIRE_INDEX_IN_BASELINE) {
225 ZipEntry index = zip.getEntry(INDEX_DIRECTORY);
227 throw new PlatformException("Baseline archive does not contain database index directory '" + INDEX_DIRECTORY + "'");
229 } catch (IOException e) {
230 throw new PlatformException("Failed to validate baseline archive " + baseline, e);
234 public static void initializeWorkspaceWithBaseline(Path baseline, Path workspaceLocation, Path indicatorPath) throws PlatformException {
236 Files.createDirectories(workspaceLocation);
237 FileUtils.extractZip(baseline.toFile(), workspaceLocation.toFile());
238 if (indicatorPath != null)
239 Files.write(indicatorPath, DatabaseBaselines.baselineIndicatorContents(indicatorPath));
240 } catch (IOException e) {
241 throw new PlatformException(e);
245 public static void main(String[] args) throws IOException {
246 packageBaseline(Paths.get("D:/temp/desktop/workspace"), Paths.get("d:/temp/desktop/workspace/baseline.zip"));