/*******************************************************************************
* 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.nio.file.Files;
import java.nio.file.Path;
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.ini4j.Ini;
import org.ini4j.InvalidFileFormatException;
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:
*
*/
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;
/** 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 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();
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 {
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;
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("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.
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;
}
}