-/*******************************************************************************\r
- * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
- * in Industry THTH ry.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- * VTT Technical Research Centre of Finland - initial API and implementation\r
- *******************************************************************************/\r
-package org.simantics;\r
-\r
-import static org.simantics.db.common.utils.Transaction.commit;\r
-import static org.simantics.db.common.utils.Transaction.endTransaction;\r
-import static org.simantics.db.common.utils.Transaction.readGraph;\r
-import static org.simantics.db.common.utils.Transaction.startTransaction;\r
-import static org.simantics.db.common.utils.Transaction.writeGraph;\r
-\r
-import java.io.File;\r
-import java.io.IOException;\r
-import java.util.ArrayList;\r
-import java.util.Collection;\r
-import java.util.HashMap;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.Map.Entry;\r
-import java.util.Properties;\r
-import java.util.Set;\r
-import java.util.TreeMap;\r
-import java.util.UUID;\r
-\r
-import org.eclipse.core.runtime.ILog;\r
-import org.eclipse.core.runtime.IProduct;\r
-import org.eclipse.core.runtime.IProgressMonitor;\r
-import org.eclipse.core.runtime.IStatus;\r
-import org.eclipse.core.runtime.NullProgressMonitor;\r
-import org.eclipse.core.runtime.Platform;\r
-import org.eclipse.core.runtime.Status;\r
-import org.eclipse.core.runtime.SubMonitor;\r
-import org.eclipse.osgi.service.resolver.BundleDescription;\r
-import org.simantics.databoard.Bindings;\r
-import org.simantics.databoard.Databoard;\r
-import org.simantics.datatypes.literal.Font;\r
-import org.simantics.datatypes.literal.RGB;\r
-import org.simantics.db.Driver;\r
-import org.simantics.db.Driver.Management;\r
-import org.simantics.db.Manager;\r
-import org.simantics.db.ReadGraph;\r
-import org.simantics.db.Resource;\r
-import org.simantics.db.Session;\r
-import org.simantics.db.SessionModel;\r
-import org.simantics.db.UndoContext;\r
-import org.simantics.db.VirtualGraph;\r
-import org.simantics.db.WriteGraph;\r
-import org.simantics.db.common.request.ObjectsWithType;\r
-import org.simantics.db.common.request.Queries;\r
-import org.simantics.db.common.request.WriteRequest;\r
-import org.simantics.db.common.request.WriteResultRequest;\r
-import org.simantics.db.common.utils.Transaction;\r
-import org.simantics.db.exception.ClusterSetExistException;\r
-import org.simantics.db.exception.DatabaseException;\r
-import org.simantics.db.exception.ResourceNotFoundException;\r
-import org.simantics.db.indexing.DatabaseIndexing;\r
-import org.simantics.db.layer0.genericrelation.DependenciesRelation;\r
-import org.simantics.db.layer0.util.SimanticsClipboardImpl;\r
-import org.simantics.db.layer0.util.SimanticsKeys;\r
-import org.simantics.db.layer0.util.TGTransferableGraphSource;\r
-import org.simantics.db.layer0.variable.VariableRepository;\r
-import org.simantics.db.management.SessionContext;\r
-import org.simantics.db.request.Read;\r
-import org.simantics.db.service.LifecycleSupport.LifecycleListener;\r
-import org.simantics.db.service.LifecycleSupport.LifecycleState;\r
-import org.simantics.db.service.QueryControl;\r
-import org.simantics.db.service.UndoRedoSupport;\r
-import org.simantics.db.service.VirtualGraphSupport;\r
-import org.simantics.db.service.XSupport;\r
-import org.simantics.graph.db.GraphDependencyAnalyzer;\r
-import org.simantics.graph.db.GraphDependencyAnalyzer.IU;\r
-import org.simantics.graph.db.GraphDependencyAnalyzer.IdentityNode;\r
-import org.simantics.graph.db.IImportAdvisor;\r
-import org.simantics.graph.db.TransferableGraphs;\r
-import org.simantics.graph.diff.Diff;\r
-import org.simantics.graph.diff.TransferableGraphDelta1;\r
-import org.simantics.internal.Activator;\r
-import org.simantics.internal.startup.StartupExtensions;\r
-import org.simantics.layer0.Layer0;\r
-import org.simantics.operation.Layer0X;\r
-import org.simantics.project.IProject;\r
-import org.simantics.project.ProjectFeatures;\r
-import org.simantics.project.ProjectKeys;\r
-import org.simantics.project.Projects;\r
-import org.simantics.project.exception.ProjectException;\r
-import org.simantics.project.features.registry.GroupReference;\r
-import org.simantics.project.management.DatabaseManagement;\r
-import org.simantics.project.management.GraphBundle;\r
-import org.simantics.project.management.GraphBundleEx;\r
-import org.simantics.project.management.GraphBundleRef;\r
-import org.simantics.project.management.PlatformUtil;\r
-import org.simantics.project.management.ServerManager;\r
-import org.simantics.project.management.ServerManagerFactory;\r
-import org.simantics.project.management.WorkspaceUtil;\r
-import org.simantics.utils.FileUtils;\r
-import org.simantics.utils.datastructures.Pair;\r
-import org.simantics.utils.logging.TimeLogger;\r
-\r
-/**\r
- * SimanticsPlatform performs procedures required in order to get simantics\r
- * workbench into operational state. This consists of the following steps:\r
- * <ul>\r
- * <li> Asserting there is Database\r
- * </li>\r
- * <li> Starting Database process\r
- * </li>\r
- * <li> Opening a session to Database process\r
- * </li>\r
- * <li> Asserting required ontologies or other transferable graphs are installed in the database\r
- * </li>\r
- * <li> Asserting required project is installed in the database\r
- * </li>\r
- * <li> Asserting Simantics Features are installed in the database\r
- * </li>\r
- * <li> Asserting Simantics Features are installed to the project\r
- * </li>\r
- * <li> Shutdown: Save Session, Close session, Kill Database process\r
- * </li>\r
- * </ul>\r
- *\r
- * @author Toni Kalajainen <toni.kalajainen@vtt.fi>\r
- */\r
-public class SimanticsPlatform implements LifecycleListener {\r
-\r
- /**\r
- * The policy is relevant when developing Simantics from Eclipse IDE.\r
- * It is applied when the ontology in the database of a workspace doesn't match\r
- * a newer ontology in the Eclipse workspace.\r
- */\r
- public static enum OntologyRecoveryPolicy { ThrowError, Merge, ReinstallDatabase }\r
-\r
- /**\r
- * This policy dictates how the Simantics platform startup should react if\r
- * the started workspace is not set up properly. The alternatives are to\r
- * just throw an error and fail or to attempt all possible measures to fix\r
- * the encountered problems.\r
- */\r
- public static enum RecoveryPolicy { ThrowError, FixError }\r
-\r
- /** Singleton instance, started in SimanticsWorkbenchAdvisor */\r
- public static final SimanticsPlatform INSTANCE = new SimanticsPlatform();\r
-\r
- /** Set to true when the Simantics Platform is in good-and-go condition */\r
- public boolean running;\r
-\r
- /** Database Session */\r
- public Session session;\r
- private Management databasebManagement;\r
-\r
- /** Database session context */\r
- public SessionContext sessionContext;\r
-\r
- /** Project identifier in Database */\r
- public String projectURI;\r
-\r
- /** Project name */\r
- public String projectName;\r
-\r
- /** Project resource */\r
- public Resource projectResource;\r
-\r
- /** Session specific bindings */\r
- public SimanticsBindings simanticsBindings;\r
- public SimanticsBindings simanticsBindings2;\r
-\r
- public Thread mainThread;\r
-\r
- /**\r
- * The {@link IProject} activated by\r
- * {@link #startUp(IProgressMonitor, RecoveryPolicy, OntologyRecoveryPolicy, ServerAddress, PlatformUserAgent)}\r
- */\r
- private IProject project;\r
-\r
- protected ILog log;\r
-\r
- /**\r
- * Create a new simantics plaform manager in uninitialized state and\r
- * with default policies. <p>\r
- */\r
- public SimanticsPlatform() {\r
- log = Platform.getLog(Activator.getBundleContext().getBundle());\r
- mainThread = Thread.currentThread();\r
- }\r
-\r
- public String getApplicationClientId() {\r
- IProduct product = Platform.getProduct();\r
- if(product == null) return "noProduct";//UUID.randomUUID().toString();\r
- String application = product.getApplication();\r
- return application != null ? application : UUID.randomUUID().toString();\r
- }\r
-\r
- private Session setupDatabase(String databaseDriverId, IProgressMonitor progressMonitor, RecoveryPolicy workspacePolicy, PlatformUserAgent userAgent) throws PlatformException {\r
- if (progressMonitor == null)\r
- progressMonitor = new NullProgressMonitor();\r
- File dbLocation = Platform.getLocation().append("db").toFile();\r
- ServerManager serverManager;\r
- try {\r
- serverManager = ServerManagerFactory.create(databaseDriverId, dbLocation.getAbsolutePath());\r
- } catch (DatabaseException | IOException e) {\r
- throw new PlatformException("Failed to initialize Server Manager", e);\r
- }\r
- progressMonitor.beginTask("Setting up Simantics Database", 100);\r
- progressMonitor.setTaskName("Asserting Database is installed.");\r
- String msg = "Failed to initialize Simantics database.";\r
- try {\r
- // Create database\r
- log.log(new Status(IStatus.INFO, Activator.PLUGIN_ID, "Creating database at " + dbLocation));\r
- progressMonitor.setTaskName("Creating database at " + dbLocation);\r
- databasebManagement = serverManager.getManagement(dbLocation);\r
- databasebManagement.create();\r
- // Create layer0.\r
- return serverManager.createDatabase(dbLocation);\r
- } catch (DatabaseException e) {\r
- throw new PlatformException(msg, e);\r
- } catch (Throwable e) {\r
- throw new PlatformException(msg, e);\r
- } finally {\r
- progressMonitor.worked(20);\r
- }\r
- }\r
-\r
- public void synchronizeOntologies(IProgressMonitor progressMonitor, OntologyRecoveryPolicy ontologyPolicy, boolean requireSynchronize) throws PlatformException {\r
-\r
- if (progressMonitor == null) progressMonitor = new NullProgressMonitor();\r
-\r
- final DatabaseManagement mgmt = new DatabaseManagement();\r
-\r
- PlatformUtil.compileAllDynamicOntologies();\r
-\r
- progressMonitor.setTaskName("Asserting all ontologies are installed");\r
- final Map<GraphBundleRef, GraphBundleEx> platformTGs = new HashMap<GraphBundleRef, GraphBundleEx>();\r
- try {\r
-\r
- // Get a list of bundles installed into the database\r
- progressMonitor.subTask("find installed bundles from database");\r
- Map<GraphBundleRef, GraphBundleEx> installedTGs = new HashMap<GraphBundleRef, GraphBundleEx>();\r
- for (GraphBundle b : session.syncRequest( mgmt.GraphBundleQuery )) {\r
- installedTGs.put(GraphBundleRef.of(b), GraphBundleEx.extend(b));\r
- }\r
-\r
- if(!requireSynchronize && installedTGs.size() > 1 && !Platform.inDevelopmentMode()) return;\r
-// if(installedTGs.size() > 1) return;\r
-\r
- // Get a list of all bundles in the platform (Bundle Context)\r
- List<GraphBundle> tgs = new ArrayList<GraphBundle>();\r
- progressMonitor.subTask("load all transferable graphs from platform");\r
- PlatformUtil.getAllGraphs(tgs);\r
- progressMonitor.subTask("extend bundles to compile versions");\r
- for (GraphBundle b : tgs) {\r
- GraphBundleEx gbe = GraphBundleEx.extend(b);\r
- gbe.build();\r
- platformTGs.put(GraphBundleRef.of(b), gbe);\r
- }\r
-\r
- // Compile a list of TGs that need to be installed or reinstalled in the database\r
- progressMonitor.subTask("check bundle reinstallation demand");\r
- List<GraphBundleEx> installTGs = new ArrayList<GraphBundleEx>();\r
- // Create list of TGs to update, <newTg, oldTg>\r
- Map<GraphBundleEx,GraphBundleEx> reinstallTGs = new TreeMap<GraphBundleEx,GraphBundleEx>();\r
- for (Entry<GraphBundleRef, GraphBundleEx> e : platformTGs.entrySet()) {\r
- GraphBundleRef key = e.getKey();\r
- GraphBundleEx platformBundle = e.getValue();\r
- GraphBundleEx existingBundle = installedTGs.get(key);\r
- \r
-// System.out.println("GraphBundleRef key=" + key.toString());\r
- \r
- if (existingBundle == null) {\r
- // Bundle did not exist in the database, put it into list of bundles to install\r
- installTGs.add(platformBundle);\r
- }\r
- else {\r
- // Bundle exists in the database\r
- boolean platformBundleIsNewer = existingBundle.getVersion().compareTo(platformBundle.getVersion())<0;\r
- if (!platformBundleIsNewer)\r
- continue;\r
- // Check hash of transferable graph to know whether to update or not.\r
- if (platformBundle.getHashcode() == existingBundle.getHashcode())\r
- continue;\r
- //System.out.println("Ontology hashcodes do not match: platform bundle="\r
- // + platformBundle.getVersionedId() + ", hash=" + platformBundle.getHashcode()\r
- // + "; existing bundle=" + existingBundle.getVersionedId() + ", hash=" + existingBundle.getHashcode());\r
- reinstallTGs.put(platformBundle, existingBundle);\r
- }\r
- }\r
- // INSTALL\r
- // Database is missing graphs\r
- if (!installTGs.isEmpty() || !reinstallTGs.isEmpty()) {\r
- session.getService(XSupport.class).setServiceMode(true, true);\r
-\r
- // Throw error\r
- if (ontologyPolicy == OntologyRecoveryPolicy.ThrowError) {\r
- StringBuilder sb = new StringBuilder("The following graphs are not installed in the database: ");\r
- if (!installTGs.isEmpty()) {\r
- int i = 0;\r
- for (GraphBundleEx e : installTGs) {\r
- if (i>0) sb.append(", ");\r
- i++;\r
- sb.append(e.toString());\r
- }\r
- sb.append(" is missing from the database.\n");\r
- }\r
- if (!reinstallTGs.isEmpty()) {\r
- int i = 0;\r
- for (Entry<GraphBundleEx, GraphBundleEx> e : reinstallTGs.entrySet()) {\r
- if (i>0) sb.append(", ");\r
- i++;\r
- sb.append(e.getKey().toString());\r
- }\r
- sb.append(" Database/Platform Bundle version mismatch.\n");\r
- }\r
- sb.append("Hint: Use -fixErrors to install the graphs.");\r
- throw new PlatformException(sb.toString());\r
- }\r
- // Reinstall database\r
- if (ontologyPolicy == OntologyRecoveryPolicy.ReinstallDatabase) {\r
- log.log(new Status(IStatus.INFO, Activator.PLUGIN_ID, "Reinstalling the database."));\r
- // TODO Install DB\r
- // Stop Session\r
- // Kill Process\r
- // Delete Database\r
- // Create Database\r
- // Start Database\r
- // Open Session\r
- // Install TGs\r
- throw new PlatformException("Reinstalling Database, NOT IMPLEMENTED");\r
- }\r
-\r
- if (ontologyPolicy == OntologyRecoveryPolicy.Merge) {\r
- progressMonitor.subTask("Merging ontology changes");\r
- // Sort missing TGs into install order\r
- GraphDependencyAnalyzer<GraphBundle> analyzer = new GraphDependencyAnalyzer<GraphBundle>();\r
- for(GraphBundle tg : installTGs) analyzer.addGraph(tg, tg.getGraph());\r
- for(GraphBundle tg : reinstallTGs.keySet()) analyzer.addGraph(tg, tg.getGraph());\r
- if(!analyzer.analyzeDependency()) {\r
- Collection<Pair<GraphBundle, GraphBundle>> problems = analyzer.getConflicts();\r
- StringBuilder sb = new StringBuilder();\r
- for (Pair<GraphBundle, GraphBundle> problem : problems) {\r
- sb.append("Conflict with "+problem.first+" and "+problem.second+".\n");\r
- }\r
- throw new PlatformException(sb.toString());\r
- }\r
- else if(!session.syncRequest( analyzer.queryExternalDependenciesSatisfied )) {\r
- Collection<IdentityNode> unsatisfiedDependencies = analyzer.getUnsatisfiedDependencies();\r
- StringBuilder sb = new StringBuilder();\r
- for (IdentityNode dep: unsatisfiedDependencies) {\r
- sb.append("Unsatisfied Dependency "+dep+". Required by\n");\r
- for(IU iu : GraphDependencyAnalyzer.toCollection(dep.getRequires())) {\r
- sb.append(" " + ((GraphBundle)iu.getId()).getId() + "\n");\r
- }\r
- }\r
- throw new PlatformException(sb.toString());\r
- }\r
- \r
- List<GraphBundle> sortedBundles = analyzer.getSortedGraphs();\r
- if(!sortedBundles.isEmpty()) {\r
- \r
- session.syncRequest(new WriteRequest() {\r
- @Override\r
- public void perform(WriteGraph graph) throws DatabaseException {\r
- try {\r
- graph.newClusterSet(graph.getRootLibrary());\r
- } catch (ClusterSetExistException e) {\r
- // Cluster set exist already, no problem.\r
- }\r
- graph.setClusterSet4NewResource(graph.getRootLibrary());\r
- graph.flushCluster();\r
- }\r
- });\r
-\r
- boolean mergedOntologies = false;\r
-\r
- // Install TGs\r
- for(final GraphBundle tg : sortedBundles) {\r
-\r
- final IImportAdvisor advisor = new OntologyImportAdvisor(tg, mgmt);\r
- final GraphBundle oldTG = reinstallTGs.get(tg);\r
-\r
- if (oldTG==null) {\r
- \r
- boolean createImmutable = tg.getImmutable();\r
- session.getService(XSupport.class).setServiceMode(true, createImmutable);\r
-\r
- // Install TG\r
- log.log(new Status(IStatus.INFO, Activator.PLUGIN_ID, "Installing "+tg.toString()+" - "+tg.getName()));\r
- TransferableGraphs.importGraph1(session, new TGTransferableGraphSource(tg.getGraph()), advisor, null);\r
- } else {\r
- // Merge TG\r
- startTransaction(session, false);\r
- TransferableGraphDelta1 delta = new Diff(oldTG.getGraph(), tg.getGraph()).diff();\r
- final long[] oldResources = oldTG.getResourceArray();\r
- boolean changes = TransferableGraphs.hasChanges(readGraph(), oldResources, delta);\r
- endTransaction();\r
- if (!changes) {\r
- //log.log(new Status(IStatus.INFO, Activator.PLUGIN_ID, "Nothing to merge for "+tg.toString()));\r
- continue;\r
- }\r
-\r
- log.log(new Status(IStatus.INFO, Activator.PLUGIN_ID, "Merging new version of "+tg.toString()));\r
-\r
- startTransaction(session, true);\r
- \r
- //delta.print();\r
- try {\r
- \r
- \r
- long[] resourceArray = TransferableGraphs.applyDelta(writeGraph(), oldResources, delta);\r
- tg.setResourceArray(resourceArray);\r
- mgmt.setGraphBundleEntry(tg);\r
- commit();\r
- mergedOntologies = true;\r
- } catch (Throwable t) {\r
- throw new PlatformException(t);\r
- } finally {\r
- endTransaction();\r
- }\r
- }\r
- }\r
- \r
- session.syncRequest(new WriteRequest() {\r
- @Override\r
- public void perform(WriteGraph graph) throws DatabaseException {\r
- graph.setClusterSet4NewResource(graph.getRootLibrary());\r
- graph.flushCluster();\r
- }\r
- });\r
-\r
- if (mergedOntologies)\r
- DatabaseIndexing.deleteAllIndexes();\r
- }\r
-\r
- TimeLogger.log("Ontologies synchronized.");\r
- \r
- }\r
- session.getService(XSupport.class).setServiceMode(false, false);\r
- }\r
- progressMonitor.worked(20);\r
- } catch (IOException e) {\r
- throw new PlatformException(e);\r
- } catch (DatabaseException e) {\r
- throw new PlatformException(e);\r
- }\r
-\r
- }\r
-\r
- public boolean assertConfiguration(IProgressMonitor progressMonitor, RecoveryPolicy workspacePolicy) throws PlatformException {\r
-\r
- if (progressMonitor == null) progressMonitor = new NullProgressMonitor();\r
-\r
- File workspaceLocation = Platform.getLocation().toFile();\r
-\r
- boolean installProject = false;\r
- progressMonitor.setTaskName("Asserting simantics.cfg is installed");\r
- try {\r
- File propertyFile = new File(workspaceLocation, "simantics.cfg");\r
- Properties properties;\r
- try {\r
- properties = WorkspaceUtil.readProperties(propertyFile);\r
- } catch (IOException e) {\r
- if (workspacePolicy == RecoveryPolicy.ThrowError) throw new PlatformException("Could not load "+propertyFile);\r
-\r
- // Create a project and write Property file\r
- properties = new Properties();\r
- properties.setProperty("project_uri", "http://Projects/Development%20Project");\r
- properties.setProperty("project_name", "Development Project");\r
- WorkspaceUtil.writeProperties(propertyFile, properties);\r
- installProject |= true;\r
- }\r
- projectURI = properties.getProperty("project_uri");\r
- projectName = properties.getProperty("project_name");\r
- progressMonitor.worked(10);\r
- } catch (IOException e) {\r
- throw new PlatformException(e);\r
- }\r
-\r
- return installProject;\r
-\r
- }\r
-\r
- public boolean assertProject(IProgressMonitor progressMonitor, RecoveryPolicy workspacePolicy, boolean installProject) throws PlatformException {\r
-\r
- if (progressMonitor == null) progressMonitor = new NullProgressMonitor();\r
-\r
- final DatabaseManagement mgmt = new DatabaseManagement();\r
-\r
- progressMonitor.setTaskName("Asserting project resource exists in the database");\r
- try {\r
- projectResource = session.syncRequest( Queries.resource( projectURI ) );\r
- } catch (ResourceNotFoundException nfe) {\r
- // Project was not found\r
- if (workspacePolicy == RecoveryPolicy.ThrowError)\r
- throw new PlatformException("Project Resource "+projectURI+" is not found in the database.");\r
- // Create empty project with no features\r
- try {\r
- Transaction.startTransaction(session, true);\r
- try {\r
- // The project needs to be created mutable.\r
- session.getService(XSupport.class).setServiceMode(true, false);\r
-\r
- ArrayList<String> empty = new ArrayList<String>();\r
- projectResource = mgmt.createProject(projectName, empty);\r
- installProject |= true;\r
-\r
- session.getService(XSupport.class).setServiceMode(false, false);\r
- Transaction.commit();\r
- } finally {\r
- Transaction.endTransaction();\r
- }\r
- //session.getService( LifecycleSupport.class ).save();\r
- } catch (DatabaseException e) {\r
- throw new PlatformException("Failed to create "+projectURI, e);\r
- }\r
- } catch (DatabaseException e) {\r
- throw new PlatformException("Failed to create "+projectURI, e);\r
- }\r
- progressMonitor.worked(10);\r
-\r
- return installProject;\r
-\r
- }\r
-\r
- public void updateInstalledGroups(IProgressMonitor progressMonitor, boolean installProject) throws PlatformException {\r
-\r
- if (installProject)\r
- {\r
- // Attach all feature groups available in platform to created project\r
- progressMonitor.setTaskName("Install all features");\r
- Set<GroupReference> publishedFeatureGroups = ProjectFeatures.getInstallGroupsOfPublishedFeatures();\r
- Collection<GroupReference> groupsWithoutVersion = GroupReference.stripVersions(publishedFeatureGroups);\r
-\r
- // final List<String> Platform_Features = new ArrayList<String>();\r
- //\r
- // // Convert graph instances\r
- // Collection<TransferableGraph1> platformGraphs = new ArrayList<TransferableGraph1>();\r
- // for (GraphBundleEx e : platformTGs.values()) platformGraphs.add( e.getGraph() );\r
- // IGraph graph = Graphs.createGraph(platformGraphs);\r
- //\r
- // Res PublishedProjectFeatures = UriUtils.uriToPath( ProjectResource.URIs.PublishedProjectFeatures );\r
- // Path HasFeature = UriUtils.uriToPath( ProjectResource.URIs.HasFeature );\r
- // for(Res feature : graph.getObjects(PublishedProjectFeatures, HasFeature)) {\r
- // System.out.println("Installing Project Feature: "+feature.toString());\r
- // Platform_Features.add( feature.toString() );\r
- // }\r
-\r
- try {\r
- Transaction.startTransaction(session, true);\r
- try {\r
- // for (String feature : Platform_Features) {\r
- // try {\r
- // getResource(feature);\r
- // } catch(ResourceNotFoundException e) {\r
- // System.out.println(feature+" not found");\r
- // }\r
- // mgmt.installFeature(projectResource, feature);\r
- // }\r
- Projects.setProjectInstalledGroups(writeGraph(), projectResource, groupsWithoutVersion);\r
- Transaction.commit();\r
- } finally {\r
- Transaction.endTransaction();\r
- }\r
- //session.getService( LifecycleSupport.class ).save();\r
- } catch(DatabaseException ae) {\r
- throw new PlatformException("Failed to install features", ae);\r
- }\r
- progressMonitor.worked(10);\r
- }\r
-\r
- }\r
-\r
- public void assertSessionModel(IProgressMonitor progressMonitor) throws PlatformException {\r
-\r
- Properties properties = session.getService(Properties.class);\r
- final String clientId = properties.getProperty("clientId");\r
-\r
- try {\r
-\r
- // Currently this needs to be done before data becomes available\r
- VirtualGraphSupport support = session.getService(VirtualGraphSupport.class);\r
- VirtualGraph activations = support.getWorkspacePersistent("activations");\r
-\r
- Resource sessionModel = session.syncRequest(new Read<Resource>() {\r
-\r
- @Override\r
- public Resource perform(ReadGraph graph) throws DatabaseException {\r
-\r
- Layer0X L0X = Layer0X.getInstance(graph);\r
- for(Resource sessionModel : graph.syncRequest(new ObjectsWithType(graph.getRootLibrary(), L0X.HasSession, L0X.Session))) {\r
- String id = graph.getPossibleRelatedValue(sessionModel, L0X.Session_HasClientId);\r
- if(id != null && id.equals(clientId)) return sessionModel;\r
- }\r
- return null;\r
-\r
- }\r
-\r
- });\r
-\r
- if(sessionModel == null) {\r
-\r
- sessionModel = session.syncRequest(new WriteResultRequest<Resource>(activations) {\r
-\r
- @Override\r
- public Resource perform(WriteGraph graph) throws DatabaseException {\r
- Layer0 L0 = Layer0.getInstance(graph);\r
- Layer0X L0X = Layer0X.getInstance(graph);\r
- Resource session = graph.newResource();\r
- graph.claim(session, L0.InstanceOf, null, L0X.Session);\r
- graph.claim(session, L0X.Session_HasUser, null, graph.getResource("http://Users/AdminUser"));\r
- graph.addLiteral(session, L0X.Session_HasClientId, L0X.Session_HasClientId_Inverse, clientId, Bindings.STRING);\r
- graph.claim(graph.getRootLibrary(), L0X.HasSession, session);\r
- return session;\r
- }\r
- });\r
-\r
- }\r
-\r
- session.registerService(SessionModel.class, new PlatformSessionModel(sessionModel));\r
- } catch (DatabaseException e) {\r
- throw new PlatformException(e);\r
- }\r
-\r
- }\r
-\r
- static class PlatformSessionModel implements SessionModel {\r
- private final Resource sessionModel;\r
-\r
- public PlatformSessionModel(Resource model) {\r
- this.sessionModel = model;\r
- }\r
-\r
- @Override\r
- public Resource getResource() {\r
- return sessionModel;\r
- }\r
- }\r
-\r
- public void resetDatabase(IProgressMonitor monitor) throws PlatformException {\r
- File dbLocation = Platform.getLocation().append("db").toFile();\r
- if(!dbLocation.exists()) return;\r
- try { // Load driver\r
- Driver driver = Manager.getDriver("procore");\r
- Management management = driver.getManagement(dbLocation.getAbsolutePath(), null);\r
- management.delete();\r
- } catch (DatabaseException e) {\r
- throw new PlatformException("Failed to remove database at " + dbLocation.getAbsolutePath(), e);\r
- }\r
- // We have created extra files to database folder which have to be deleted also.\r
- // This is an awful idea! Do not create extra files to database folder!\r
- Throwable t = null;\r
- for (int i=0; i<10; ++i) {\r
- try {\r
- FileUtils.deleteAll(dbLocation);\r
- t = null;\r
- break;\r
- } catch (IOException e) {\r
- // Assuming this has been thrown because delete file/folder failed.\r
- t = e;\r
- }\r
- try {\r
- Thread.sleep(200);\r
- } catch (InterruptedException e) {\r
- // Ignoring interrupted exception.\r
- }\r
- }\r
- if (null != t)\r
- throw new PlatformException("Failed to remove database folder at " + dbLocation.getAbsolutePath(), t);\r
- }\r
- public void resetWorkspace(IProgressMonitor monitor, ArrayList<String> fileFilter) throws PlatformException, IllegalStateException, IOException {\r
- File file = Platform.getLocation().toFile();\r
- if (null != fileFilter)\r
- FileUtils.deleteAllWithFilter(file , fileFilter);\r
- resetDatabase(monitor);\r
- }\r
-\r
- /**\r
- * Start-up the platform. The procedure consists of 8 steps. Once everything\r
- * is up and running, all fields are set property.\r
- * <p>\r
- *\r
- * If workspacePolicy is FixErrors, there is an attempt to fix unexpected\r
- * errors. It includes installing database files, installing ontologies, and\r
- * installing project features.\r
- * <p>\r
- *\r
- * In SWB this is handled in SimanticsWorkbenchAdvisor#openWindows().\r
- * <p>\r
- *\r
- * If remote server is given, simantics plaform takes connection there\r
- * instead of local server at "db/".\r
- *\r
- * @param workspacePolicy action to take on workspace/database related\r
- * errors\r
- * @param ontologyPolicy action to take on ontology mismatch\r
- * @param progressMonitor optional progress monitor\r
- * @param userAgent interface for resorting to user feedback during platform\r
- * startup or <code>null</code> to resort to default measures\r
- * @throws PlatformException\r
- */\r
- public SessionContext startUp(String databaseDriverId, IProgressMonitor progressMonitor, RecoveryPolicy workspacePolicy,\r
- OntologyRecoveryPolicy ontologyPolicy, boolean requireSynchronize, PlatformUserAgent userAgent)\r
- throws PlatformException\r
- {\r
-\r
- assert(!running);\r
- TimeLogger.log("Beginning of SimanticsPlatform.startUp");\r
-\r
- if (progressMonitor == null) progressMonitor = new NullProgressMonitor();\r
-\r
- // For debugging on what kind of platform automatic tests are running in\r
- // case there are problems.\r
- if ("true".equals(System.getProperty("org.simantics.dumpBundleState")))\r
- dumpPlatformBundleState();\r
-\r
- // 0. Consult all startup extensions before doing anything with the workspace.\r
- StartupExtensions.consultStartupExtensions();\r
- TimeLogger.log("Consulted platform pre-startup extensions");\r
-\r
- // 0.1. Clear all temporary files\r
- Simantics.clearTemporaryDirectory();\r
- TimeLogger.log("Cleared temporary directory");\r
-\r
- // 0.2 Clear VariableRepository.repository static map which holds references to SessionImplDb\r
- VariableRepository.clear();\r
- \r
- // 1. Assert there is a database at <workspace>/db\r
- session = setupDatabase(databaseDriverId, progressMonitor, workspacePolicy, userAgent);\r
- TimeLogger.log("Database setup complete");\r
-\r
- // 2. Assert all graphs, and correct versions, are installed to the database\r
- synchronizeOntologies(progressMonitor, ontologyPolicy, requireSynchronize);\r
- TimeLogger.log("Synchronized ontologies");\r
-\r
- // 4. Assert simantics.cfg exists\r
- boolean installProject = assertConfiguration(progressMonitor,workspacePolicy);\r
-\r
- // 5. Assert Project Resource is installed in the database\r
- installProject = assertProject(progressMonitor, workspacePolicy, installProject);\r
-\r
- // 6. Install all features into project, if in debug mode\r
- updateInstalledGroups(progressMonitor, installProject);\r
- TimeLogger.log("Installed all features into project");\r
-\r
- // 7. Assert L0.Session in database for this session\r
- assertSessionModel(progressMonitor);\r
-\r
- session.getService(XSupport.class).setServiceMode(false, false);\r
-\r
- try {\r
- session.sync(new WriteRequest() {\r
-\r
- @Override\r
- public void perform(WriteGraph graph) throws DatabaseException {\r
- QueryControl qc = graph.getService(QueryControl.class);\r
- qc.flush(graph);\r
- }\r
-\r
- });\r
- TimeLogger.log("Flushed queries");\r
- } catch (DatabaseException e) {\r
- Logger.defaultLogError(e);\r
- }\r
- boolean loadProject = true;\r
- try {\r
-\r
- sessionContext = SimanticsPlatform.INSTANCE.createSessionContext(true);\r
- // This must be before setSessionContext since some listeners might query this\r
- sessionContext.setHint(SimanticsKeys.KEY_PROJECT, SimanticsPlatform.INSTANCE.projectResource);\r
-\r
- Simantics.setSessionContext(sessionContext);\r
-\r
- // 1. Put ResourceBinding that throws an exception to General Bindings\r
- simanticsBindings = new SimanticsBindings( null );\r
- Bindings.classBindingFactory.addFactory( simanticsBindings );\r
-\r
-\r
- // 2. Create session-specific second Binding context (Databoard) and\r
- // put that to Session as a service\r
- Session session = sessionContext.getSession();\r
- Databoard sessionDataboard = new Databoard();\r
- session.registerService(Databoard.class, sessionDataboard);\r
- simanticsBindings2 = new SimanticsBindings( session );\r
- sessionDataboard.classBindingFactory.addFactory( simanticsBindings2 );\r
-\r
- // Register datatype bindings\r
- Bindings.defaultBindingFactory.getRepository().put(RGB.Integer.BINDING.type(), RGB.Integer.BINDING);\r
- Bindings.defaultBindingFactory.getRepository().put(Font.BINDING.type(), Font.BINDING);\r
-\r
- if(loadProject) {\r
-\r
- TimeLogger.log("Load projects");\r
- project = Projects.loadProject(sessionContext.getSession(), SimanticsPlatform.INSTANCE.projectResource);\r
-\r
- sessionContext.setHint(ProjectKeys.KEY_PROJECT, project);\r
- TimeLogger.log("Loading projects complete");\r
-\r
- project.activate();\r
- TimeLogger.log("Project activated");\r
- }\r
-\r
- } catch (DatabaseException e) {\r
- Logger.defaultLogError(e);\r
- throw new PlatformException(e);\r
- } catch (ProjectException e) {\r
- boolean hasStackTrace = e.getStackTrace().length > 0;\r
- if (!hasStackTrace)\r
- throw new PlatformException(e.getMessage(), hasStackTrace);\r
- throw new PlatformException(e, hasStackTrace);\r
- }\r
-\r
- running = true;\r
-\r
- return sessionContext;\r
-\r
- }\r
-\r
- public SessionContext createSessionContext(boolean init) throws PlatformException {\r
- try {\r
- // Construct and initialize SessionContext from Session.\r
- SessionContext sessionContext = SessionContext.create(session, init);\r
- if (init)\r
- sessionContext.registerServices();\r
- return sessionContext;\r
- } catch (DatabaseException e) {\r
- throw new PlatformException(e);\r
- }\r
- }\r
-\r
-// private static File getIgnorePrerequisitesFile(URL workspaceUrl) {\r
-// if (workspaceUrl == null)\r
-// return null;\r
-// return new File(workspaceUrl.getPath(), ".ignorePrerequisites");\r
-// }\r
-//\r
-// private void ensurePrerequisites(IProgressMonitor progressMonitor, PlatformUserAgent userAgent) throws PlatformException {\r
-// Location loc = Platform.getInstanceLocation();\r
-// File ignorePrerequisites = getIgnorePrerequisitesFile(loc.getURL());\r
-// if (loc.isSet() && ignorePrerequisites != null) {\r
-// if (ignorePrerequisites.exists() || ignorePrerequisites.isFile())\r
-// return;\r
-// }\r
-//\r
-// try {\r
-// ServerEnvironment.ensureServerDependenciesMet();\r
-// } catch (ExecutionEnvironmentException e) {\r
-// // Not installed properly, ask user whether to try installation.\r
-// try {\r
-// StringBuilder msg = new StringBuilder();\r
-// msg.append("Your system seems to be missing the following prerequisites for running this application:\n\n");\r
-// for (Product product : e.requiredProducts)\r
-// msg.append("\t" + product.getDescription() + "\n");\r
-// 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.");\r
-// msg.append("\n\nSelecting Cancel will close the application.");\r
-//\r
-// int selection = 0;\r
-// if (userAgent != null) {\r
-// selection = userAgent.showPrompt("Missing Prerequisites", msg.toString(), new String[] {\r
-// "Install Pre-requisites",\r
-// "Ignore Now",\r
-// "Ignore Always",\r
-// "Cancel"\r
-// }, selection);\r
-// }\r
-// boolean tryInstall = false;\r
-// switch (selection) {\r
-// case 0:\r
-// tryInstall = true;\r
-// break;\r
-// case 2:\r
-// ignorePrerequisites.createNewFile();\r
-// case 1:\r
-// break;\r
-// case 3:\r
-// case -1:\r
-// throw new CancelStartupException();\r
-// }\r
-//\r
-// if (tryInstall) {\r
-// // Try to install it and check for success afterwards.\r
-// ServerEnvironment.tryInstallDependencies(progressMonitor);\r
-// ServerEnvironment.ensureServerDependenciesMet();\r
-// }\r
-// } catch (InstallException ie) {\r
-// throw new PlatformException(ie);\r
-// } catch (ExecutionEnvironmentException eee) {\r
-// throw new PlatformException(eee);\r
-// } catch (IOException ie) {\r
-// throw new PlatformException(ie);\r
-// }\r
-// }\r
-// }\r
-\r
- /**\r
- * Shutdown Simantics Platform.\r
- *\r
- * In SWB this is handled in SimanticsWorkbenchAdvisor#disconnectFromWorkspace.\r
- *\r
- * @param progressMonitor optional progress monitor\r
- * @throws PlatformException\r
- */\r
- public void shutdown(IProgressMonitor progressMonitor) throws PlatformException\r
- {\r
- SubMonitor progress = SubMonitor.convert(progressMonitor, 100);\r
- PlatformException platformException = null;\r
- try {\r
- progress.subTask("Close Project");\r
- if (project != null) {\r
- project.safeDispose();\r
- }\r
- progress.worked(10);\r
-\r
- running = false;\r
- progress.subTask("Close Database Session");\r
- Databoard databoard = null;\r
- if (sessionContext != null) {\r
- Session s = sessionContext.peekSession();\r
- if (s != null) {\r
- databoard = s.peekService(Databoard.class);\r
-\r
- progress.subTask("Flushing Index Caches");\r
- try {\r
- Simantics.flushIndexCaches(progress.newChild(20), s);\r
- } catch (Throwable t) {\r
- Logger.defaultLogError(t);\r
- }\r
- }\r
-\r
- progress.subTask("Close Database Session");\r
- sessionContext.safeDispose();\r
- sessionContext = null;\r
- Simantics.setSessionContext(null);\r
- }\r
- if (simanticsBindings != null) {\r
- Bindings.classBindingFactory.removeFactory( simanticsBindings );\r
- simanticsBindings = null;\r
- }\r
- if (databoard != null) {\r
- if (simanticsBindings2 != null) {\r
- databoard.classBindingFactory.removeFactory( simanticsBindings2 );\r
- simanticsBindings2 = null;\r
- }\r
- databoard.clear();\r
- }\r
-\r
- // Make sure Simantics clipboard doesn't store unwanted session data references.\r
- Simantics.setClipboard(new SimanticsClipboardImpl());\r
-\r
- progress.worked(30);\r
-\r
- session = null;\r
- projectResource = null;\r
-\r
- DependenciesRelation.assertFinishedTracking();\r
-\r
- } catch (Exception e) {\r
- platformException = new PlatformException("Failed to shutdown Simantics Platform", e);\r
- }\r
-\r
- progress.worked(10);\r
- progress.subTask("Shutting down database");\r
- try {\r
- if (null != databasebManagement)\r
- databasebManagement.shutdown();\r
- } catch (Throwable t) {\r
- Logger.defaultLogError(t);\r
- }\r
- progress.worked(10);\r
-\r
- progress.subTask("Clearing Workspace Temporary Directory");\r
- try {\r
- Simantics.clearTemporaryDirectory();\r
- } catch (Throwable t) {\r
- Logger.defaultLogError(t);\r
- }\r
- progress.worked(10);\r
- if (null != platformException)\r
- throw platformException;\r
- }\r
-\r
- // TODO: consider removing this in the future ??\r
- @Override\r
- public void stateChanged(LifecycleState newState) {\r
- if(newState == LifecycleState.CLOSED) {\r
- if(running) {\r
- if(Platform.isRunning()) {\r
- mainThread.interrupt();\r
- }\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * @return <code>true</code> if discard was successful, <code>false</code>\r
- * if there was no session, {@link UndoRedoSupport} or\r
- * {@link UndoContext} to discard through\r
- */\r
- public boolean discardSessionUndoHistory() {\r
- Session s = session;\r
- if (s != null) {\r
- UndoRedoSupport urs = s.peekService(UndoRedoSupport.class);\r
- if (urs != null) {\r
- UndoContext uc = urs.getUndoContext(s);\r
- if (uc != null) {\r
- uc.clear();\r
- return true;\r
- }\r
- }\r
- }\r
- return false;\r
- }\r
-\r
- public void reconnect(String databaseDriverId) throws Exception {\r
- // Starts database server.\r
- SimanticsPlatform.INSTANCE.startUp(databaseDriverId, null, RecoveryPolicy.ThrowError, OntologyRecoveryPolicy.ThrowError, true, null);\r
- }\r
-\r
- private void dumpPlatformBundleState() {\r
- BundleDescription[] bs = Platform.getPlatformAdmin().getState().getBundles();\r
- System.out.println("Total bundles: " + bs.length);\r
- for (BundleDescription b : bs) {\r
- System.out.format("%-80s @ %s\n", b.toString(), b.getLocation());\r
- }\r
- }\r
-\r
-}\r
-\r
+/*******************************************************************************
+ * Copyright (c) 2007, 2018 Association for Decentralized Information Management
+ * in Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+package org.simantics;
+
+import static org.simantics.db.common.utils.Transaction.commit;
+import static org.simantics.db.common.utils.Transaction.endTransaction;
+import static org.simantics.db.common.utils.Transaction.readGraph;
+import static org.simantics.db.common.utils.Transaction.startTransaction;
+import static org.simantics.db.common.utils.Transaction.writeGraph;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.UUID;
+
+import org.eclipse.core.runtime.ILog;
+import org.eclipse.core.runtime.IProduct;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+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;
+import org.simantics.datatypes.literal.RGB;
+import org.simantics.db.Driver;
+import org.simantics.db.Driver.Management;
+import org.simantics.db.Manager;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.Session;
+import org.simantics.db.SessionModel;
+import org.simantics.db.UndoContext;
+import org.simantics.db.VirtualGraph;
+import org.simantics.db.WriteGraph;
+import org.simantics.db.common.processor.MergingDelayedWriteProcessor;
+import org.simantics.db.common.processor.MergingGraphRequestProcessor;
+import org.simantics.db.common.request.ObjectsWithType;
+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.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;
+import org.simantics.db.service.UndoRedoSupport;
+import org.simantics.db.service.VirtualGraphSupport;
+import org.simantics.db.service.XSupport;
+import org.simantics.db.services.GlobalServiceInitializer;
+import org.simantics.graph.db.GraphDependencyAnalyzer;
+import org.simantics.graph.db.GraphDependencyAnalyzer.IU;
+import org.simantics.graph.db.GraphDependencyAnalyzer.IdentityNode;
+import org.simantics.graph.db.IImportAdvisor;
+import org.simantics.graph.db.ImportResult;
+import org.simantics.graph.db.TransferableGraphs;
+import org.simantics.graph.diff.Diff;
+import org.simantics.graph.diff.TransferableGraphDelta1;
+import org.simantics.internal.Activator;
+import org.simantics.internal.TimedSessionCache;
+import org.simantics.internal.startup.StartupExtensions;
+import org.simantics.layer0.Layer0;
+import org.simantics.operation.Layer0X;
+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;
+import org.simantics.project.management.GraphBundle;
+import org.simantics.project.management.GraphBundleEx;
+import org.simantics.project.management.GraphBundleRef;
+import org.simantics.project.management.PlatformUtil;
+import org.simantics.project.management.ServerManager;
+import org.simantics.project.management.ServerManagerFactory;
+import org.simantics.project.management.WorkspaceUtil;
+import org.simantics.scl.compiler.module.repository.ModuleRepository;
+import org.simantics.scl.osgi.SCLOsgi;
+import org.simantics.utils.FileUtils;
+import org.simantics.utils.datastructures.Pair;
+import org.simantics.utils.strings.EString;
+import org.simantics.utils.threads.ExecutorWorker;
+import org.simantics.utils.threads.ThreadUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * SimanticsPlatform performs procedures required in order to get simantics
+ * workbench into operational state. This consists of the following steps:
+ * <ul>
+ * <li> Asserting there is Database
+ * </li>
+ * <li> Starting Database process
+ * </li>
+ * <li> Opening a session to Database process
+ * </li>
+ * <li> Asserting required ontologies or other transferable graphs are installed in the database
+ * </li>
+ * <li> Asserting required project is installed in the database
+ * </li>
+ * <li> Asserting Simantics Features are installed in the database
+ * </li>
+ * <li> Asserting Simantics Features are installed to the project
+ * </li>
+ * <li> Shutdown: Save Session, Close session, Kill Database process
+ * </li>
+ * </ul>
+ *
+ * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
+ */
+public class SimanticsPlatform implements LifecycleListener {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(SimanticsPlatform.class);
+
+ /**
+ * The policy is relevant when developing Simantics from Eclipse IDE.
+ * It is applied when the ontology in the database of a workspace doesn't match
+ * a newer ontology in the Eclipse workspace.
+ */
+ public static enum OntologyRecoveryPolicy { ThrowError, Merge, ReinstallDatabase, Bypass}
+
+ /**
+ * This policy dictates how the Simantics platform startup should react if
+ * the started workspace is not set up properly. The alternatives are to
+ * just throw an error and fail or to attempt all possible measures to fix
+ * the encountered problems.
+ */
+ public static enum RecoveryPolicy { ThrowError, FixError }
+
+ /** Singleton instance, started in SimanticsWorkbenchAdvisor */
+ public static final SimanticsPlatform INSTANCE = new SimanticsPlatform();
+
+ /** Set to true when the Simantics Platform is in good-and-go condition */
+ public boolean running;
+
+ /** ID of the database driver that the platform is currently using */
+ private String currentDatabaseDriver;
+
+ /** Database Session */
+ public Session session;
+ private Management databasebManagement;
+
+ /** Database session context */
+ public SessionContext sessionContext;
+
+ /** Project identifier in Database */
+ public String projectURI;
+
+ /** Project name */
+ public String projectName;
+
+ /** Project resource */
+ public Resource projectResource;
+
+ /** Session specific bindings */
+ public SimanticsBindings simanticsBindings;
+
+ 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)}
+ */
+ private IProject project;
+
+ protected ILog log;
+
+ /**
+ * Create a new simantics plaform manager in uninitialized state and
+ * with default policies. <p>
+ */
+ public SimanticsPlatform() {
+ log = Platform.getLog(Activator.getBundleContext().getBundle());
+ mainThread = Thread.currentThread();
+ }
+
+ public String getApplicationClientId() {
+ IProduct product = Platform.getProduct();
+ if(product == null) return "noProduct";//UUID.randomUUID().toString();
+ String application = product.getApplication();
+ return application != null ? application : UUID.randomUUID().toString();
+ }
+
+ 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();
+ Path dbLocation = workspaceLocation.resolve("db");
+ Path dbIniPath = workspaceLocation.resolve("db.ini");
+ // The driver file overrides any command line arguments to prevent
+ // using the wrong driver for an existing database directory.
+ ServerManager serverManager;
+ try {
+ Ini dbIni = loadOrCreateDatabaseIni(dbIniPath, databaseDriverId);
+ databaseDriverId = dbIni.get("driver", "id");
+ serverManager = ServerManagerFactory.create(databaseDriverId, dbLocation.toAbsolutePath().toString());
+ } catch (DatabaseException | IOException e) {
+ throw new PlatformException("Failed to initialize database ServerManager with driver " + databaseDriverId, e);
+ }
+ progressMonitor.beginTask("Setting up Simantics Database", 100);
+ progressMonitor.setTaskName("Asserting Database is installed.");
+ String msg = "Failed to initialize Simantics database.";
+ try {
+ // Create database
+ log.log(new Status(IStatus.INFO, Activator.PLUGIN_ID, "Initializing database at " + dbLocation + " with driver " + databaseDriverId));
+ progressMonitor.setTaskName("Creating database at " + dbLocation);
+ databasebManagement = serverManager.getManagement(dbLocation.toFile());
+ databasebManagement.create();
+ currentDatabaseDriver = databaseDriverId;
+ // Create layer0.
+ return serverManager.createDatabase(dbLocation.toFile());
+ } catch (DatabaseException e) {
+ throw new PlatformException(msg, e);
+ } catch (Throwable e) {
+ throw new PlatformException(msg, e);
+ } finally {
+ progressMonitor.worked(20);
+ }
+ }
+
+ public void synchronizeOntologies(IProgressMonitor progressMonitor, OntologyRecoveryPolicy ontologyPolicy, boolean requireSynchronize) throws PlatformException {
+
+ SubMonitor monitor = SubMonitor.convert(progressMonitor, 100);
+
+ monitor.setTaskName("Compile dynamic ontologies");
+ PlatformUtil.compileAllDynamicOntologies();
+
+ 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
+ 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));
+ }
+
+ if(!requireSynchronize && installedTGs.size() > 1 && !Platform.inDevelopmentMode()) return;
+// if(installedTGs.size() > 1) return;
+
+ // Get a list of all bundles in the platform (Bundle Context)
+ 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();
+ platformTGs.put(GraphBundleRef.of(b), gbe);
+ }
+
+ // Compile a list of TGs that need to be installed or reinstalled in the database
+ 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<>();
+ for (Entry<GraphBundleRef, GraphBundleEx> e : platformTGs.entrySet()) {
+ GraphBundleRef key = e.getKey();
+ GraphBundleEx platformBundle = e.getValue();
+ GraphBundleEx existingBundle = installedTGs.get(key);
+
+// System.out.println("GraphBundleRef key=" + key.toString());
+
+ if (existingBundle == null) {
+ // Bundle did not exist in the database, put it into list of bundles to install
+ installTGs.add(platformBundle);
+ }
+ else {
+ // Bundle exists in the database
+ boolean platformBundleIsNewer = existingBundle.getVersion().compareTo(platformBundle.getVersion())<0;
+ if (!platformBundleIsNewer)
+ continue;
+ // Check hash of transferable graph to know whether to update or not.
+ if (platformBundle.getHashcode() == existingBundle.getHashcode())
+ continue;
+ //System.out.println("Ontology hashcodes do not match: platform bundle="
+ // + platformBundle.getVersionedId() + ", hash=" + platformBundle.getHashcode()
+ // + "; existing bundle=" + existingBundle.getVersionedId() + ", hash=" + existingBundle.getHashcode());
+ reinstallTGs.put(platformBundle, existingBundle);
+ }
+ }
+ // INSTALL
+ // Database is missing graphs
+ if (!installTGs.isEmpty() || !reinstallTGs.isEmpty()) {
+ session.getService(XSupport.class).setServiceMode(true, true);
+
+ // Throw error
+ if (ontologyPolicy == OntologyRecoveryPolicy.ThrowError || ontologyPolicy == OntologyRecoveryPolicy.Bypass) {
+ StringBuilder sb = new StringBuilder("The following graphs are not installed in the database: ");
+ if (!installTGs.isEmpty()) {
+ int i = 0;
+ for (GraphBundleEx e : installTGs) {
+ if (i>0) sb.append(", ");
+ i++;
+ sb.append(e.toString());
+ }
+ sb.append(" is missing from the database.\n");
+ }
+ if (!reinstallTGs.isEmpty()) {
+ int i = 0;
+ for (Entry<GraphBundleEx, GraphBundleEx> e : reinstallTGs.entrySet()) {
+ if (i>0) sb.append(", ");
+ i++;
+ sb.append(e.getKey().toString());
+ }
+ sb.append(" Database/Platform Bundle version mismatch.\n");
+ }
+ sb.append("Hint: Use -fixErrors to install the graphs.");
+ if (ontologyPolicy == OntologyRecoveryPolicy.ThrowError)
+ throw new PlatformException(sb.toString());
+ else
+ log.log(new Status(IStatus.WARNING, Activator.PLUGIN_ID, sb.toString()));
+ }
+
+ // Reinstall database
+ if (ontologyPolicy == OntologyRecoveryPolicy.ReinstallDatabase) {
+ log.log(new Status(IStatus.INFO, Activator.PLUGIN_ID, "Reinstalling the database."));
+ // TODO Install DB
+ // Stop Session
+ // Kill Process
+ // Delete Database
+ // Create Database
+ // Start Database
+ // Open Session
+ // Install TGs
+ throw new PlatformException("Reinstalling Database, NOT IMPLEMENTED");
+ }
+
+ if (ontologyPolicy == OntologyRecoveryPolicy.Merge) {
+ 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());
+ for(GraphBundle tg : reinstallTGs.keySet()) analyzer.addGraph(tg, tg.getGraph());
+ if(!analyzer.analyzeDependency()) {
+ Collection<Pair<GraphBundle, GraphBundle>> problems = analyzer.getConflicts();
+ StringBuilder sb = new StringBuilder();
+ for (Pair<GraphBundle, GraphBundle> problem : problems) {
+ sb.append("Conflict with "+problem.first+" and "+problem.second+".\n");
+ }
+ throw new PlatformException(sb.toString());
+ } else if(!session.syncRequest( analyzer.queryExternalDependenciesSatisfied )) {
+ Collection<IdentityNode> unsatisfiedDependencies = analyzer.getUnsatisfiedDependencies();
+ StringBuilder sb = new StringBuilder();
+ for (IdentityNode dep: unsatisfiedDependencies) {
+ sb.append("Unsatisfied Dependency "+dep+". Required by\n");
+ for(IU iu : GraphDependencyAnalyzer.toCollection(dep.getRequires())) {
+ sb.append(" " + ((GraphBundle)iu.getId()).getId() + "\n");
+ }
+ }
+ throw new PlatformException(sb.toString());
+ }
+
+ message = "Analyzed graph bundles";
+ monitor.subTask(message);
+ LOGGER.info(message);
+
+ List<GraphBundle> sortedBundles = analyzer.getSortedGraphs();
+ if(!sortedBundles.isEmpty()) {
+
+ 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;
+
+ // Install TGs
+ for(final GraphBundle tg : sortedBundles) {
+
+ final IImportAdvisor advisor = new OntologyImportAdvisor(tg, mgmt);
+ final GraphBundle oldTG = reinstallTGs.get(tg);
+
+ boolean createImmutable = tg.getImmutable();
+
+ if (oldTG==null) {
+
+ session.getService(XSupport.class).setServiceMode(true, createImmutable);
+
+ // Install TG
+ log.log(new Status(IStatus.INFO, Activator.PLUGIN_ID, "Installing "+tg.toString()+" - "+tg.getName()));
+ ImportResult result = TransferableGraphs.importGraph1(session, new TGTransferableGraphSource(tg.getGraph()), advisor, null);
+ if (!result.missingExternals.isEmpty()) {
+ log.log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Import of " + tg.toString() + " was missing the following external entities:\n" + EString.implode(result.missingExternals)));
+ }
+ } else {
+ if(!createImmutable)
+ continue;
+
+ // Merge TG
+ startTransaction(session, false);
+ TransferableGraphDelta1 delta = new Diff(oldTG.getGraph(), tg.getGraph()).diff();
+ final long[] oldResources = oldTG.getResourceArray();
+ boolean changes = TransferableGraphs.hasChanges(readGraph(), oldResources, delta);
+ endTransaction();
+ if (!changes) {
+ //log.log(new Status(IStatus.INFO, Activator.PLUGIN_ID, "Nothing to merge for "+tg.toString()));
+ continue;
+ }
+
+ 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);
+ commit();
+ mergedOntologies = true;
+ } catch (Throwable t) {
+ throw new PlatformException(t);
+ } finally {
+ endTransaction();
+ }
+ }
+ }
+
+ session.syncRequest((Write) graph -> {
+ graph.setClusterSet4NewResource(graph.getRootLibrary());
+ graph.flushCluster();
+ });
+
+ if (mergedOntologies)
+ DatabaseIndexing.deleteAllIndexes();
+ }
+ }
+ session.getService(XSupport.class).setServiceMode(false, false);
+ }
+ message = "Ontologies synchronized";
+ monitor.subTask(message);
+ LOGGER.info(message);
+ monitor.worked(100);
+ } catch (IOException e) {
+ throw new PlatformException(e);
+ } catch (DatabaseException e) {
+ throw new PlatformException(e);
+ }
+
+ }
+
+ public boolean assertConfiguration(IProgressMonitor progressMonitor, RecoveryPolicy workspacePolicy) throws PlatformException {
+
+ if (progressMonitor == null) progressMonitor = new NullProgressMonitor();
+
+ File workspaceLocation = Platform.getLocation().toFile();
+
+ boolean installProject = false;
+ progressMonitor.setTaskName("Asserting simantics.cfg is installed");
+ try {
+ File propertyFile = new File(workspaceLocation, "simantics.cfg");
+ Properties properties;
+ try {
+ properties = WorkspaceUtil.readProperties(propertyFile);
+ } catch (IOException e) {
+ if (workspacePolicy == RecoveryPolicy.ThrowError) throw new PlatformException("Could not load "+propertyFile);
+
+ // Create a project and write Property file
+ properties = new Properties();
+ properties.setProperty("project_uri", "http://Projects/Development%20Project");
+ properties.setProperty("project_name", "Development Project");
+ WorkspaceUtil.writeProperties(propertyFile, properties);
+ installProject |= true;
+ }
+ projectURI = properties.getProperty("project_uri");
+ projectName = properties.getProperty("project_name");
+ progressMonitor.worked(10);
+ } catch (IOException e) {
+ throw new PlatformException(e);
+ }
+
+ return installProject;
+
+ }
+
+ public boolean assertProject(IProgressMonitor progressMonitor, RecoveryPolicy workspacePolicy, boolean installProject) throws PlatformException {
+
+ SubMonitor monitor = SubMonitor.convert(progressMonitor, 10);
+
+ final DatabaseManagement mgmt = new DatabaseManagement();
+
+ monitor.setTaskName("Asserting project resource exists in the database");
+ try {
+ 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 {
+ 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;
+
+ 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);
+ }
+ }
+ } catch (DatabaseException e) {
+ throw new PlatformException("Failed to create "+projectURI, e);
+ }
+ monitor.worked(10);
+
+ return installProject;
+
+ }
+
+ public void updateInstalledGroups(IProgressMonitor progressMonitor, boolean installProject) throws PlatformException {
+
+ if (installProject)
+ {
+ // Attach all feature groups available in platform to created project
+ progressMonitor.setTaskName("Install all features");
+ Set<GroupReference> publishedFeatureGroups = ProjectFeatures.getInstallGroupsOfPublishedFeatures();
+ Collection<GroupReference> groupsWithoutVersion = GroupReference.stripVersions(publishedFeatureGroups);
+
+ try {
+ session.syncRequest(
+ (Write) graph ->
+ Projects.setProjectInstalledGroups(graph, projectResource, groupsWithoutVersion));
+ } catch (DatabaseException ae) {
+ throw new PlatformException("Failed to install features", ae);
+ }
+ progressMonitor.worked(10);
+ }
+
+ }
+
+ public void assertSessionModel(IProgressMonitor progressMonitor) throws PlatformException {
+
+ Properties properties = session.getService(Properties.class);
+ final String clientId = properties.getProperty("clientId");
+
+ try {
+
+ // Currently this needs to be done before data becomes available
+ VirtualGraphSupport support = session.getService(VirtualGraphSupport.class);
+ VirtualGraph activations = support.getWorkspacePersistent("activations");
+
+ Resource sessionModel = session.syncRequest(new Read<Resource>() {
+
+ @Override
+ public Resource perform(ReadGraph graph) throws DatabaseException {
+
+ Layer0X L0X = Layer0X.getInstance(graph);
+ for(Resource sessionModel : graph.syncRequest(new ObjectsWithType(graph.getRootLibrary(), L0X.HasSession, L0X.Session))) {
+ String id = graph.getPossibleRelatedValue(sessionModel, L0X.Session_HasClientId);
+ if(id != null && id.equals(clientId)) return sessionModel;
+ }
+ return null;
+
+ }
+
+ });
+
+ if(sessionModel == null) {
+
+ sessionModel = session.syncRequest(new WriteResultRequest<Resource>(activations) {
+
+ @Override
+ public Resource perform(WriteGraph graph) throws DatabaseException {
+ Layer0 L0 = Layer0.getInstance(graph);
+ Layer0X L0X = Layer0X.getInstance(graph);
+ Resource session = graph.newResource();
+ graph.claim(session, L0.InstanceOf, null, L0X.Session);
+ graph.claim(session, L0X.Session_HasUser, null, graph.getResource("http://Users/AdminUser"));
+ graph.addLiteral(session, L0X.Session_HasClientId, L0X.Session_HasClientId_Inverse, clientId, Bindings.STRING);
+ graph.claim(graph.getRootLibrary(), L0X.HasSession, session);
+ return session;
+ }
+ });
+
+ }
+
+ session.registerService(SessionModel.class, new PlatformSessionModel(sessionModel));
+ } catch (DatabaseException e) {
+ throw new PlatformException(e);
+ }
+
+ }
+
+ static class PlatformSessionModel implements SessionModel {
+ private final Resource sessionModel;
+
+ public PlatformSessionModel(Resource model) {
+ this.sessionModel = model;
+ }
+
+ @Override
+ public Resource getResource() {
+ return sessionModel;
+ }
+ }
+
+ public void resetDatabase(IProgressMonitor monitor) throws PlatformException {
+ // TODO: fix this to use Path APIs
+ File dbLocation = Platform.getLocation().append("db").toFile();
+ if(!dbLocation.exists()) return;
+ try { // Load driver
+ Driver driver = Manager.getDriver("acorn");
+ Management management = driver.getManagement(dbLocation.getAbsolutePath(), null);
+ management.delete();
+ } catch (DatabaseException e) {
+ throw new PlatformException("Failed to remove database at " + dbLocation.getAbsolutePath(), e);
+ }
+ // We have created extra files to database folder which have to be deleted also.
+ // This is an awful idea! Do not create extra files to database folder!
+ Throwable t = null;
+ for (int i=0; i<10; ++i) {
+ try {
+ FileUtils.deleteAll(dbLocation);
+ t = null;
+ break;
+ } catch (IOException e) {
+ // Assuming this has been thrown because delete file/folder failed.
+ t = e;
+ }
+ try {
+ Thread.sleep(200);
+ } catch (InterruptedException e) {
+ // Ignoring interrupted exception.
+ }
+ }
+ if (null != t)
+ throw new PlatformException("Failed to remove database folder at " + dbLocation.getAbsolutePath(), t);
+ }
+ public void resetWorkspace(IProgressMonitor monitor, ArrayList<String> fileFilter) throws PlatformException, IllegalStateException, IOException {
+ File file = Platform.getLocation().toFile();
+ if (null != fileFilter)
+ FileUtils.deleteAllWithFilter(file , fileFilter);
+ resetDatabase(monitor);
+ }
+
+ 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 null;
+
+ Path baseline = Paths.get(dbBaselineArchive);
+ 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;
+ }
+
+ // 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 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;
+ }
+
+ Path baseline = resolveBaselineFile();
+ if (baseline == null)
+ return false;
+
+ DatabaseBaselines.validateBaselineFile(baseline);
+ DatabaseBaselines.validateWorkspaceForBaselineInitialization(workspaceLocation);
+ DatabaseBaselines.initializeWorkspaceWithBaseline(baseline, workspaceLocation, baselineIndicatorFile);
+ return true;
+ }
+
+ /**
+ * Start-up the platform. The procedure consists of 8 steps. Once everything
+ * is up and running, all fields are set property.
+ * <p>
+ *
+ * If workspacePolicy is FixErrors, there is an attempt to fix unexpected
+ * errors. It includes installing database files, installing ontologies, and
+ * installing project features.
+ * <p>
+ *
+ * In Simantics Workbench this is handled in
+ * <code>SimanticsWorkbenchAdvisor#openWindows()</code>.
+ * <p>
+ *
+ * If remote server is given, simantics plaform takes connection there
+ * instead of local server at "db/".
+ *
+ * @param workspacePolicy action to take on workspace/database related
+ * errors
+ * @param ontologyPolicy action to take on ontology mismatch
+ * @param progressMonitor optional progress monitor
+ * @param userAgent interface for resorting to user feedback during platform
+ * startup or <code>null</code> to resort to default measures
+ * @throws PlatformException
+ */
+ public synchronized SessionContext startUp(String databaseDriverId, IProgressMonitor progressMonitor, RecoveryPolicy workspacePolicy,
+ OntologyRecoveryPolicy ontologyPolicy, boolean requireSynchronize, PlatformUserAgent userAgent)
+ throws PlatformException
+ {
+
+ assert(!running);
+ LOGGER.info("Beginning of SimanticsPlatform.startUp");
+
+ SubMonitor monitor = SubMonitor.convert(progressMonitor, 1000);
+
+ // For debugging on what kind of platform automatic tests are running in
+ // case there are problems.
+ if ("true".equals(System.getProperty("org.simantics.dumpBundleState")))
+ dumpPlatformBundleState();
+
+ // 0. Consult all startup extensions before doing anything with the workspace.
+ StartupExtensions.consultStartupExtensions();
+ LOGGER.info("Consulted platform pre-startup extensions");
+
+ // 0.1. Clear all temporary files
+ Simantics.clearTemporaryDirectory();
+ LOGGER.info("Cleared temporary directory");
+
+ // 0.2 Clear VariableRepository.repository static map which holds references to SessionImplDb
+ VariableRepository.clear();
+
+ // 0.2.1 Activate org.simantics.scl.osgi to prime the SCL compiler early.
+ @SuppressWarnings("unused")
+ ModuleRepository modRepo = SCLOsgi.MODULE_REPOSITORY;
+
+ // 0.3 Handle baseline database before opening db
+ @SuppressWarnings("unused")
+ boolean usingBaseline = handleBaselineDatabase();
+
+ // 1. Assert there is a database at <workspace>/db
+ SessionDescriptor sessionDescriptor = setupDatabase(databaseDriverId, monitor.newChild(200, SubMonitor.SUPPRESS_NONE), workspacePolicy, userAgent);
+ session = sessionDescriptor.getSession();
+ LOGGER.info("Database setup complete");
+
+ // 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 {
+ DatabaseIndexing.deleteAllIndexes();
+ } catch (IOException e) {
+ throw new PlatformException(e);
+ }
+ }
+
+ // 3. Assert all graphs, and correct versions, are installed to the database
+ synchronizeOntologies(monitor.newChild(400, SubMonitor.SUPPRESS_NONE), ontologyPolicy, requireSynchronize);
+
+ // 4. Assert simantics.cfg exists
+ boolean installProject = assertConfiguration(monitor.newChild(25, SubMonitor.SUPPRESS_NONE),workspacePolicy);
+
+ // 5. Assert Project Resource is installed in the database
+ installProject = assertProject(monitor.newChild(25, SubMonitor.SUPPRESS_NONE), workspacePolicy, installProject);
+
+ // 6. Install all features into project, if in debug mode
+ updateInstalledGroups(monitor.newChild(25), true); //installProject);
+ LOGGER.info("Installed all features into project");
+
+ // 7. Assert L0.Session in database for this session
+ assertSessionModel(monitor.newChild(25, SubMonitor.SUPPRESS_NONE));
+
+ session.getService(XSupport.class).setServiceMode(false, false);
+
+ try {
+ String message = "Flush query cache";
+ monitor.setTaskName(message);
+ LOGGER.info(message);
+ session.syncRequest((Write) graph -> {
+ QueryControl qc = graph.getService(QueryControl.class);
+ qc.flush(graph);
+ });
+ } catch (DatabaseException e) {
+ LOGGER.error("Flushing queries failed.", e);
+ }
+ boolean loadProject = true;
+ try {
+ String message = "Open database session";
+ monitor.setTaskName(message);
+ LOGGER.info(message);
+ sessionContext = SimanticsPlatform.INSTANCE.createSessionContext(true);
+ // This must be before setSessionContext since some listeners might query this
+ sessionContext.setHint(SimanticsKeys.KEY_PROJECT, SimanticsPlatform.INSTANCE.projectResource);
+
+ Simantics.setSessionContext(sessionContext);
+
+ // 1. Put ResourceBinding that throws an exception to General Bindings
+ message = "Put ResourceBinding that throws an exception to General Bindings";
+ LOGGER.info(message);
+ simanticsBindings = new SimanticsBindings();
+ Bindings.classBindingFactory.addFactory( simanticsBindings );
+
+ Session session = sessionContext.getSession();
+ session.registerService(Databoard.class, Bindings.databoard);
+
+ // Register datatype bindings
+ message = "Register datatype bindings";
+ LOGGER.info(message);
+ Bindings.defaultBindingFactory.getRepository().put(RGB.Integer.BINDING.type(), RGB.Integer.BINDING);
+ Bindings.defaultBindingFactory.getRepository().put(Font.BINDING.type(), Font.BINDING);
+
+ if (support.rolledback() || sessionDescriptor.isFreshDatabase()) {
+ message = "Rebuilding all indexes";
+ LOGGER.info(message);
+ monitor.setTaskName(message);
+ 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);
+ }
+
+ if(loadProject) {
+ message = "Load project";
+ monitor.setTaskName(message);
+ LOGGER.info(message);
+ project = Projects.loadProject(sessionContext.getSession(), SimanticsPlatform.INSTANCE.projectResource);
+ sessionContext.setHint(ProjectKeys.KEY_PROJECT, project);
+ monitor.worked(100);
+ message = "Loading projects complete";
+ LOGGER.info(message);
+
+ message = "Activate project";
+ monitor.setTaskName(message);
+ LOGGER.info(message);
+ project.activate();
+ monitor.worked(100);
+ LOGGER.info("Project activated");
+ }
+
+ } catch (DatabaseException e) {
+ LOGGER.error("Platform startup failed.", e);
+ throw new PlatformException(e);
+ } catch (ProjectException e) {
+ boolean hasStackTrace = e.getStackTrace().length > 0;
+ if (!hasStackTrace)
+ throw new PlatformException(e.getMessage(), hasStackTrace);
+ throw new PlatformException(e, hasStackTrace);
+ }
+
+ 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();
+ LOGGER.info("Discarded session undo history");
+
+ return sessionContext;
+
+ }
+
+ public void registerServices(SessionContext context) {
+ new GlobalServiceInitializer().initialize(session);
+ session.registerService(MergingGraphRequestProcessor.class, new MergingGraphRequestProcessor("SessionService", session, 20));
+ session.registerService(MergingDelayedWriteProcessor.class, new MergingDelayedWriteProcessor(session, 20));
+ }
+
+
+ public SessionContext createSessionContext(boolean init) throws PlatformException {
+ try {
+ // Construct and initialize SessionContext from Session.
+ SessionContext sessionContext = SessionContext.create(session, init);
+ String message = "Session context created";
+ LOGGER.info(message);
+ if (init) {
+ registerServices(sessionContext);
+ message = "Session services registered";
+ LOGGER.info(message);
+ }
+ return sessionContext;
+ } catch (DatabaseException e) {
+ throw new PlatformException(e);
+ }
+ }
+
+ /**
+ * 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 Simantics Workbench this is handled in
+ * <code>SimanticsWorkbenchAdvisor#disconnectFromWorkspace</code>.
+ *
+ * @param progressMonitor
+ * optional progress monitor
+ * @param clearTemporaryFiles
+ * allow or prevent deletion of temporary files at the end of the
+ * shutdown procedure
+ * @throws PlatformException
+ */
+ public synchronized void shutdown(IProgressMonitor progressMonitor, boolean clearTemporaryFiles) throws PlatformException
+ {
+ SubMonitor progress = SubMonitor.convert(progressMonitor, 100);
+ PlatformException platformException = null;
+ try {
+ progress.subTask("Close Project");
+ if (project != null) {
+ project.safeDispose();
+ }
+ progress.worked(10);
+
+ // NOP at the moment
+ TimedSessionCache.close();
+
+ progress.subTask("Thread pools");
+ ThreadUtils.shutdown();
+ ExecutorWorker.shutdown();
+ progress.worked(5);
+
+ running = false;
+ progress.subTask("Close Database Session");
+ if (sessionContext != null) {
+ Session s = sessionContext.peekSession();
+ if (s != null) {
+ progress.subTask("Flushing Index Caches");
+ try {
+ Simantics.flushIndexCaches(progress.newChild(20), s);
+ } catch (Throwable t) {
+ LOGGER.error("Failed to flush index caches.", t);
+ }
+ }
+
+ progress.subTask("Close Database Session");
+ sessionContext.safeDispose();
+ sessionContext = null;
+ Simantics.setSessionContext(null);
+ }
+ if (simanticsBindings != null) {
+ Bindings.classBindingFactory.removeFactory( simanticsBindings );
+ simanticsBindings = null;
+ }
+
+ // Make sure Simantics clipboard doesn't store unwanted session data references.
+ Simantics.setClipboard(new SimanticsClipboardImpl());
+
+ progress.worked(50);
+
+ session = null;
+ projectResource = null;
+ currentDatabaseDriver = null;
+
+ DependenciesRelation.assertFinishedTracking();
+
+ } catch (Exception e) {
+ platformException = new PlatformException("Failed to shutdown Simantics Platform", e);
+ }
+
+ progress.worked(10);
+ progress.subTask("Shutting down database");
+ try {
+ if (null != databasebManagement)
+ databasebManagement.shutdown();
+ } catch (Throwable t) {
+ LOGGER.error("Database shutdown failed.", t);
+ }
+ progress.worked(10);
+
+ progress.subTask("Clear index status");
+ try {
+ // Everything ok, clear index dirty state.
+ DatabaseIndexing.clearAllDirty();
+ } catch (IOException e) {
+ LOGGER.error("Problems encountered while refreshing database index states, see exception for details.", e);
+ }
+ progress.worked(5);
+
+ 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 ??
+ @Override
+ public void stateChanged(LifecycleState newState) {
+ if(newState == LifecycleState.CLOSED) {
+ if(running) {
+ if(Platform.isRunning()) {
+ mainThread.interrupt();
+ }
+ }
+ }
+ }
+
+ /**
+ * @return <code>true</code> if discard was successful, <code>false</code>
+ * if there was no session, {@link UndoRedoSupport} or
+ * {@link UndoContext} to discard through
+ */
+ public boolean discardSessionUndoHistory() {
+ Session s = session;
+ if (s != null) {
+ UndoRedoSupport urs = s.peekService(UndoRedoSupport.class);
+ if (urs != null) {
+ UndoContext uc = urs.getUndoContext(s);
+ if (uc != null) {
+ uc.clear();
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public void reconnect(String databaseDriverId) throws Exception {
+ // Starts database server.
+ if (currentDatabaseDriver != null)
+ databaseDriverId = currentDatabaseDriver;
+ SimanticsPlatform.INSTANCE.startUp(databaseDriverId, null, RecoveryPolicy.ThrowError, OntologyRecoveryPolicy.ThrowError, true, null);
+ }
+
+ private void dumpPlatformBundleState() {
+ BundleDescription[] bs = Platform.getPlatformAdmin().getState().getBundles();
+ System.out.println("Total bundles: " + bs.length);
+ for (BundleDescription b : bs) {
+ System.out.format("%-80s @ %s\n", b.toString(), b.getLocation());
+ }
+ }
+
+ private Ini loadOrCreateDatabaseIni(Path path, String databaseDriverId)
+ throws InvalidFileFormatException, IOException
+ {
+ File f = path.toFile();
+ Ini dbIni = Files.isRegularFile(path) ? new Ini(f) : new Ini();
+ String iniId = dbIni != null ? dbIni.get("driver", "id") : null;
+ if (iniId == null) {
+ dbIni.put("driver", "id", databaseDriverId);
+ dbIni.store(f);
+ }
+ return dbIni;
+ }
+
+}