]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics/src/org/simantics/SimanticsPlatform.java
Initial support for concurrency in databoard, bindings and serializers
[simantics/platform.git] / bundles / org.simantics / src / org / simantics / SimanticsPlatform.java
index 961dfc71899d8a872d8941dda396f95a8ab2f0ca..8b1ac1bced1659d414c17dbf6930b11500bbed2b 100644 (file)
@@ -22,9 +22,6 @@ 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.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
@@ -35,8 +32,6 @@ import java.util.Properties;
 import java.util.Set;
 import java.util.TreeMap;
 import java.util.UUID;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
 
 import org.eclipse.core.runtime.ILog;
 import org.eclipse.core.runtime.IProduct;
@@ -46,11 +41,10 @@ import org.eclipse.core.runtime.NullProgressMonitor;
 import org.eclipse.core.runtime.Platform;
 import org.eclipse.core.runtime.Status;
 import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.osgi.service.datalocation.Location;
 import org.eclipse.osgi.service.resolver.BundleDescription;
 import org.ini4j.Ini;
 import org.ini4j.InvalidFileFormatException;
-import org.simantics.SimanticsPlatform.OntologyRecoveryPolicy;
-import org.simantics.SimanticsPlatform.RecoveryPolicy;
 import org.simantics.databoard.Bindings;
 import org.simantics.databoard.Databoard;
 import org.simantics.datatypes.literal.Font;
@@ -66,21 +60,22 @@ import org.simantics.db.UndoContext;
 import org.simantics.db.VirtualGraph;
 import org.simantics.db.WriteGraph;
 import org.simantics.db.common.request.ObjectsWithType;
-import org.simantics.db.common.request.Queries;
-import org.simantics.db.common.request.WriteRequest;
 import org.simantics.db.common.request.WriteResultRequest;
 import org.simantics.db.common.utils.Transaction;
 import org.simantics.db.exception.ClusterSetExistException;
 import org.simantics.db.exception.DatabaseException;
-import org.simantics.db.exception.ResourceNotFoundException;
 import org.simantics.db.indexing.DatabaseIndexing;
 import org.simantics.db.layer0.genericrelation.DependenciesRelation;
+import org.simantics.db.layer0.genericrelation.IndexException;
+import org.simantics.db.layer0.genericrelation.IndexedRelations;
+import org.simantics.db.layer0.request.PossibleResource;
 import org.simantics.db.layer0.util.SimanticsClipboardImpl;
 import org.simantics.db.layer0.util.SimanticsKeys;
 import org.simantics.db.layer0.util.TGTransferableGraphSource;
 import org.simantics.db.layer0.variable.VariableRepository;
 import org.simantics.db.management.SessionContext;
 import org.simantics.db.request.Read;
+import org.simantics.db.request.Write;
 import org.simantics.db.service.LifecycleSupport.LifecycleListener;
 import org.simantics.db.service.LifecycleSupport.LifecycleState;
 import org.simantics.db.service.QueryControl;
@@ -103,6 +98,7 @@ import org.simantics.project.IProject;
 import org.simantics.project.ProjectFeatures;
 import org.simantics.project.ProjectKeys;
 import org.simantics.project.Projects;
+import org.simantics.project.SessionDescriptor;
 import org.simantics.project.exception.ProjectException;
 import org.simantics.project.features.registry.GroupReference;
 import org.simantics.project.management.DatabaseManagement;
@@ -190,10 +186,22 @@ public class SimanticsPlatform implements LifecycleListener {
 
     /** Session specific bindings */
     public SimanticsBindings simanticsBindings;
-    public SimanticsBindings simanticsBindings2;
 
     public Thread mainThread;
 
+    private Thread shutdownHook = new Thread() {
+        @Override
+        public void run() {
+            try {
+                LOGGER.warn("Simantics platform was not properly shut down. Executing safety shutdown hook.");
+                shutdown(null, false);
+            } catch (PlatformException e) {
+                LOGGER.error("Simantics Platform shutdown hook execution failed.", e);
+                log.log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Simantics Platform shutdown hook execution failed.", e));
+            }
+        }
+    };
+
     /**
      * The {@link IProject} activated by
      * {@link #startUp(IProgressMonitor, RecoveryPolicy, OntologyRecoveryPolicy, ServerAddress, PlatformUserAgent)}
@@ -218,7 +226,7 @@ public class SimanticsPlatform implements LifecycleListener {
         return application != null ? application : UUID.randomUUID().toString();
     }
 
-    private Session setupDatabase(String databaseDriverId, IProgressMonitor progressMonitor, RecoveryPolicy workspacePolicy, PlatformUserAgent userAgent) throws PlatformException {
+    private SessionDescriptor setupDatabase(String databaseDriverId, IProgressMonitor progressMonitor, RecoveryPolicy workspacePolicy, PlatformUserAgent userAgent) throws PlatformException {
         if (progressMonitor == null)
             progressMonitor = new NullProgressMonitor();
         Path workspaceLocation = Platform.getLocation().toFile().toPath();
@@ -257,19 +265,24 @@ public class SimanticsPlatform implements LifecycleListener {
 
     public void synchronizeOntologies(IProgressMonitor progressMonitor, OntologyRecoveryPolicy ontologyPolicy, boolean requireSynchronize) throws PlatformException {
 
-        if (progressMonitor == null) progressMonitor = new NullProgressMonitor();
-
-        final DatabaseManagement mgmt = new DatabaseManagement();
+        SubMonitor monitor = SubMonitor.convert(progressMonitor, 100);
 
+        monitor.setTaskName("Compile dynamic ontologies");
         PlatformUtil.compileAllDynamicOntologies();
 
-        progressMonitor.setTaskName("Asserting all ontologies are installed");
-        final Map<GraphBundleRef, GraphBundleEx> platformTGs = new HashMap<GraphBundleRef, GraphBundleEx>();
+        String message = "Asserting all ontologies are installed";
+        LOGGER.info(message);
+        monitor.setTaskName(message);
+
+        DatabaseManagement mgmt = new DatabaseManagement();
+        Map<GraphBundleRef, GraphBundleEx> platformTGs = new HashMap<>();
         try {
 
             // Get a list of bundles installed into the database
-            progressMonitor.subTask("find installed bundles from database");
-            Map<GraphBundleRef, GraphBundleEx> installedTGs = new HashMap<GraphBundleRef, GraphBundleEx>();
+            message = "find installed bundles from database";
+            monitor.subTask(message);
+            LOGGER.info(message);
+            Map<GraphBundleRef, GraphBundleEx> installedTGs = new HashMap<>();
             for (GraphBundle b : session.syncRequest( mgmt.GraphBundleQuery )) {
                 installedTGs.put(GraphBundleRef.of(b), GraphBundleEx.extend(b));
             }
@@ -278,10 +291,13 @@ public class SimanticsPlatform implements LifecycleListener {
 //            if(installedTGs.size() > 1) return;
 
             // Get a list of all bundles in the platform (Bundle Context)
-            List<GraphBundle> tgs = new ArrayList<GraphBundle>();
-            progressMonitor.subTask("load all transferable graphs from platform");
-            PlatformUtil.getAllGraphs(tgs);
-            progressMonitor.subTask("extend bundles to compile versions");
+            message = "load all transferable graphs from platform";
+            monitor.subTask(message);
+            LOGGER.info(message);
+            Collection<GraphBundle> tgs = PlatformUtil.getAllGraphs();
+            message = "extend bundles to compile versions";
+            monitor.subTask(message);
+            LOGGER.info(message);
             for (GraphBundle b : tgs) {
                 GraphBundleEx gbe = GraphBundleEx.extend(b);
                 gbe.build();
@@ -289,10 +305,12 @@ public class SimanticsPlatform implements LifecycleListener {
             }
 
             // Compile a list of TGs that need to be installed or reinstalled in the database
-            progressMonitor.subTask("check bundle reinstallation demand");
-            List<GraphBundleEx> installTGs = new ArrayList<GraphBundleEx>();
+            message = "check bundle reinstallation demand";
+            monitor.subTask(message);
+            LOGGER.info(message);
+            List<GraphBundleEx> installTGs = new ArrayList<>();
             // Create list of TGs to update, <newTg, oldTg>
-            Map<GraphBundleEx,GraphBundleEx> reinstallTGs = new TreeMap<GraphBundleEx,GraphBundleEx>();
+            Map<GraphBundleEx,GraphBundleEx> reinstallTGs = new TreeMap<>();
             for (Entry<GraphBundleRef, GraphBundleEx> e : platformTGs.entrySet()) {
                 GraphBundleRef key = e.getKey();
                 GraphBundleEx platformBundle = e.getValue();
@@ -362,7 +380,9 @@ public class SimanticsPlatform implements LifecycleListener {
                 }
 
                 if (ontologyPolicy == OntologyRecoveryPolicy.Merge) {
-                    progressMonitor.subTask("Merging ontology changes");
+                    message = "Merging ontology changes";
+                    monitor.subTask(message);
+                    LOGGER.info(message);
                     // Sort missing TGs into install order
                     GraphDependencyAnalyzer<GraphBundle> analyzer = new GraphDependencyAnalyzer<GraphBundle>();
                     for(GraphBundle tg : installTGs) analyzer.addGraph(tg, tg.getGraph());
@@ -386,21 +406,18 @@ public class SimanticsPlatform implements LifecycleListener {
                         }
                         throw new PlatformException(sb.toString());
                     }
-                    
+
                     List<GraphBundle> sortedBundles = analyzer.getSortedGraphs();
                     if(!sortedBundles.isEmpty()) {
-                       
-                        session.syncRequest(new WriteRequest() {
-                            @Override
-                            public void perform(WriteGraph graph) throws DatabaseException {
-                                try {
-                                    graph.newClusterSet(graph.getRootLibrary());
-                                } catch (ClusterSetExistException e) {
-                                    // Cluster set exist already, no problem.
-                                }
-                                graph.setClusterSet4NewResource(graph.getRootLibrary());
-                                graph.flushCluster();
+
+                        session.syncRequest((Write) graph -> {
+                            try {
+                                graph.newClusterSet(graph.getRootLibrary());
+                            } catch (ClusterSetExistException e) {
+                                // Cluster set exist already, no problem.
                             }
+                            graph.setClusterSet4NewResource(graph.getRootLibrary());
+                            graph.flushCluster();
                         });
 
                         boolean mergedOntologies = false;
@@ -441,11 +458,9 @@ public class SimanticsPlatform implements LifecycleListener {
                                 log.log(new Status(IStatus.INFO, Activator.PLUGIN_ID, "Merging new version of "+tg.toString()));
 
                                        startTransaction(session, true);
-                                       
+
                                        //delta.print();
                                        try {
-                                               
-                                               
                                                long[] resourceArray = TransferableGraphs.applyDelta(writeGraph(), oldResources, delta);
                                                tg.setResourceArray(resourceArray);
                                                mgmt.setGraphBundleEntry(tg);
@@ -458,25 +473,19 @@ public class SimanticsPlatform implements LifecycleListener {
                                        }
                                }
                         }
-                        
-                        session.syncRequest(new WriteRequest() {
-                            @Override
-                            public void perform(WriteGraph graph) throws DatabaseException {
-                                graph.setClusterSet4NewResource(graph.getRootLibrary());
-                                graph.flushCluster();
-                            }
+
+                        session.syncRequest((Write) graph -> {
+                            graph.setClusterSet4NewResource(graph.getRootLibrary());
+                            graph.flushCluster();
                         });
 
                         if (mergedOntologies)
                             DatabaseIndexing.deleteAllIndexes();
                     }
-
-                    TimeLogger.log("Ontologies synchronized.");
-                    
                 }
                 session.getService(XSupport.class).setServiceMode(false, false);
             }
-            progressMonitor.worked(20);
+            monitor.worked(100);
         } catch (IOException e) {
             throw new PlatformException(e);
         } catch (DatabaseException e) {
@@ -521,41 +530,42 @@ public class SimanticsPlatform implements LifecycleListener {
 
     public boolean assertProject(IProgressMonitor progressMonitor, RecoveryPolicy workspacePolicy, boolean installProject) throws PlatformException {
 
-        if (progressMonitor == null) progressMonitor = new NullProgressMonitor();
+        SubMonitor monitor = SubMonitor.convert(progressMonitor, 10);
 
         final DatabaseManagement mgmt = new DatabaseManagement();
 
-        progressMonitor.setTaskName("Asserting project resource exists in the database");
+        monitor.setTaskName("Asserting project resource exists in the database");
         try {
-            projectResource = session.syncRequest( Queries.resource( projectURI ) );
-        } catch (ResourceNotFoundException nfe) {
-            // Project was not found
-            if (workspacePolicy == RecoveryPolicy.ThrowError)
-                throw new PlatformException("Project Resource "+projectURI+" is not found in the database.");
-            // Create empty project with no features
-            try {
-                Transaction.startTransaction(session, true);
+            projectResource = session.syncRequest(new PossibleResource(projectURI));
+            if (projectResource == null) {
+                // Project was not found
+                if (workspacePolicy == RecoveryPolicy.ThrowError)
+                    throw new PlatformException("Project Resource "+projectURI+" is not found in the database.");
+                // Create empty project with no features
                 try {
-                    // The project needs to be created mutable.
-                    session.getService(XSupport.class).setServiceMode(true, false);
+                    Transaction.startTransaction(session, true);
+                    try {
+                        // The project needs to be created mutable.
+                        session.getService(XSupport.class).setServiceMode(true, false);
 
-                    ArrayList<String> empty = new ArrayList<String>();
-                    projectResource = mgmt.createProject(projectName, empty);
-                    installProject |= true;
+                        ArrayList<String> empty = new ArrayList<String>();
+                        projectResource = mgmt.createProject(projectName, empty);
+                        installProject |= true;
 
-                    session.getService(XSupport.class).setServiceMode(false, false);
-                    Transaction.commit();
-                } finally {
-                    Transaction.endTransaction();
+                        session.getService(XSupport.class).setServiceMode(false, false);
+                        Transaction.commit();
+                    } finally {
+                        Transaction.endTransaction();
+                    }
+                    //session.getService( LifecycleSupport.class ).save();
+                } catch (DatabaseException e) {
+                    throw new PlatformException("Failed to create "+projectURI, e);
                 }
-                //session.getService( LifecycleSupport.class ).save();
-            } catch (DatabaseException e) {
-                throw new PlatformException("Failed to create "+projectURI, e);
             }
         } catch (DatabaseException e) {
             throw new PlatformException("Failed to create "+projectURI, e);
         }
-        progressMonitor.worked(10);
+        monitor.worked(10);
 
         return installProject;
 
@@ -570,38 +580,11 @@ public class SimanticsPlatform implements LifecycleListener {
             Set<GroupReference> publishedFeatureGroups = ProjectFeatures.getInstallGroupsOfPublishedFeatures();
             Collection<GroupReference> groupsWithoutVersion = GroupReference.stripVersions(publishedFeatureGroups);
 
-    //        final List<String> Platform_Features = new ArrayList<String>();
-    //
-    //        // Convert graph instances
-    //        Collection<TransferableGraph1> platformGraphs = new ArrayList<TransferableGraph1>();
-    //        for (GraphBundleEx e : platformTGs.values()) platformGraphs.add( e.getGraph() );
-    //        IGraph graph = Graphs.createGraph(platformGraphs);
-    //
-    //        Res    PublishedProjectFeatures = UriUtils.uriToPath( ProjectResource.URIs.PublishedProjectFeatures );
-    //        Path   HasFeature = UriUtils.uriToPath( ProjectResource.URIs.HasFeature );
-    //        for(Res feature : graph.getObjects(PublishedProjectFeatures, HasFeature)) {
-    //            System.out.println("Installing Project Feature: "+feature.toString());
-    //            Platform_Features.add( feature.toString() );
-    //        }
-
             try {
-                Transaction.startTransaction(session, true);
-                try {
-    //                for (String feature : Platform_Features) {
-    //                    try {
-    //                        getResource(feature);
-    //                    } catch(ResourceNotFoundException e) {
-    //                        System.out.println(feature+" not found");
-    //                    }
-    //                    mgmt.installFeature(projectResource, feature);
-    //                }
-                    Projects.setProjectInstalledGroups(writeGraph(), projectResource, groupsWithoutVersion);
-                    Transaction.commit();
-                } finally {
-                    Transaction.endTransaction();
-                }
-                //session.getService( LifecycleSupport.class ).save();
-            } catch(DatabaseException ae) {
+                session.syncRequest(
+                        (Write) graph ->
+                        Projects.setProjectInstalledGroups(graph, projectResource, groupsWithoutVersion));
+            } catch (DatabaseException ae) {
                 throw new PlatformException("Failed to install features", ae);
             }
             progressMonitor.worked(10);
@@ -713,73 +696,58 @@ public class SimanticsPlatform implements LifecycleListener {
         resetDatabase(monitor);
     }
 
-    public boolean handleBaselineDatabase() throws PlatformException {
-        Path workspaceLocation = Platform.getLocation().toFile().toPath();
-        Path baselineIndicatorFile = workspaceLocation.resolve(".baselined");
-        if (Files.isRegularFile(baselineIndicatorFile)) {
-            // This means that the workspace has already been initialized from
-            // a database baseline and further initialization is not necessary.
-            return true;
-        }
+    private static Path tryGetInstallLocation() {
+        Location l = Platform.getInstallLocation();
+        return l == null ? null : new File(l.getURL().getPath()).toPath();
+    }
 
+    private Path resolveBaselineFile() throws PlatformException {
         String dbBaselineArchive = System.getProperty("org.simantics.db.baseline", null);
         if (dbBaselineArchive == null)
-            return false;
+            return null;
 
         Path baseline = Paths.get(dbBaselineArchive);
-        if (!Files.isRegularFile(baseline))
-            throw new PlatformException("Specified database baseline archive " + baseline + " does not exist. Cannot initialize workspace database.");
-
-        validateBaselineFile(baseline);
-        validateWorkspaceForBaselineInitialization(workspaceLocation);
-
-        try {
-            Files.createDirectories(workspaceLocation);
-            FileUtils.extractZip(baseline.toFile(), workspaceLocation.toFile());
-            Files.write(baselineIndicatorFile, baselineIndicatorContents(baselineIndicatorFile));
-            return true;
-        } catch (IOException e) {
-            throw new PlatformException(e);
+        if (baseline.isAbsolute()) {
+            if (!Files.isRegularFile(baseline))
+                throw new PlatformException("Specified database baseline archive " + baseline
+                        + " does not exist. Cannot initialize workspace database from baseline.");
+            return baseline;
         }
-    }
-
-    private static final DateTimeFormatter TIMESTAMP_FORMAT = DateTimeFormatter.ofPattern("d. MMM yyyy HH:mm:ss");
 
-    private 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");
+        // Relative path resolution order:
+        // 1. from the platform "install location"
+        // 2. from working directory
+        Path installLocation = tryGetInstallLocation();
+        if (installLocation != null) {
+            Path installedBaseline = installLocation.resolve(dbBaselineArchive);
+            if (Files.isRegularFile(installedBaseline))
+                return installedBaseline;
+        }
+        if (!Files.isRegularFile(baseline))
+            throw new PlatformException("Specified database baseline archive " + baseline
+                    + " does not exist in either the install location (" + installLocation
+                    + ") or the working directory (" + Paths.get(".").toAbsolutePath()
+                    + "). Cannot initialize workspace database.");
+        return null;
     }
 
-    private void validateWorkspaceForBaselineInitialization(Path workspaceLocation) throws PlatformException {
-        try {
-            Path db = workspaceLocation.resolve("db");
-            if (Files.exists(db))
-                throw new PlatformException("Database location " + db + " already exists. Cannot re-initialize workspace from baseline.");
-            Path index = workspaceLocation.resolve(".metadata/.plugins/org.simantics.db.indexing");
-            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 boolean handleBaselineDatabase() throws PlatformException {
+        Path workspaceLocation = Platform.getLocation().toFile().toPath();
+        Path baselineIndicatorFile = workspaceLocation.resolve(".baselined");
+        if (Files.isRegularFile(baselineIndicatorFile)) {
+            // This means that the workspace has already been initialized from
+            // a database baseline and further initialization is not necessary.
+            return true;
         }
-    }
 
-    private static boolean isEmptyDirectory(Path dir) throws IOException {
-        return Files.walk(dir).count() == 1;
-    }
+        Path baseline = resolveBaselineFile();
+        if (baseline == null)
+            return false;
 
-    private void validateBaselineFile(Path baseline) throws PlatformException {
-        try (ZipFile zip = new ZipFile(baseline.toFile())) {
-            ZipEntry db = zip.getEntry("db");
-            if (db == null)
-               throw new PlatformException("Baseline archive does not contain database directory 'db'");
-            ZipEntry index = zip.getEntry(".metadata/.plugins/org.simantics.db.indexing");
-            if (index == null)
-                throw new PlatformException("Baseline archive does not contain database index directory '.metadata/.plugins/org.simantics.db.indexing'");
-        } catch (IOException e) {
-            throw new PlatformException("Failed to validate baseline archive " + baseline, e);
-        }
+        DatabaseBaselines.validateBaselineFile(baseline);
+        DatabaseBaselines.validateWorkspaceForBaselineInitialization(workspaceLocation);
+        DatabaseBaselines.initializeWorkspaceWithBaseline(baseline, workspaceLocation, baselineIndicatorFile);
+        return true;
     }
 
     /**
@@ -792,7 +760,8 @@ public class SimanticsPlatform implements LifecycleListener {
      * installing project features.
      * <p>
      *
-     * In SWB this is handled in SimanticsWorkbenchAdvisor#openWindows().
+     * In Simantics Workbench this is handled in
+     * <code>SimanticsWorkbenchAdvisor#openWindows()</code>.
      * <p>
      *
      * If remote server is given, simantics plaform takes connection there
@@ -806,7 +775,7 @@ public class SimanticsPlatform implements LifecycleListener {
      *        startup or <code>null</code> to resort to default measures
      * @throws PlatformException
      */
-    public SessionContext startUp(String databaseDriverId, IProgressMonitor progressMonitor, RecoveryPolicy workspacePolicy,
+    public synchronized SessionContext startUp(String databaseDriverId, IProgressMonitor progressMonitor, RecoveryPolicy workspacePolicy,
             OntologyRecoveryPolicy ontologyPolicy, boolean requireSynchronize, PlatformUserAgent userAgent)
     throws PlatformException
     {
@@ -815,8 +784,8 @@ public class SimanticsPlatform implements LifecycleListener {
         TimeLogger.log("Beginning of SimanticsPlatform.startUp");
 
         LOGGER.info("Beginning of SimanticsPlatform.startUp");
-        
-        if (progressMonitor == null) progressMonitor = new NullProgressMonitor();
+
+        SubMonitor monitor = SubMonitor.convert(progressMonitor, 1000);
 
         // For debugging on what kind of platform automatic tests are running in
         // case there are problems.
@@ -833,15 +802,18 @@ public class SimanticsPlatform implements LifecycleListener {
 
         // 0.2 Clear VariableRepository.repository static map which holds references to SessionImplDb
         VariableRepository.clear();
-        
+
         // 0.3 Handle baseline database before opening db
+        @SuppressWarnings("unused")
         boolean usingBaseline = handleBaselineDatabase();
-        
+
         // 1. Assert there is a database at <workspace>/db
-        session = setupDatabase(databaseDriverId, progressMonitor, workspacePolicy, userAgent);
+        SessionDescriptor sessionDescriptor = setupDatabase(databaseDriverId, monitor.newChild(200, SubMonitor.SUPPRESS_NONE), workspacePolicy, userAgent);
+        session = sessionDescriptor.getSession();
         TimeLogger.log("Database setup complete");
         
-        // 1.1 
+        // 2. Delete all indexes if we cannot be certain they are up-to-date
+        //    A full index rebuild will be done later, before project activation.
         XSupport support = session.getService(XSupport.class);
         if (support.rolledback()) {
             try {
@@ -850,37 +822,31 @@ public class SimanticsPlatform implements LifecycleListener {
                 throw new PlatformException(e);
             }
         }
-        
-        // 2. Assert all graphs, and correct versions, are installed to the database
-        if(!usingBaseline) {
-            synchronizeOntologies(progressMonitor, ontologyPolicy, requireSynchronize);
-            TimeLogger.log("Synchronized ontologies");
-        }
+
+        // 3. Assert all graphs, and correct versions, are installed to the database
+        synchronizeOntologies(monitor.newChild(400, SubMonitor.SUPPRESS_NONE), ontologyPolicy, requireSynchronize);
+        TimeLogger.log("Synchronized ontologies");
 
         // 4. Assert simantics.cfg exists
-        boolean installProject = assertConfiguration(progressMonitor,workspacePolicy);
+        boolean installProject = assertConfiguration(monitor.newChild(25, SubMonitor.SUPPRESS_NONE),workspacePolicy);
 
         // 5. Assert Project Resource is installed in the database
-        installProject = assertProject(progressMonitor, workspacePolicy, installProject);
+        installProject = assertProject(monitor.newChild(25, SubMonitor.SUPPRESS_NONE), workspacePolicy, installProject);
 
         // 6. Install all features into project, if in debug mode
-        updateInstalledGroups(progressMonitor, installProject);
+        updateInstalledGroups(monitor.newChild(25), true); //installProject);
         TimeLogger.log("Installed all features into project");
 
         // 7. Assert L0.Session in database for this session
-        assertSessionModel(progressMonitor);
+        assertSessionModel(monitor.newChild(25, SubMonitor.SUPPRESS_NONE));
 
         session.getService(XSupport.class).setServiceMode(false, false);
 
         try {
-            session.sync(new WriteRequest() {
-
-                @Override
-                public void perform(WriteGraph graph) throws DatabaseException {
-                    QueryControl qc = graph.getService(QueryControl.class);
-                    qc.flush(graph);
-                }
-
+            monitor.setTaskName("Flush query cache");
+            session.syncRequest((Write) graph -> {
+                QueryControl qc = graph.getService(QueryControl.class);
+                qc.flush(graph);
             });
             TimeLogger.log("Flushed queries");
         } catch (DatabaseException e) {
@@ -889,6 +855,7 @@ public class SimanticsPlatform implements LifecycleListener {
         boolean loadProject = true;
         try {
 
+            monitor.setTaskName("Open database session");
                sessionContext = SimanticsPlatform.INSTANCE.createSessionContext(true);
                // This must be before setSessionContext since some listeners might query this
             sessionContext.setHint(SimanticsKeys.KEY_PROJECT, SimanticsPlatform.INSTANCE.projectResource);
@@ -896,31 +863,38 @@ public class SimanticsPlatform implements LifecycleListener {
             Simantics.setSessionContext(sessionContext);
 
             // 1. Put ResourceBinding that throws an exception to General Bindings
-            simanticsBindings = new SimanticsBindings( null );
+            simanticsBindings = new SimanticsBindings();
             Bindings.classBindingFactory.addFactory( simanticsBindings );
 
-
-            // 2. Create session-specific second Binding context (Databoard) and
-            //    put that to Session as a service
             Session session = sessionContext.getSession();
-            Databoard sessionDataboard = new Databoard();
-            session.registerService(Databoard.class, sessionDataboard);
-            simanticsBindings2 = new SimanticsBindings( session );
-            sessionDataboard.classBindingFactory.addFactory( simanticsBindings2 );
+            session.registerService(Databoard.class, Bindings.databoard);
 
             // Register datatype bindings
             Bindings.defaultBindingFactory.getRepository().put(RGB.Integer.BINDING.type(), RGB.Integer.BINDING);
             Bindings.defaultBindingFactory.getRepository().put(Font.BINDING.type(), Font.BINDING);
 
-            if(loadProject) {
+            if (support.rolledback() || sessionDescriptor.isFreshDatabase()) {
+                monitor.setTaskName("Rebuilding all indexes");
+                try {
+                    session.getService(IndexedRelations.class).fullRebuild(monitor.newChild(100), session);
+                } catch (IndexException e) {
+                    LOGGER.error("Failed to re-build all indexes", e);
+                }
+            } else {
+                monitor.worked(100);
+            }
 
-                TimeLogger.log("Load projects");
+            if(loadProject) {
+                TimeLogger.log("Load project");
+                monitor.setTaskName("Load project");
                 project = Projects.loadProject(sessionContext.getSession(), SimanticsPlatform.INSTANCE.projectResource);
-
                 sessionContext.setHint(ProjectKeys.KEY_PROJECT, project);
+                monitor.worked(100);
                 TimeLogger.log("Loading projects complete");
 
+                monitor.setTaskName("Activate project");
                 project.activate();
+                monitor.worked(100);
                 TimeLogger.log("Project activated");
             }
 
@@ -936,6 +910,15 @@ public class SimanticsPlatform implements LifecycleListener {
 
         running = true;
 
+        // #7650: improve shutdown robustness in all applications that use the platform
+        Runtime.getRuntime().addShutdownHook(shutdownHook);
+
+        // Discard database session undo history at this point to prevent
+        // the user from undoing any initialization operations performed
+        // by the platform startup.
+        SimanticsPlatform.INSTANCE.discardSessionUndoHistory();
+        TimeLogger.log("Discarded session undo history");
+
         return sessionContext;
 
     }
@@ -955,79 +938,31 @@ public class SimanticsPlatform implements LifecycleListener {
         }
     }
 
-//    private static File getIgnorePrerequisitesFile(URL workspaceUrl) {
-//        if (workspaceUrl == null)
-//            return null;
-//        return new File(workspaceUrl.getPath(), ".ignorePrerequisites");
-//    }
-//
-//    private void ensurePrerequisites(IProgressMonitor progressMonitor, PlatformUserAgent userAgent) throws PlatformException {
-//        Location loc = Platform.getInstanceLocation();
-//        File ignorePrerequisites = getIgnorePrerequisitesFile(loc.getURL());
-//        if (loc.isSet() && ignorePrerequisites != null) {
-//            if (ignorePrerequisites.exists() || ignorePrerequisites.isFile())
-//                return;
-//        }
-//
-//        try {
-//            ServerEnvironment.ensureServerDependenciesMet();
-//        } catch (ExecutionEnvironmentException e) {
-//            // Not installed properly, ask user whether to try installation.
-//            try {
-//                StringBuilder msg = new StringBuilder();
-//                msg.append("Your system seems to be missing the following prerequisites for running this application:\n\n");
-//                for (Product product : e.requiredProducts)
-//                    msg.append("\t" + product.getDescription() + "\n");
-//                msg.append("\nYou can either install the missing components now or ignore and attempt to start the application without them. Ignore Always will ignore this question for this workspace.");
-//                msg.append("\n\nSelecting Cancel will close the application.");
-//
-//                int selection = 0;
-//                if (userAgent != null) {
-//                    selection = userAgent.showPrompt("Missing Prerequisites", msg.toString(), new String[] {
-//                        "Install Pre-requisites",
-//                        "Ignore Now",
-//                        "Ignore Always",
-//                        "Cancel"
-//                    }, selection);
-//                }
-//                boolean tryInstall = false;
-//                switch (selection) {
-//                    case 0:
-//                        tryInstall = true;
-//                        break;
-//                    case 2:
-//                        ignorePrerequisites.createNewFile();
-//                    case 1:
-//                        break;
-//                    case 3:
-//                    case -1:
-//                        throw new CancelStartupException();
-//                }
-//
-//                if (tryInstall) {
-//                    // Try to install it and check for success afterwards.
-//                    ServerEnvironment.tryInstallDependencies(progressMonitor);
-//                    ServerEnvironment.ensureServerDependenciesMet();
-//                }
-//            } catch (InstallException ie) {
-//                throw new PlatformException(ie);
-//            } catch (ExecutionEnvironmentException eee) {
-//                throw new PlatformException(eee);
-//            } catch (IOException ie) {
-//                throw new PlatformException(ie);
-//            }
-//        }
-//    }
+    /**
+     * Perform normal shutdown for the Simantics Platform.
+     *
+     * @param progressMonitor optional progress monitor
+     * @throws PlatformException
+     * @see {@link #shutdown(IProgressMonitor, boolean)}
+     */
+    public synchronized void shutdown(IProgressMonitor progressMonitor) throws PlatformException {
+        shutdown(progressMonitor, true);
+    }
 
     /**
      * Shutdown Simantics Platform.
      *
-     * In SWB this is handled in SimanticsWorkbenchAdvisor#disconnectFromWorkspace.
+     * In Simantics Workbench this is handled in
+     * <code>SimanticsWorkbenchAdvisor#disconnectFromWorkspace</code>.
      *
-     * @param progressMonitor optional progress monitor
+     * @param progressMonitor
+     *            optional progress monitor
+     * @param clearTemporaryFiles
+     *            allow or prevent deletion of temporary files at the end of the
+     *            shutdown procedure
      * @throws PlatformException
      */
-    public void shutdown(IProgressMonitor progressMonitor) throws PlatformException
+    public synchronized void shutdown(IProgressMonitor progressMonitor, boolean clearTemporaryFiles) throws PlatformException
     {
         SubMonitor progress = SubMonitor.convert(progressMonitor, 100);
         PlatformException platformException = null;
@@ -1040,12 +975,9 @@ public class SimanticsPlatform implements LifecycleListener {
 
             running = false;
             progress.subTask("Close Database Session");
-            Databoard databoard = null;
             if (sessionContext != null) {
                 Session s = sessionContext.peekSession();
                 if (s != null) {
-                    databoard = s.peekService(Databoard.class);
-
                     progress.subTask("Flushing Index Caches");
                     try {
                         Simantics.flushIndexCaches(progress.newChild(20), s);
@@ -1063,13 +995,6 @@ public class SimanticsPlatform implements LifecycleListener {
                 Bindings.classBindingFactory.removeFactory( simanticsBindings );
                 simanticsBindings = null;
             }
-            if (databoard != null) {
-               if (simanticsBindings2 != null) {
-                       databoard.classBindingFactory.removeFactory( simanticsBindings2 );
-                       simanticsBindings2 = null;
-               }
-               databoard.clear();
-            }
 
             // Make sure Simantics clipboard doesn't store unwanted session data references.
             Simantics.setClipboard(new SimanticsClipboardImpl());
@@ -1096,15 +1021,20 @@ public class SimanticsPlatform implements LifecycleListener {
         }
         progress.worked(10);
 
-        progress.subTask("Clearing Workspace Temporary Directory");
-        try {
-            Simantics.clearTemporaryDirectory();
-        } catch (Throwable t) {
-            LOGGER.error("Failed to clear the temporary directory.", t);
+        if (clearTemporaryFiles) {
+            progress.subTask("Clearing Workspace Temporary Directory");
+            try {
+                Simantics.clearTemporaryDirectory();
+            } catch (Throwable t) {
+                LOGGER.error("Failed to clear the temporary directory.", t);
+            }
         }
         progress.worked(10);
         if (null != platformException)
             throw platformException;
+
+        // #7650: improve shutdown robustness in all applications that use the platform
+        Runtime.getRuntime().removeShutdownHook(shutdownHook);
     }
 
     // TODO: consider removing this in the future ??