X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=blobdiff_plain;f=bundles%2Forg.simantics%2Fsrc%2Forg%2Fsimantics%2FSimanticsPlatform.java;h=11013eaf7e32ffd1f09fbc4477ed074c2dad21e7;hp=7e6bd9dd9be5c7c134baff73223061f535ce6e92;hb=5930811a7911090a0c4984380c3b45ed81a93cde;hpb=ffdf83729b496d5afe74c7888075bb17ce1c4bbb diff --git a/bundles/org.simantics/src/org/simantics/SimanticsPlatform.java b/bundles/org.simantics/src/org/simantics/SimanticsPlatform.java index 7e6bd9dd9..11013eaf7 100644 --- a/bundles/org.simantics/src/org/simantics/SimanticsPlatform.java +++ b/bundles/org.simantics/src/org/simantics/SimanticsPlatform.java @@ -1,1041 +1,1047 @@ -/******************************************************************************* - * Copyright (c) 2007, 2010 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.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.resolver.BundleDescription; -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.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.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.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.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.TransferableGraphs; -import org.simantics.graph.diff.Diff; -import org.simantics.graph.diff.TransferableGraphDelta1; -import org.simantics.internal.Activator; -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.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.utils.FileUtils; -import org.simantics.utils.datastructures.Pair; -import org.simantics.utils.logging.TimeLogger; - -/** - * SimanticsPlatform performs procedures required in order to get simantics - * workbench into operational state. This consists of the following steps: - * - * - * @author Toni Kalajainen - */ -public class SimanticsPlatform implements LifecycleListener { - - /** - * 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 } - - /** - * 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; - - /** 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 SimanticsBindings simanticsBindings2; - - public Thread mainThread; - - /** - * 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.

- */ - 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 Session setupDatabase(String databaseDriverId, IProgressMonitor progressMonitor, RecoveryPolicy workspacePolicy, PlatformUserAgent userAgent) throws PlatformException { - if (progressMonitor == null) - progressMonitor = new NullProgressMonitor(); - File dbLocation = Platform.getLocation().append("db").toFile(); - ServerManager serverManager; - try { - serverManager = ServerManagerFactory.create(databaseDriverId, dbLocation.getAbsolutePath()); - } catch (DatabaseException | IOException e) { - throw new PlatformException("Failed to initialize Server Manager", 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, "Creating database at " + dbLocation)); - progressMonitor.setTaskName("Creating database at " + dbLocation); - databasebManagement = serverManager.getManagement(dbLocation); - databasebManagement.create(); - // Create layer0. - return serverManager.createDatabase(dbLocation); - } 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 { - - if (progressMonitor == null) progressMonitor = new NullProgressMonitor(); - - final DatabaseManagement mgmt = new DatabaseManagement(); - - PlatformUtil.compileAllDynamicOntologies(); - - progressMonitor.setTaskName("Asserting all ontologies are installed"); - final Map platformTGs = new HashMap(); - try { - - // Get a list of bundles installed into the database - progressMonitor.subTask("find installed bundles from database"); - Map 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) - List tgs = new ArrayList(); - progressMonitor.subTask("load all transferable graphs from platform"); - PlatformUtil.getAllGraphs(tgs); - progressMonitor.subTask("extend bundles to compile versions"); - 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 - progressMonitor.subTask("check bundle reinstallation demand"); - List installTGs = new ArrayList(); - // Create list of TGs to update, - Map reinstallTGs = new TreeMap(); - for (Entry 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) { - 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 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."); - throw new PlatformException(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) { - progressMonitor.subTask("Merging ontology changes"); - // Sort missing TGs into install order - GraphDependencyAnalyzer analyzer = new GraphDependencyAnalyzer(); - for(GraphBundle tg : installTGs) analyzer.addGraph(tg, tg.getGraph()); - for(GraphBundle tg : reinstallTGs.keySet()) analyzer.addGraph(tg, tg.getGraph()); - if(!analyzer.analyzeDependency()) { - Collection> problems = analyzer.getConflicts(); - StringBuilder sb = new StringBuilder(); - for (Pair problem : problems) { - sb.append("Conflict with "+problem.first+" and "+problem.second+".\n"); - } - throw new PlatformException(sb.toString()); - } - else if(!session.syncRequest( analyzer.queryExternalDependenciesSatisfied )) { - Collection 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()); - } - - List 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(); - } - }); - - boolean mergedOntologies = false; - - // Install TGs - for(final GraphBundle tg : sortedBundles) { - - final IImportAdvisor advisor = new OntologyImportAdvisor(tg, mgmt); - final GraphBundle oldTG = reinstallTGs.get(tg); - - if (oldTG==null) { - - boolean createImmutable = tg.getImmutable(); - session.getService(XSupport.class).setServiceMode(true, createImmutable); - - // Install TG - log.log(new Status(IStatus.INFO, Activator.PLUGIN_ID, "Installing "+tg.toString()+" - "+tg.getName())); - TransferableGraphs.importGraph1(session, new TGTransferableGraphSource(tg.getGraph()), advisor, null); - } else { - // 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(new WriteRequest() { - @Override - public void perform(WriteGraph graph) throws DatabaseException { - graph.setClusterSet4NewResource(graph.getRootLibrary()); - graph.flushCluster(); - } - }); - - if (mergedOntologies) - DatabaseIndexing.deleteAllIndexes(); - } - - TimeLogger.log("Ontologies synchronized."); - - } - session.getService(XSupport.class).setServiceMode(false, false); - } - progressMonitor.worked(20); - } 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 { - - if (progressMonitor == null) progressMonitor = new NullProgressMonitor(); - - final DatabaseManagement mgmt = new DatabaseManagement(); - - progressMonitor.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); - try { - // The project needs to be created mutable. - session.getService(XSupport.class).setServiceMode(true, false); - - ArrayList empty = new ArrayList(); - 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); - } - progressMonitor.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 publishedFeatureGroups = ProjectFeatures.getInstallGroupsOfPublishedFeatures(); - Collection groupsWithoutVersion = GroupReference.stripVersions(publishedFeatureGroups); - - // final List Platform_Features = new ArrayList(); - // - // // Convert graph instances - // Collection platformGraphs = new ArrayList(); - // 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) { - 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() { - - @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(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 { - File dbLocation = Platform.getLocation().append("db").toFile(); - if(!dbLocation.exists()) return; - try { // Load driver - Driver driver = Manager.getDriver("procore"); - 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 fileFilter) throws PlatformException, IllegalStateException, IOException { - File file = Platform.getLocation().toFile(); - if (null != fileFilter) - FileUtils.deleteAllWithFilter(file , fileFilter); - resetDatabase(monitor); - } - - /** - * Start-up the platform. The procedure consists of 8 steps. Once everything - * is up and running, all fields are set property. - *

- * - * If workspacePolicy is FixErrors, there is an attempt to fix unexpected - * errors. It includes installing database files, installing ontologies, and - * installing project features. - *

- * - * In SWB this is handled in SimanticsWorkbenchAdvisor#openWindows(). - *

- * - * 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 null to resort to default measures - * @throws PlatformException - */ - public SessionContext startUp(String databaseDriverId, IProgressMonitor progressMonitor, RecoveryPolicy workspacePolicy, - OntologyRecoveryPolicy ontologyPolicy, boolean requireSynchronize, PlatformUserAgent userAgent) - throws PlatformException - { - - assert(!running); - TimeLogger.log("Beginning of SimanticsPlatform.startUp"); - - if (progressMonitor == null) progressMonitor = new NullProgressMonitor(); - - // 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(); - TimeLogger.log("Consulted platform pre-startup extensions"); - - // 0.1. Clear all temporary files - Simantics.clearTemporaryDirectory(); - TimeLogger.log("Cleared temporary directory"); - - // 0.2 Clear VariableRepository.repository static map which holds references to SessionImplDb - VariableRepository.clear(); - - // 1. Assert there is a database at /db - session = setupDatabase(databaseDriverId, progressMonitor, workspacePolicy, userAgent); - TimeLogger.log("Database setup complete"); - - // 1.1 - XSupport support = session.getService(XSupport.class); - if (support.rolledback()) { - try { - DatabaseIndexing.deleteAllIndexes(); - } catch (IOException e) { - throw new PlatformException(e); - } - } - - // 2. Assert all graphs, and correct versions, are installed to the database - synchronizeOntologies(progressMonitor, ontologyPolicy, requireSynchronize); - TimeLogger.log("Synchronized ontologies"); - - // 4. Assert simantics.cfg exists - boolean installProject = assertConfiguration(progressMonitor,workspacePolicy); - - // 5. Assert Project Resource is installed in the database - installProject = assertProject(progressMonitor, workspacePolicy, installProject); - - // 6. Install all features into project, if in debug mode - updateInstalledGroups(progressMonitor, installProject); - TimeLogger.log("Installed all features into project"); - - // 7. Assert L0.Session in database for this session - assertSessionModel(progressMonitor); - - 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); - } - - }); - TimeLogger.log("Flushed queries"); - } catch (DatabaseException e) { - Logger.defaultLogError(e); - } - boolean loadProject = true; - try { - - 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 - simanticsBindings = new SimanticsBindings( null ); - 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 ); - - // 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) { - - TimeLogger.log("Load projects"); - project = Projects.loadProject(sessionContext.getSession(), SimanticsPlatform.INSTANCE.projectResource); - - sessionContext.setHint(ProjectKeys.KEY_PROJECT, project); - TimeLogger.log("Loading projects complete"); - - project.activate(); - TimeLogger.log("Project activated"); - } - - } catch (DatabaseException e) { - Logger.defaultLogError(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; - - return sessionContext; - - } - - public SessionContext createSessionContext(boolean init) throws PlatformException { - try { - // Construct and initialize SessionContext from Session. - SessionContext sessionContext = SessionContext.create(session, init); - if (init) - sessionContext.registerServices(); - return sessionContext; - } catch (DatabaseException e) { - throw new PlatformException(e); - } - } - -// 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); -// } -// } -// } - - /** - * Shutdown Simantics Platform. - * - * In SWB this is handled in SimanticsWorkbenchAdvisor#disconnectFromWorkspace. - * - * @param progressMonitor optional progress monitor - * @throws PlatformException - */ - public void shutdown(IProgressMonitor progressMonitor) throws PlatformException - { - SubMonitor progress = SubMonitor.convert(progressMonitor, 100); - PlatformException platformException = null; - try { - progress.subTask("Close Project"); - if (project != null) { - project.safeDispose(); - } - progress.worked(10); - - 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); - } catch (Throwable t) { - Logger.defaultLogError(t); - } - } - - progress.subTask("Close Database Session"); - sessionContext.safeDispose(); - sessionContext = null; - Simantics.setSessionContext(null); - } - if (simanticsBindings != null) { - 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()); - - progress.worked(30); - - session = null; - projectResource = 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.defaultLogError(t); - } - progress.worked(10); - - progress.subTask("Clearing Workspace Temporary Directory"); - try { - Simantics.clearTemporaryDirectory(); - } catch (Throwable t) { - Logger.defaultLogError(t); - } - progress.worked(10); - if (null != platformException) - throw platformException; - } - - // 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 true if discard was successful, false - * 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. - 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()); - } - } - -} - +/******************************************************************************* + * Copyright (c) 2007, 2010 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.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.resolver.BundleDescription; +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.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.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.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.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.TransferableGraphs; +import org.simantics.graph.diff.Diff; +import org.simantics.graph.diff.TransferableGraphDelta1; +import org.simantics.internal.Activator; +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.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.utils.FileUtils; +import org.simantics.utils.datastructures.Pair; +import org.simantics.utils.logging.TimeLogger; +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: + *

+ * + * @author Toni Kalajainen + */ +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 } + + /** + * 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; + + /** 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 SimanticsBindings simanticsBindings2; + + public Thread mainThread; + + /** + * 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.

+ */ + 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 Session setupDatabase(String databaseDriverId, IProgressMonitor progressMonitor, RecoveryPolicy workspacePolicy, PlatformUserAgent userAgent) throws PlatformException { + if (progressMonitor == null) + progressMonitor = new NullProgressMonitor(); + File dbLocation = Platform.getLocation().append("db").toFile(); + ServerManager serverManager; + try { + serverManager = ServerManagerFactory.create(databaseDriverId, dbLocation.getAbsolutePath()); + } catch (DatabaseException | IOException e) { + throw new PlatformException("Failed to initialize Server Manager", 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, "Creating database at " + dbLocation)); + progressMonitor.setTaskName("Creating database at " + dbLocation); + databasebManagement = serverManager.getManagement(dbLocation); + databasebManagement.create(); + // Create layer0. + return serverManager.createDatabase(dbLocation); + } 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 { + + if (progressMonitor == null) progressMonitor = new NullProgressMonitor(); + + final DatabaseManagement mgmt = new DatabaseManagement(); + + PlatformUtil.compileAllDynamicOntologies(); + + progressMonitor.setTaskName("Asserting all ontologies are installed"); + final Map platformTGs = new HashMap(); + try { + + // Get a list of bundles installed into the database + progressMonitor.subTask("find installed bundles from database"); + Map 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) + List tgs = new ArrayList(); + progressMonitor.subTask("load all transferable graphs from platform"); + PlatformUtil.getAllGraphs(tgs); + progressMonitor.subTask("extend bundles to compile versions"); + 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 + progressMonitor.subTask("check bundle reinstallation demand"); + List installTGs = new ArrayList(); + // Create list of TGs to update, + Map reinstallTGs = new TreeMap(); + for (Entry 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) { + 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 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."); + throw new PlatformException(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) { + progressMonitor.subTask("Merging ontology changes"); + // Sort missing TGs into install order + GraphDependencyAnalyzer analyzer = new GraphDependencyAnalyzer(); + for(GraphBundle tg : installTGs) analyzer.addGraph(tg, tg.getGraph()); + for(GraphBundle tg : reinstallTGs.keySet()) analyzer.addGraph(tg, tg.getGraph()); + if(!analyzer.analyzeDependency()) { + Collection> problems = analyzer.getConflicts(); + StringBuilder sb = new StringBuilder(); + for (Pair problem : problems) { + sb.append("Conflict with "+problem.first+" and "+problem.second+".\n"); + } + throw new PlatformException(sb.toString()); + } + else if(!session.syncRequest( analyzer.queryExternalDependenciesSatisfied )) { + Collection 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()); + } + + List 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(); + } + }); + + boolean mergedOntologies = false; + + // Install TGs + for(final GraphBundle tg : sortedBundles) { + + final IImportAdvisor advisor = new OntologyImportAdvisor(tg, mgmt); + final GraphBundle oldTG = reinstallTGs.get(tg); + + if (oldTG==null) { + + boolean createImmutable = tg.getImmutable(); + session.getService(XSupport.class).setServiceMode(true, createImmutable); + + // Install TG + log.log(new Status(IStatus.INFO, Activator.PLUGIN_ID, "Installing "+tg.toString()+" - "+tg.getName())); + TransferableGraphs.importGraph1(session, new TGTransferableGraphSource(tg.getGraph()), advisor, null); + } else { + // 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(new WriteRequest() { + @Override + public void perform(WriteGraph graph) throws DatabaseException { + graph.setClusterSet4NewResource(graph.getRootLibrary()); + graph.flushCluster(); + } + }); + + if (mergedOntologies) + DatabaseIndexing.deleteAllIndexes(); + } + + TimeLogger.log("Ontologies synchronized."); + + } + session.getService(XSupport.class).setServiceMode(false, false); + } + progressMonitor.worked(20); + } 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 { + + if (progressMonitor == null) progressMonitor = new NullProgressMonitor(); + + final DatabaseManagement mgmt = new DatabaseManagement(); + + progressMonitor.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); + try { + // The project needs to be created mutable. + session.getService(XSupport.class).setServiceMode(true, false); + + ArrayList empty = new ArrayList(); + 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); + } + progressMonitor.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 publishedFeatureGroups = ProjectFeatures.getInstallGroupsOfPublishedFeatures(); + Collection groupsWithoutVersion = GroupReference.stripVersions(publishedFeatureGroups); + + // final List Platform_Features = new ArrayList(); + // + // // Convert graph instances + // Collection platformGraphs = new ArrayList(); + // 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) { + 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() { + + @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(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 { + File dbLocation = Platform.getLocation().append("db").toFile(); + if(!dbLocation.exists()) return; + try { // Load driver + Driver driver = Manager.getDriver("procore"); + 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 fileFilter) throws PlatformException, IllegalStateException, IOException { + File file = Platform.getLocation().toFile(); + if (null != fileFilter) + FileUtils.deleteAllWithFilter(file , fileFilter); + resetDatabase(monitor); + } + + /** + * Start-up the platform. The procedure consists of 8 steps. Once everything + * is up and running, all fields are set property. + *

+ * + * If workspacePolicy is FixErrors, there is an attempt to fix unexpected + * errors. It includes installing database files, installing ontologies, and + * installing project features. + *

+ * + * In SWB this is handled in SimanticsWorkbenchAdvisor#openWindows(). + *

+ * + * 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 null to resort to default measures + * @throws PlatformException + */ + public SessionContext startUp(String databaseDriverId, IProgressMonitor progressMonitor, RecoveryPolicy workspacePolicy, + OntologyRecoveryPolicy ontologyPolicy, boolean requireSynchronize, PlatformUserAgent userAgent) + throws PlatformException + { + + assert(!running); + TimeLogger.log("Beginning of SimanticsPlatform.startUp"); + + LOGGER.info("Beginning of SimanticsPlatform.startUp"); + + if (progressMonitor == null) progressMonitor = new NullProgressMonitor(); + + // 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(); + TimeLogger.log("Consulted platform pre-startup extensions"); + + // 0.1. Clear all temporary files + Simantics.clearTemporaryDirectory(); + TimeLogger.log("Cleared temporary directory"); + + // 0.2 Clear VariableRepository.repository static map which holds references to SessionImplDb + VariableRepository.clear(); + + // 1. Assert there is a database at /db + session = setupDatabase(databaseDriverId, progressMonitor, workspacePolicy, userAgent); + TimeLogger.log("Database setup complete"); + + // 1.1 + XSupport support = session.getService(XSupport.class); + if (support.rolledback()) { + try { + DatabaseIndexing.deleteAllIndexes(); + } catch (IOException e) { + throw new PlatformException(e); + } + } + + // 2. Assert all graphs, and correct versions, are installed to the database + synchronizeOntologies(progressMonitor, ontologyPolicy, requireSynchronize); + TimeLogger.log("Synchronized ontologies"); + + // 4. Assert simantics.cfg exists + boolean installProject = assertConfiguration(progressMonitor,workspacePolicy); + + // 5. Assert Project Resource is installed in the database + installProject = assertProject(progressMonitor, workspacePolicy, installProject); + + // 6. Install all features into project, if in debug mode + updateInstalledGroups(progressMonitor, installProject); + TimeLogger.log("Installed all features into project"); + + // 7. Assert L0.Session in database for this session + assertSessionModel(progressMonitor); + + 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); + } + + }); + TimeLogger.log("Flushed queries"); + } catch (DatabaseException e) { + LOGGER.error("Flushing queries failed.", e); + } + boolean loadProject = true; + try { + + 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 + simanticsBindings = new SimanticsBindings( null ); + 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 ); + + // 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) { + + TimeLogger.log("Load projects"); + project = Projects.loadProject(sessionContext.getSession(), SimanticsPlatform.INSTANCE.projectResource); + + sessionContext.setHint(ProjectKeys.KEY_PROJECT, project); + TimeLogger.log("Loading projects complete"); + + project.activate(); + TimeLogger.log("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; + + return sessionContext; + + } + + public SessionContext createSessionContext(boolean init) throws PlatformException { + try { + // Construct and initialize SessionContext from Session. + SessionContext sessionContext = SessionContext.create(session, init); + if (init) + sessionContext.registerServices(); + return sessionContext; + } catch (DatabaseException e) { + throw new PlatformException(e); + } + } + +// 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); +// } +// } +// } + + /** + * Shutdown Simantics Platform. + * + * In SWB this is handled in SimanticsWorkbenchAdvisor#disconnectFromWorkspace. + * + * @param progressMonitor optional progress monitor + * @throws PlatformException + */ + public void shutdown(IProgressMonitor progressMonitor) throws PlatformException + { + SubMonitor progress = SubMonitor.convert(progressMonitor, 100); + PlatformException platformException = null; + try { + progress.subTask("Close Project"); + if (project != null) { + project.safeDispose(); + } + progress.worked(10); + + 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); + } 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; + } + 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()); + + progress.worked(30); + + session = null; + projectResource = 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("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; + } + + // 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 true if discard was successful, false + * 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. + 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()); + } + } + +} +