]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics/src/org/simantics/DatabaseBaselines.java
c58cb508623026fbf5f054cc67645e6d87d1db1a
[simantics/platform.git] / bundles / org.simantics / src / org / simantics / DatabaseBaselines.java
1 package org.simantics;
2
3 import java.io.File;
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;
16
17 import org.eclipse.core.runtime.Platform;
18 import org.simantics.utils.FileUtils;
19 import org.slf4j.Logger;
20 import org.slf4j.LoggerFactory;
21
22 /**
23  * @author Tuukka Lehtonen
24  * @since 1.34.0
25  */
26 public class DatabaseBaselines {
27
28         private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseBaselines.class);
29
30         private static final boolean REQUIRE_INDEX_IN_BASELINE = false;
31         
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$
34
35         private static final DateTimeFormatter TIMESTAMP_FORMAT = DateTimeFormatter.ofPattern("d. MMM yyyy HH:mm:ss");
36
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.
43             return true;
44         }
45
46         Path baseline = resolveBaselineFile(installLocation);
47         if (baseline == null) {
48             baseline = getAutomaticBaselinePath();
49             if(baseline == null)
50                 return false;
51             if(databaseExists)
52                 return false;
53             if(!existsAutomaticBaseline())
54                 return false;
55         }
56         
57         DatabaseBaselines.validateBaselineFile(baseline);
58         DatabaseBaselines.validateWorkspaceForBaselineInitialization(workspaceLocation);
59         DatabaseBaselines.initializeWorkspaceWithBaseline(baseline, workspaceLocation, baselineIndicatorFile);
60         return true;
61         
62     }
63
64     private static Path resolveBaselineFile(Path installLocation) throws PlatformException {
65         String dbBaselineArchive = System.getProperty("org.simantics.db.baseline", null);
66         if (dbBaselineArchive == null)
67             return null;
68
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.");
74             return baseline;
75         }
76
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;
85         }
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.");
91         return null;
92     }
93
94     private static boolean useAutomaticBaseline() {
95         return getAutomaticBaselinePath() != null;
96     }
97
98     private static Path getAutomaticBaselinePath() {
99         if("true".equals(System.getProperty("org.simantics.db.baseline.automatic")))
100             return Paths.get("automatic_baseline", "baseline.zip");
101         else
102             return null;
103     }
104     
105     private static boolean existsAutomaticBaseline() {
106         Path baselineFile = getAutomaticBaselinePath();
107         if(baselineFile == null)
108             return false;
109         return Files.exists(baselineFile);
110     }
111
112     public static boolean shouldCreateAutomaticBaseline(boolean existsDatabase) throws PlatformException {
113         if(!useAutomaticBaseline()) {
114             // Are we using this feature? 
115             return false;
116         }
117         if(existsDatabase) {
118             // Baseline can only be created after db initialization
119             return false;
120         }
121         if(existsAutomaticBaseline()) {
122             // Existing baselines should not be automatically overridden
123             return false;
124         }
125         return true;
126     }
127
128     public static void createAutomaticBaseline(Path dbLocation) throws PlatformException {
129
130         if(existsAutomaticBaseline())
131             return;
132
133         try {
134             DatabaseBaselines.packageBaseline(dbLocation.getParent(), getAutomaticBaselinePath());
135         } catch (IOException e) {
136             LOGGER.error("Error while creating automatic baseline", e);
137         }
138
139     }
140
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);
145         }
146
147         private static List<Path> collectBaselinePaths(Path workspace) throws IOException {
148                 Path dbPath = workspace.resolve(DB_DIRECTORY);
149                 Path indexPath = workspace.resolve(INDEX_DIRECTORY);
150
151                 if (!Files.isDirectory(dbPath))
152                         throw new IllegalArgumentException("workspace database directory " + dbPath + " does not exist");
153
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);
158                 } else {
159                         if (REQUIRE_INDEX_IN_BASELINE)
160                                 throw new IllegalArgumentException("workspace database index directory " + indexPath + " does not exist");
161                 }
162                 return paths;
163         }
164
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);
170                         return zipFile;
171                 } finally {
172                         if (LOGGER.isDebugEnabled())
173                                 LOGGER.debug("Compressed " + paths.size() + " entries into " + zipFile);
174                 }
175         }
176
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));
184                         } else {
185                                 zout.putNextEntry(new ZipEntry(name));
186                                 FileUtils.copy(p.toFile(), zout);
187                                 zout.closeEntry();
188                         }
189                 }
190         }
191
192         public static byte[] baselineIndicatorContents(Path path) throws IOException {
193                 return String.format("%s%n%s%n",
194                                 path.toString(),
195                                 Instant.now().atZone(ZoneId.systemDefault()).format(TIMESTAMP_FORMAT))
196                                 .getBytes("UTF-8");
197         }
198
199         public static void validateWorkspaceForBaselineInitialization(Path workspaceLocation) throws PlatformException {
200                 try {
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.");
208                         }
209                 } catch (IOException e) {
210                         throw new PlatformException("Failed to validate workspace for baseline initialization", e);
211                 }
212         }
213
214         private static boolean isEmptyDirectory(Path dir) throws IOException {
215                 return Files.walk(dir).count() == 1;
216         }
217
218         public static void validateBaselineFile(Path baseline) throws PlatformException {
219                 try (ZipFile zip = new ZipFile(baseline.toFile())) {
220                         ZipEntry db = zip.getEntry(DB_DIRECTORY);
221                         if (db == null)
222                                 throw new PlatformException("Baseline archive does not contain database directory '" + DB_DIRECTORY + "'");
223
224                         if (REQUIRE_INDEX_IN_BASELINE) {
225                                 ZipEntry index = zip.getEntry(INDEX_DIRECTORY);
226                                 if (index == null)
227                                         throw new PlatformException("Baseline archive does not contain database index directory '" + INDEX_DIRECTORY + "'");
228                         }
229                 } catch (IOException e) {
230                         throw new PlatformException("Failed to validate baseline archive " + baseline, e);
231                 }
232         }
233
234         public static void initializeWorkspaceWithBaseline(Path baseline, Path workspaceLocation, Path indicatorPath) throws PlatformException {
235                 try {
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);
242                 }
243         }
244
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"));
247         }
248
249 }