]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics/src/org/simantics/DatabaseBaselines.java
Fixed multiple issues causing dangling references to discarded queries
[simantics/platform.git] / bundles / org.simantics / src / org / simantics / DatabaseBaselines.java
1 package org.simantics;
2
3 import java.io.IOException;
4 import java.nio.file.Files;
5 import java.nio.file.Path;
6 import java.nio.file.Paths;
7 import java.time.Instant;
8 import java.time.ZoneId;
9 import java.time.format.DateTimeFormatter;
10 import java.util.List;
11 import java.util.stream.Collectors;
12 import java.util.zip.ZipEntry;
13 import java.util.zip.ZipFile;
14 import java.util.zip.ZipOutputStream;
15
16 import org.eclipse.core.runtime.Platform;
17 import org.simantics.utils.FileUtils;
18 import org.slf4j.Logger;
19 import org.slf4j.LoggerFactory;
20
21 /**
22  * @author Tuukka Lehtonen
23  * @since 1.34.0
24  */
25 public class DatabaseBaselines {
26
27         private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseBaselines.class);
28
29         private static final boolean REQUIRE_INDEX_IN_BASELINE = false;
30
31         private static final String DB_DIRECTORY = "db"; //$NON-NLS-1$
32         private static final String INDEX_DIRECTORY = ".metadata/.plugins/org.simantics.db.indexing"; //$NON-NLS-1$
33
34         private static final DateTimeFormatter TIMESTAMP_FORMAT = DateTimeFormatter.ofPattern("d. MMM yyyy HH:mm:ss");
35
36         public static boolean handleBaselineDatabase(Path installLocation, boolean databaseExists) throws PlatformException {
37                 Path workspaceLocation = Platform.getLocation().toFile().toPath();
38                 Path baselineIndicatorFile = workspaceLocation.resolve(".baselined");
39                 if (Files.isRegularFile(baselineIndicatorFile)) {
40                         // This means that the workspace has already been initialized from
41                         // a database baseline and further initialization is not necessary.
42                         return true;
43                 }
44
45                 Path baseline = resolveBaselineFile(installLocation);
46                 if (baseline == null) {
47                         baseline = getAutomaticBaselinePath();
48                         if(baseline == null)
49                                 return false;
50                         if(databaseExists)
51                                 return false;
52                         if(!existsAutomaticBaseline())
53                                 return false;
54                 }
55
56                 DatabaseBaselines.validateBaselineFile(baseline);
57                 DatabaseBaselines.validateWorkspaceForBaselineInitialization(workspaceLocation);
58                 DatabaseBaselines.initializeWorkspaceWithBaseline(baseline, workspaceLocation, baselineIndicatorFile);
59                 return true;
60
61         }
62
63         private static Path resolveBaselineFile(Path installLocation) throws PlatformException {
64                 String dbBaselineArchive = System.getProperty("org.simantics.db.baseline", null);
65                 if (dbBaselineArchive == null)
66                         return null;
67
68                 Path baseline = Paths.get(dbBaselineArchive);
69                 if (baseline.isAbsolute()) {
70                         if (!Files.isRegularFile(baseline))
71                                 throw new PlatformException("Specified database baseline archive " + baseline
72                                                 + " does not exist. Cannot initialize workspace database from baseline.");
73                         return baseline;
74                 }
75
76                 // Relative path resolution order:
77                 // 1. from the platform "install location"
78                 // 2. from working directory
79                 //Path installLocation = tryGetInstallLocation();
80                 if (installLocation != null) {
81                         Path installedBaseline = installLocation.resolve(dbBaselineArchive);
82                         if (Files.isRegularFile(installedBaseline))
83                                 return installedBaseline;
84                 }
85                 if (!Files.isRegularFile(baseline))
86                         throw new PlatformException("Specified database baseline archive " + baseline
87                                         + " does not exist in either the install location (" + installLocation
88                                         + ") or the working directory (" + Paths.get(".").toAbsolutePath()
89                                         + "). Cannot initialize workspace database.");
90                 return null;
91         }
92
93         private static boolean useAutomaticBaseline() {
94                 return getAutomaticBaselinePath() != null;
95         }
96
97         private static Path getAutomaticBaselinePath() {
98                 if("true".equals(System.getProperty("org.simantics.db.baseline.automatic")))
99                         return Paths.get("automatic_baseline", "baseline.zip");
100                 else
101                         return null;
102         }
103
104         private static boolean existsAutomaticBaseline() {
105                 Path baselineFile = getAutomaticBaselinePath();
106                 if(baselineFile == null)
107                         return false;
108                 return Files.exists(baselineFile);
109         }
110
111         public static boolean shouldCreateAutomaticBaseline(boolean existsDatabase) throws PlatformException {
112                 if(!useAutomaticBaseline()) {
113                         // Are we using this feature? 
114                         return false;
115                 }
116                 if(existsDatabase) {
117                         // Baseline can only be created after db initialization
118                         return false;
119                 }
120                 if(existsAutomaticBaseline()) {
121                         // Existing baselines should not be automatically overridden
122                         return false;
123                 }
124                 return true;
125         }
126
127         public static void createAutomaticBaseline(Path dbLocation) throws PlatformException {
128
129                 if(existsAutomaticBaseline())
130                         return;
131
132                 try {
133                         DatabaseBaselines.packageBaseline(dbLocation.getParent(), getAutomaticBaselinePath());
134                 } catch (IOException e) {
135                         LOGGER.error("Error while creating automatic baseline", e);
136                 }
137
138         }
139
140         public static Path packageBaseline(Path fromWorkspace, Path packageFilePath) throws IOException {
141                 Files.createDirectories(packageFilePath.getParent());
142                 return compressZip(fromWorkspace, collectBaselinePaths(fromWorkspace), packageFilePath);
143         }
144
145         private static List<Path> collectBaselinePaths(Path workspace) throws IOException {
146                 Path dbPath = workspace.resolve(DB_DIRECTORY);
147                 Path indexPath = workspace.resolve(INDEX_DIRECTORY);
148
149                 if (!Files.isDirectory(dbPath))
150                         throw new IllegalArgumentException("workspace database directory " + dbPath + " does not exist");
151
152                 List<Path> paths = Files.walk(dbPath).collect(Collectors.toList());
153                 if (Files.isDirectory(indexPath)) {
154                         List<Path> indexPaths = Files.walk(indexPath).collect(Collectors.toList());
155                         paths.addAll(indexPaths);
156                 } else {
157                         if (REQUIRE_INDEX_IN_BASELINE)
158                                 throw new IllegalArgumentException("workspace database index directory " + indexPath + " does not exist");
159                 }
160                 return paths;
161         }
162
163         private static Path compressZip(Path relativeRoot, List<Path> paths, Path zipFile) throws IOException {
164                 if (LOGGER.isDebugEnabled())
165                         LOGGER.debug("Compressing " + paths.size() + " path entries into ZIP file " + zipFile);
166                 try (ZipOutputStream zout = new ZipOutputStream(Files.newOutputStream(zipFile))) {
167                         compressZip(relativeRoot, zout, paths);
168                         return zipFile;
169                 } finally {
170                         if (LOGGER.isDebugEnabled())
171                                 LOGGER.debug("Compressed " + paths.size() + " entries into " + zipFile);
172                 }
173         }
174
175         private static void compressZip(Path relativeRoot, ZipOutputStream zout, List<Path> paths) throws IOException {
176                 for (Path p : paths) {
177                         Path rp = relativeRoot.relativize(p);
178                         String name = rp.toString();
179                         if (Files.isDirectory(p)) {
180                                 name = name.endsWith("/") ? name : name + "/";
181                                 zout.putNextEntry(new ZipEntry(name));
182                         } else {
183                                 zout.putNextEntry(new ZipEntry(name));
184                                 FileUtils.copy(p.toFile(), zout);
185                                 zout.closeEntry();
186                         }
187                 }
188         }
189
190         public static byte[] baselineIndicatorContents(Path path) throws IOException {
191                 return String.format("%s%n%s%n",
192                                 path.toString(),
193                                 Instant.now().atZone(ZoneId.systemDefault()).format(TIMESTAMP_FORMAT))
194                                 .getBytes("UTF-8");
195         }
196
197         public static void validateWorkspaceForBaselineInitialization(Path workspaceLocation) throws PlatformException {
198                 try {
199                         Path db = workspaceLocation.resolve(DB_DIRECTORY);
200                         if (Files.exists(db))
201                                 throw new PlatformException("Database location " + db + " already exists. Cannot re-initialize workspace from baseline.");
202                         if (REQUIRE_INDEX_IN_BASELINE) {
203                                 Path index = workspaceLocation.resolve(INDEX_DIRECTORY);
204                                 if (Files.exists(index) || !isEmptyDirectory(index))
205                                         throw new PlatformException("Index location " + index + " already exists. Cannot re-initialize workspace from baseline.");
206                         }
207                 } catch (IOException e) {
208                         throw new PlatformException("Failed to validate workspace for baseline initialization", e);
209                 }
210         }
211
212         private static boolean isEmptyDirectory(Path dir) throws IOException {
213                 return Files.walk(dir).count() == 1;
214         }
215
216         public static void validateBaselineFile(Path baseline) throws PlatformException {
217                 try (ZipFile zip = new ZipFile(baseline.toFile())) {
218                         ZipEntry db = zip.getEntry(DB_DIRECTORY);
219                         if (db == null)
220                                 throw new PlatformException("Baseline archive does not contain database directory '" + DB_DIRECTORY + "'");
221
222                         if (REQUIRE_INDEX_IN_BASELINE) {
223                                 ZipEntry index = zip.getEntry(INDEX_DIRECTORY);
224                                 if (index == null)
225                                         throw new PlatformException("Baseline archive does not contain database index directory '" + INDEX_DIRECTORY + "'");
226                         }
227                 } catch (IOException e) {
228                         throw new PlatformException("Failed to validate baseline archive " + baseline, e);
229                 }
230         }
231
232         public static void initializeWorkspaceWithBaseline(Path baseline, Path workspaceLocation, Path indicatorPath) throws PlatformException {
233                 try {
234                         Files.createDirectories(workspaceLocation);
235                         FileUtils.extractZip(baseline.toFile(), workspaceLocation.toFile());
236                         if (indicatorPath != null)
237                                 Files.write(indicatorPath, DatabaseBaselines.baselineIndicatorContents(indicatorPath));
238                 } catch (IOException e) {
239                         throw new PlatformException(e);
240                 }
241         }
242
243         public static void main(String[] args) throws IOException {
244                 packageBaseline(Paths.get("D:/temp/desktop/workspace"), Paths.get("d:/temp/desktop/workspace/baseline.zip"));
245         }
246
247 }