Adding configuration logging for Simantics platform with SLF4J and
[simantics/platform.git] / bundles / org.simantics / src / org / simantics / SimanticsPlatform.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics;
13
14 import static org.simantics.db.common.utils.Transaction.commit;
15 import static org.simantics.db.common.utils.Transaction.endTransaction;
16 import static org.simantics.db.common.utils.Transaction.readGraph;
17 import static org.simantics.db.common.utils.Transaction.startTransaction;
18 import static org.simantics.db.common.utils.Transaction.writeGraph;
19
20 import java.io.File;
21 import java.io.IOException;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.HashMap;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Map.Entry;
28 import java.util.Properties;
29 import java.util.Set;
30 import java.util.TreeMap;
31 import java.util.UUID;
32
33 import org.eclipse.core.runtime.ILog;
34 import org.eclipse.core.runtime.IProduct;
35 import org.eclipse.core.runtime.IProgressMonitor;
36 import org.eclipse.core.runtime.IStatus;
37 import org.eclipse.core.runtime.NullProgressMonitor;
38 import org.eclipse.core.runtime.Platform;
39 import org.eclipse.core.runtime.Status;
40 import org.eclipse.core.runtime.SubMonitor;
41 import org.eclipse.osgi.service.resolver.BundleDescription;
42 import org.simantics.databoard.Bindings;
43 import org.simantics.databoard.Databoard;
44 import org.simantics.datatypes.literal.Font;
45 import org.simantics.datatypes.literal.RGB;
46 import org.simantics.db.Driver;
47 import org.simantics.db.Driver.Management;
48 import org.simantics.db.Manager;
49 import org.simantics.db.ReadGraph;
50 import org.simantics.db.Resource;
51 import org.simantics.db.Session;
52 import org.simantics.db.SessionModel;
53 import org.simantics.db.UndoContext;
54 import org.simantics.db.VirtualGraph;
55 import org.simantics.db.WriteGraph;
56 import org.simantics.db.common.request.ObjectsWithType;
57 import org.simantics.db.common.request.Queries;
58 import org.simantics.db.common.request.WriteRequest;
59 import org.simantics.db.common.request.WriteResultRequest;
60 import org.simantics.db.common.utils.Transaction;
61 import org.simantics.db.exception.ClusterSetExistException;
62 import org.simantics.db.exception.DatabaseException;
63 import org.simantics.db.exception.ResourceNotFoundException;
64 import org.simantics.db.indexing.DatabaseIndexing;
65 import org.simantics.db.layer0.genericrelation.DependenciesRelation;
66 import org.simantics.db.layer0.util.SimanticsClipboardImpl;
67 import org.simantics.db.layer0.util.SimanticsKeys;
68 import org.simantics.db.layer0.util.TGTransferableGraphSource;
69 import org.simantics.db.layer0.variable.VariableRepository;
70 import org.simantics.db.management.SessionContext;
71 import org.simantics.db.request.Read;
72 import org.simantics.db.service.LifecycleSupport.LifecycleListener;
73 import org.simantics.db.service.LifecycleSupport.LifecycleState;
74 import org.simantics.db.service.QueryControl;
75 import org.simantics.db.service.UndoRedoSupport;
76 import org.simantics.db.service.VirtualGraphSupport;
77 import org.simantics.db.service.XSupport;
78 import org.simantics.graph.db.GraphDependencyAnalyzer;
79 import org.simantics.graph.db.GraphDependencyAnalyzer.IU;
80 import org.simantics.graph.db.GraphDependencyAnalyzer.IdentityNode;
81 import org.simantics.graph.db.IImportAdvisor;
82 import org.simantics.graph.db.TransferableGraphs;
83 import org.simantics.graph.diff.Diff;
84 import org.simantics.graph.diff.TransferableGraphDelta1;
85 import org.simantics.internal.Activator;
86 import org.simantics.internal.startup.StartupExtensions;
87 import org.simantics.layer0.Layer0;
88 import org.simantics.operation.Layer0X;
89 import org.simantics.project.IProject;
90 import org.simantics.project.ProjectFeatures;
91 import org.simantics.project.ProjectKeys;
92 import org.simantics.project.Projects;
93 import org.simantics.project.exception.ProjectException;
94 import org.simantics.project.features.registry.GroupReference;
95 import org.simantics.project.management.DatabaseManagement;
96 import org.simantics.project.management.GraphBundle;
97 import org.simantics.project.management.GraphBundleEx;
98 import org.simantics.project.management.GraphBundleRef;
99 import org.simantics.project.management.PlatformUtil;
100 import org.simantics.project.management.ServerManager;
101 import org.simantics.project.management.ServerManagerFactory;
102 import org.simantics.project.management.WorkspaceUtil;
103 import org.simantics.utils.FileUtils;
104 import org.simantics.utils.datastructures.Pair;
105 import org.simantics.utils.logging.TimeLogger;
106 import org.slf4j.LoggerFactory;
107
108 /**
109  * SimanticsPlatform performs procedures required in order to get simantics
110  * workbench into operational state. This consists of the following steps:
111  * <ul>
112  *     <li> Asserting there is Database
113  *     </li>
114  *     <li> Starting Database process
115  *     </li>
116  *     <li> Opening a session to Database process
117  *     </li>
118  *     <li> Asserting required ontologies or other transferable graphs are installed in the database
119  *     </li>
120  *     <li> Asserting required project is installed in the database
121  *     </li>
122  *     <li> Asserting Simantics Features are installed in the database
123  *     </li>
124  *     <li> Asserting Simantics Features are installed to the project
125  *     </li>
126  *     <li> Shutdown: Save Session, Close session, Kill Database process
127  *     </li>
128  * </ul>
129  *
130  * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
131  */
132 public class SimanticsPlatform implements LifecycleListener {
133
134     private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(SimanticsPlatform.class);
135     
136     /**
137      * The policy is relevant when developing Simantics from Eclipse IDE.
138      * It is applied when the ontology in the database of a workspace doesn't match
139      * a newer ontology in the Eclipse workspace.
140      */
141     public static enum OntologyRecoveryPolicy { ThrowError, Merge, ReinstallDatabase }
142
143     /**
144      * This policy dictates how the Simantics platform startup should react if
145      * the started workspace is not set up properly. The alternatives are to
146      * just throw an error and fail or to attempt all possible measures to fix
147      * the encountered problems.
148      */
149     public static enum RecoveryPolicy { ThrowError, FixError }
150
151     /** Singleton instance, started in SimanticsWorkbenchAdvisor */
152     public static final SimanticsPlatform INSTANCE = new SimanticsPlatform();
153
154     /** Set to true when the Simantics Platform is in good-and-go condition */
155     public boolean running;
156
157     /** Database Session */
158     public Session session;
159     private Management databasebManagement;
160
161     /** Database session context */
162     public SessionContext sessionContext;
163
164     /** Project identifier in Database */
165     public String projectURI;
166
167     /** Project name */
168     public String projectName;
169
170     /** Project resource */
171     public Resource projectResource;
172
173     /** Session specific bindings */
174     public SimanticsBindings simanticsBindings;
175     public SimanticsBindings simanticsBindings2;
176
177     public Thread mainThread;
178
179     /**
180      * The {@link IProject} activated by
181      * {@link #startUp(IProgressMonitor, RecoveryPolicy, OntologyRecoveryPolicy, ServerAddress, PlatformUserAgent)}
182      */
183     private IProject project;
184
185     protected ILog log;
186
187     /**
188      * Create a new simantics plaform manager in uninitialized state and
189      * with default policies. <p>
190      */
191     public SimanticsPlatform() {
192         log = Platform.getLog(Activator.getBundleContext().getBundle());
193         mainThread = Thread.currentThread();
194     }
195
196     public String getApplicationClientId() {
197         IProduct product = Platform.getProduct();
198         if(product == null) return "noProduct";//UUID.randomUUID().toString();
199         String application = product.getApplication();
200         return application != null ? application : UUID.randomUUID().toString();
201     }
202
203     private Session setupDatabase(String databaseDriverId, IProgressMonitor progressMonitor, RecoveryPolicy workspacePolicy, PlatformUserAgent userAgent) throws PlatformException {
204         if (progressMonitor == null)
205             progressMonitor = new NullProgressMonitor();
206         File dbLocation = Platform.getLocation().append("db").toFile();
207         ServerManager serverManager;
208         try {
209             serverManager = ServerManagerFactory.create(databaseDriverId, dbLocation.getAbsolutePath());
210         } catch (DatabaseException | IOException e) {
211             throw new PlatformException("Failed to initialize Server Manager", e);
212         }
213         progressMonitor.beginTask("Setting up Simantics Database", 100);
214         progressMonitor.setTaskName("Asserting Database is installed.");
215         String msg = "Failed to initialize Simantics database.";
216         try {
217             // Create database
218             log.log(new Status(IStatus.INFO, Activator.PLUGIN_ID, "Creating database at " + dbLocation));
219             progressMonitor.setTaskName("Creating database at " + dbLocation);
220             databasebManagement = serverManager.getManagement(dbLocation);
221             databasebManagement.create();
222             // Create layer0.
223             return serverManager.createDatabase(dbLocation);
224         } catch (DatabaseException e) {
225             throw new PlatformException(msg, e);
226         } catch (Throwable e) {
227             throw new PlatformException(msg, e);
228         } finally {
229             progressMonitor.worked(20);
230         }
231     }
232
233     public void synchronizeOntologies(IProgressMonitor progressMonitor, OntologyRecoveryPolicy ontologyPolicy, boolean requireSynchronize) throws PlatformException {
234
235         if (progressMonitor == null) progressMonitor = new NullProgressMonitor();
236
237         final DatabaseManagement mgmt = new DatabaseManagement();
238
239         PlatformUtil.compileAllDynamicOntologies();
240
241         progressMonitor.setTaskName("Asserting all ontologies are installed");
242         final Map<GraphBundleRef, GraphBundleEx> platformTGs = new HashMap<GraphBundleRef, GraphBundleEx>();
243         try {
244
245             // Get a list of bundles installed into the database
246             progressMonitor.subTask("find installed bundles from database");
247             Map<GraphBundleRef, GraphBundleEx> installedTGs = new HashMap<GraphBundleRef, GraphBundleEx>();
248             for (GraphBundle b : session.syncRequest( mgmt.GraphBundleQuery )) {
249                 installedTGs.put(GraphBundleRef.of(b), GraphBundleEx.extend(b));
250             }
251
252             if(!requireSynchronize && installedTGs.size() > 1 && !Platform.inDevelopmentMode()) return;
253 //            if(installedTGs.size() > 1) return;
254
255             // Get a list of all bundles in the platform (Bundle Context)
256             List<GraphBundle> tgs = new ArrayList<GraphBundle>();
257             progressMonitor.subTask("load all transferable graphs from platform");
258             PlatformUtil.getAllGraphs(tgs);
259             progressMonitor.subTask("extend bundles to compile versions");
260             for (GraphBundle b : tgs) {
261                 GraphBundleEx gbe = GraphBundleEx.extend(b);
262                 gbe.build();
263                 platformTGs.put(GraphBundleRef.of(b), gbe);
264             }
265
266             // Compile a list of TGs that need to be installed or reinstalled in the database
267             progressMonitor.subTask("check bundle reinstallation demand");
268             List<GraphBundleEx> installTGs = new ArrayList<GraphBundleEx>();
269             // Create list of TGs to update, <newTg, oldTg>
270             Map<GraphBundleEx,GraphBundleEx> reinstallTGs = new TreeMap<GraphBundleEx,GraphBundleEx>();
271             for (Entry<GraphBundleRef, GraphBundleEx> e : platformTGs.entrySet()) {
272                 GraphBundleRef key = e.getKey();
273                 GraphBundleEx platformBundle = e.getValue();
274                 GraphBundleEx existingBundle = installedTGs.get(key);
275                 
276 //                System.out.println("GraphBundleRef key=" + key.toString());
277                 
278                 if (existingBundle == null) {
279                     // Bundle did not exist in the database, put it into list of bundles to install
280                     installTGs.add(platformBundle);
281                 }
282                 else {
283                     // Bundle exists in the database
284                     boolean platformBundleIsNewer = existingBundle.getVersion().compareTo(platformBundle.getVersion())<0;
285                     if (!platformBundleIsNewer)
286                         continue;
287                     // Check hash of transferable graph to know whether to update or not.
288                     if (platformBundle.getHashcode() == existingBundle.getHashcode())
289                         continue;
290                     //System.out.println("Ontology hashcodes do not match: platform bundle="
291                     //        + platformBundle.getVersionedId() + ", hash=" + platformBundle.getHashcode()
292                     //        + "; existing bundle=" + existingBundle.getVersionedId() + ", hash=" + existingBundle.getHashcode());
293                     reinstallTGs.put(platformBundle, existingBundle);
294                 }
295             }
296             // INSTALL
297             // Database is missing graphs
298             if (!installTGs.isEmpty() || !reinstallTGs.isEmpty()) {
299                 session.getService(XSupport.class).setServiceMode(true, true);
300
301                 // Throw error
302                 if (ontologyPolicy == OntologyRecoveryPolicy.ThrowError) {
303                     StringBuilder sb = new StringBuilder("The following graphs are not installed in the database: ");
304                     if (!installTGs.isEmpty()) {
305                         int i = 0;
306                         for (GraphBundleEx e : installTGs) {
307                             if (i>0) sb.append(", ");
308                             i++;
309                             sb.append(e.toString());
310                         }
311                         sb.append(" is missing from the database.\n");
312                     }
313                     if (!reinstallTGs.isEmpty()) {
314                         int i = 0;
315                         for (Entry<GraphBundleEx, GraphBundleEx> e : reinstallTGs.entrySet()) {
316                             if (i>0) sb.append(", ");
317                             i++;
318                             sb.append(e.getKey().toString());
319                         }
320                         sb.append(" Database/Platform Bundle version mismatch.\n");
321                     }
322                     sb.append("Hint: Use -fixErrors to install the graphs.");
323                     throw new PlatformException(sb.toString());
324                 }
325                 // Reinstall database
326                 if (ontologyPolicy == OntologyRecoveryPolicy.ReinstallDatabase) {
327                     log.log(new Status(IStatus.INFO, Activator.PLUGIN_ID, "Reinstalling the database."));
328                     // TODO Install DB
329                     // Stop Session
330                     // Kill Process
331                     // Delete Database
332                     // Create Database
333                     // Start Database
334                     // Open Session
335                     // Install TGs
336                     throw new PlatformException("Reinstalling Database, NOT IMPLEMENTED");
337                 }
338
339                 if (ontologyPolicy == OntologyRecoveryPolicy.Merge) {
340                     progressMonitor.subTask("Merging ontology changes");
341                     // Sort missing TGs into install order
342                     GraphDependencyAnalyzer<GraphBundle> analyzer = new GraphDependencyAnalyzer<GraphBundle>();
343                     for(GraphBundle tg : installTGs) analyzer.addGraph(tg, tg.getGraph());
344                     for(GraphBundle tg : reinstallTGs.keySet()) analyzer.addGraph(tg, tg.getGraph());
345                     if(!analyzer.analyzeDependency()) {
346                         Collection<Pair<GraphBundle, GraphBundle>> problems = analyzer.getConflicts();
347                         StringBuilder sb = new StringBuilder();
348                         for (Pair<GraphBundle, GraphBundle> problem : problems) {
349                             sb.append("Conflict with "+problem.first+" and "+problem.second+".\n");
350                         }
351                         throw new PlatformException(sb.toString());
352                     }
353                     else if(!session.syncRequest( analyzer.queryExternalDependenciesSatisfied )) {
354                         Collection<IdentityNode> unsatisfiedDependencies = analyzer.getUnsatisfiedDependencies();
355                         StringBuilder sb = new StringBuilder();
356                         for (IdentityNode dep: unsatisfiedDependencies) {
357                             sb.append("Unsatisfied Dependency "+dep+". Required by\n");
358                             for(IU iu : GraphDependencyAnalyzer.toCollection(dep.getRequires())) {
359                                 sb.append("    " + ((GraphBundle)iu.getId()).getId() + "\n");
360                             }
361                         }
362                         throw new PlatformException(sb.toString());
363                     }
364                     
365                     List<GraphBundle> sortedBundles = analyzer.getSortedGraphs();
366                     if(!sortedBundles.isEmpty()) {
367                         
368                         session.syncRequest(new WriteRequest() {
369                             @Override
370                             public void perform(WriteGraph graph) throws DatabaseException {
371                                 try {
372                                     graph.newClusterSet(graph.getRootLibrary());
373                                 } catch (ClusterSetExistException e) {
374                                     // Cluster set exist already, no problem.
375                                 }
376                                 graph.setClusterSet4NewResource(graph.getRootLibrary());
377                                 graph.flushCluster();
378                             }
379                         });
380
381                         boolean mergedOntologies = false;
382
383                         // Install TGs
384                         for(final GraphBundle tg : sortedBundles) {
385
386                                 final IImportAdvisor advisor = new OntologyImportAdvisor(tg, mgmt);
387                                 final GraphBundle oldTG = reinstallTGs.get(tg);
388
389                                 if (oldTG==null) {
390                                         
391                                         boolean createImmutable = tg.getImmutable();
392                                 session.getService(XSupport.class).setServiceMode(true, createImmutable);
393
394                                         // Install TG
395                                         log.log(new Status(IStatus.INFO, Activator.PLUGIN_ID, "Installing "+tg.toString()+" - "+tg.getName()));
396                                         TransferableGraphs.importGraph1(session, new TGTransferableGraphSource(tg.getGraph()), advisor, null);
397                                 } else {
398                                         // Merge TG
399                                         startTransaction(session, false);
400                                         TransferableGraphDelta1 delta = new Diff(oldTG.getGraph(), tg.getGraph()).diff();
401                                         final long[] oldResources = oldTG.getResourceArray();
402                                         boolean changes = TransferableGraphs.hasChanges(readGraph(), oldResources, delta);
403                                         endTransaction();
404                                         if (!changes) {
405                                             //log.log(new Status(IStatus.INFO, Activator.PLUGIN_ID, "Nothing to merge for "+tg.toString()));
406                                             continue;
407                                         }
408
409                                 log.log(new Status(IStatus.INFO, Activator.PLUGIN_ID, "Merging new version of "+tg.toString()));
410
411                                         startTransaction(session, true);
412                                         
413                                         //delta.print();
414                                         try {
415                                                 
416                                                 
417                                                 long[] resourceArray = TransferableGraphs.applyDelta(writeGraph(), oldResources, delta);
418                                                 tg.setResourceArray(resourceArray);
419                                                 mgmt.setGraphBundleEntry(tg);
420                                                 commit();
421                                                 mergedOntologies = true;
422                                         } catch (Throwable t) {
423                                                 throw new PlatformException(t);
424                                         } finally {
425                                                 endTransaction();
426                                         }
427                                 }
428                         }
429                         
430                         session.syncRequest(new WriteRequest() {
431                             @Override
432                             public void perform(WriteGraph graph) throws DatabaseException {
433                                 graph.setClusterSet4NewResource(graph.getRootLibrary());
434                                 graph.flushCluster();
435                             }
436                         });
437
438                         if (mergedOntologies)
439                             DatabaseIndexing.deleteAllIndexes();
440                     }
441
442                     TimeLogger.log("Ontologies synchronized.");
443                     
444                 }
445                 session.getService(XSupport.class).setServiceMode(false, false);
446             }
447             progressMonitor.worked(20);
448         } catch (IOException e) {
449             throw new PlatformException(e);
450         } catch (DatabaseException e) {
451             throw new PlatformException(e);
452         }
453
454     }
455
456     public boolean assertConfiguration(IProgressMonitor progressMonitor, RecoveryPolicy workspacePolicy) throws PlatformException {
457
458         if (progressMonitor == null) progressMonitor = new NullProgressMonitor();
459
460         File workspaceLocation = Platform.getLocation().toFile();
461
462         boolean installProject = false;
463         progressMonitor.setTaskName("Asserting simantics.cfg is installed");
464         try {
465             File propertyFile = new File(workspaceLocation, "simantics.cfg");
466             Properties properties;
467             try {
468                 properties = WorkspaceUtil.readProperties(propertyFile);
469             } catch (IOException e) {
470                 if (workspacePolicy == RecoveryPolicy.ThrowError) throw new PlatformException("Could not load "+propertyFile);
471
472                 // Create a project and write Property file
473                 properties = new Properties();
474                 properties.setProperty("project_uri", "http://Projects/Development%20Project");
475                 properties.setProperty("project_name", "Development Project");
476                 WorkspaceUtil.writeProperties(propertyFile, properties);
477                 installProject |= true;
478             }
479             projectURI = properties.getProperty("project_uri");
480             projectName = properties.getProperty("project_name");
481             progressMonitor.worked(10);
482         } catch (IOException e) {
483             throw new PlatformException(e);
484         }
485
486         return installProject;
487
488     }
489
490     public boolean assertProject(IProgressMonitor progressMonitor, RecoveryPolicy workspacePolicy, boolean installProject) throws PlatformException {
491
492         if (progressMonitor == null) progressMonitor = new NullProgressMonitor();
493
494         final DatabaseManagement mgmt = new DatabaseManagement();
495
496         progressMonitor.setTaskName("Asserting project resource exists in the database");
497         try {
498             projectResource = session.syncRequest( Queries.resource( projectURI ) );
499         } catch (ResourceNotFoundException nfe) {
500             // Project was not found
501             if (workspacePolicy == RecoveryPolicy.ThrowError)
502                 throw new PlatformException("Project Resource "+projectURI+" is not found in the database.");
503             // Create empty project with no features
504             try {
505                 Transaction.startTransaction(session, true);
506                 try {
507                     // The project needs to be created mutable.
508                     session.getService(XSupport.class).setServiceMode(true, false);
509
510                     ArrayList<String> empty = new ArrayList<String>();
511                     projectResource = mgmt.createProject(projectName, empty);
512                     installProject |= true;
513
514                     session.getService(XSupport.class).setServiceMode(false, false);
515                     Transaction.commit();
516                 } finally {
517                     Transaction.endTransaction();
518                 }
519                 //session.getService( LifecycleSupport.class ).save();
520             } catch (DatabaseException e) {
521                 throw new PlatformException("Failed to create "+projectURI, e);
522             }
523         } catch (DatabaseException e) {
524             throw new PlatformException("Failed to create "+projectURI, e);
525         }
526         progressMonitor.worked(10);
527
528         return installProject;
529
530     }
531
532     public void updateInstalledGroups(IProgressMonitor progressMonitor, boolean installProject) throws PlatformException {
533
534         if (installProject)
535         {
536             // Attach all feature groups available in platform to created project
537             progressMonitor.setTaskName("Install all features");
538             Set<GroupReference> publishedFeatureGroups = ProjectFeatures.getInstallGroupsOfPublishedFeatures();
539             Collection<GroupReference> groupsWithoutVersion = GroupReference.stripVersions(publishedFeatureGroups);
540
541     //        final List<String> Platform_Features = new ArrayList<String>();
542     //
543     //        // Convert graph instances
544     //        Collection<TransferableGraph1> platformGraphs = new ArrayList<TransferableGraph1>();
545     //        for (GraphBundleEx e : platformTGs.values()) platformGraphs.add( e.getGraph() );
546     //        IGraph graph = Graphs.createGraph(platformGraphs);
547     //
548     //        Res    PublishedProjectFeatures = UriUtils.uriToPath( ProjectResource.URIs.PublishedProjectFeatures );
549     //        Path   HasFeature = UriUtils.uriToPath( ProjectResource.URIs.HasFeature );
550     //        for(Res feature : graph.getObjects(PublishedProjectFeatures, HasFeature)) {
551     //            System.out.println("Installing Project Feature: "+feature.toString());
552     //            Platform_Features.add( feature.toString() );
553     //        }
554
555             try {
556                 Transaction.startTransaction(session, true);
557                 try {
558     //                for (String feature : Platform_Features) {
559     //                    try {
560     //                        getResource(feature);
561     //                    } catch(ResourceNotFoundException e) {
562     //                        System.out.println(feature+" not found");
563     //                    }
564     //                    mgmt.installFeature(projectResource, feature);
565     //                }
566                     Projects.setProjectInstalledGroups(writeGraph(), projectResource, groupsWithoutVersion);
567                     Transaction.commit();
568                 } finally {
569                     Transaction.endTransaction();
570                 }
571                 //session.getService( LifecycleSupport.class ).save();
572             } catch(DatabaseException ae) {
573                 throw new PlatformException("Failed to install features", ae);
574             }
575             progressMonitor.worked(10);
576         }
577
578     }
579
580     public void assertSessionModel(IProgressMonitor progressMonitor) throws PlatformException {
581
582         Properties properties = session.getService(Properties.class);
583         final String clientId = properties.getProperty("clientId");
584
585         try {
586
587             // Currently this needs to be done before data becomes available
588             VirtualGraphSupport support = session.getService(VirtualGraphSupport.class);
589             VirtualGraph activations = support.getWorkspacePersistent("activations");
590
591             Resource sessionModel = session.syncRequest(new Read<Resource>() {
592
593                 @Override
594                 public Resource perform(ReadGraph graph) throws DatabaseException {
595
596                     Layer0X L0X = Layer0X.getInstance(graph);
597                     for(Resource sessionModel : graph.syncRequest(new ObjectsWithType(graph.getRootLibrary(), L0X.HasSession, L0X.Session))) {
598                         String id = graph.getPossibleRelatedValue(sessionModel, L0X.Session_HasClientId);
599                         if(id != null && id.equals(clientId)) return sessionModel;
600                     }
601                     return null;
602
603                 }
604
605             });
606
607             if(sessionModel == null) {
608
609                 sessionModel = session.syncRequest(new WriteResultRequest<Resource>(activations) {
610
611                     @Override
612                     public Resource perform(WriteGraph graph) throws DatabaseException {
613                         Layer0 L0 = Layer0.getInstance(graph);
614                         Layer0X L0X = Layer0X.getInstance(graph);
615                         Resource session = graph.newResource();
616                         graph.claim(session, L0.InstanceOf, null, L0X.Session);
617                         graph.claim(session, L0X.Session_HasUser, null, graph.getResource("http://Users/AdminUser"));
618                         graph.addLiteral(session, L0X.Session_HasClientId, L0X.Session_HasClientId_Inverse, clientId, Bindings.STRING);
619                         graph.claim(graph.getRootLibrary(), L0X.HasSession, session);
620                         return session;
621                     }
622                 });
623
624             }
625
626             session.registerService(SessionModel.class, new PlatformSessionModel(sessionModel));
627         } catch (DatabaseException e) {
628             throw new PlatformException(e);
629         }
630
631     }
632
633     static class PlatformSessionModel implements SessionModel {
634         private final Resource sessionModel;
635
636         public PlatformSessionModel(Resource model) {
637             this.sessionModel = model;
638         }
639
640         @Override
641         public Resource getResource() {
642             return sessionModel;
643         }
644     }
645
646     public void resetDatabase(IProgressMonitor monitor) throws PlatformException {
647         File dbLocation = Platform.getLocation().append("db").toFile();
648         if(!dbLocation.exists()) return;
649         try { // Load driver
650             Driver driver = Manager.getDriver("procore");
651             Management management = driver.getManagement(dbLocation.getAbsolutePath(), null);
652             management.delete();
653         } catch (DatabaseException e) {
654             throw new PlatformException("Failed to remove database at " + dbLocation.getAbsolutePath(), e);
655         }
656         // We have created extra files to database folder which have to be deleted also.
657         // This is an awful idea! Do not create extra files to database folder!
658         Throwable t = null;
659         for (int i=0; i<10; ++i) {
660             try {
661                 FileUtils.deleteAll(dbLocation);
662                 t = null;
663                 break;
664             } catch (IOException e) {
665                 // Assuming this has been thrown because delete file/folder failed.
666                 t = e;
667             }
668             try {
669                 Thread.sleep(200);
670             } catch (InterruptedException e) {
671                 // Ignoring interrupted exception.
672             }
673         }
674         if (null != t)
675             throw new PlatformException("Failed to remove database folder at " + dbLocation.getAbsolutePath(), t);
676     }
677     public void resetWorkspace(IProgressMonitor monitor, ArrayList<String> fileFilter) throws PlatformException, IllegalStateException, IOException {
678         File file = Platform.getLocation().toFile();
679         if (null != fileFilter)
680             FileUtils.deleteAllWithFilter(file , fileFilter);
681         resetDatabase(monitor);
682     }
683
684     /**
685      * Start-up the platform. The procedure consists of 8 steps. Once everything
686      * is up and running, all fields are set property.
687      * <p>
688      *
689      * If workspacePolicy is FixErrors, there is an attempt to fix unexpected
690      * errors. It includes installing database files, installing ontologies, and
691      * installing project features.
692      * <p>
693      *
694      * In SWB this is handled in SimanticsWorkbenchAdvisor#openWindows().
695      * <p>
696      *
697      * If remote server is given, simantics plaform takes connection there
698      * instead of local server at "db/".
699      *
700      * @param workspacePolicy action to take on workspace/database related
701      *        errors
702      * @param ontologyPolicy action to take on ontology mismatch
703      * @param progressMonitor optional progress monitor
704      * @param userAgent interface for resorting to user feedback during platform
705      *        startup or <code>null</code> to resort to default measures
706      * @throws PlatformException
707      */
708     public SessionContext startUp(String databaseDriverId, IProgressMonitor progressMonitor, RecoveryPolicy workspacePolicy,
709             OntologyRecoveryPolicy ontologyPolicy, boolean requireSynchronize, PlatformUserAgent userAgent)
710     throws PlatformException
711     {
712
713         assert(!running);
714         TimeLogger.log("Beginning of SimanticsPlatform.startUp");
715
716         LOGGER.info("Beginning of SimanticsPlatform.startUp");
717         
718         if (progressMonitor == null) progressMonitor = new NullProgressMonitor();
719
720         // For debugging on what kind of platform automatic tests are running in
721         // case there are problems.
722         if ("true".equals(System.getProperty("org.simantics.dumpBundleState")))
723             dumpPlatformBundleState();
724
725         // 0. Consult all startup extensions before doing anything with the workspace.
726         StartupExtensions.consultStartupExtensions();
727         TimeLogger.log("Consulted platform pre-startup extensions");
728
729         // 0.1. Clear all temporary files
730         Simantics.clearTemporaryDirectory();
731         TimeLogger.log("Cleared temporary directory");
732
733         // 0.2 Clear VariableRepository.repository static map which holds references to SessionImplDb
734         VariableRepository.clear();
735         
736         // 1. Assert there is a database at <workspace>/db
737         session = setupDatabase(databaseDriverId, progressMonitor, workspacePolicy, userAgent);
738         TimeLogger.log("Database setup complete");
739
740         // 2. Assert all graphs, and correct versions, are installed to the database
741         synchronizeOntologies(progressMonitor, ontologyPolicy, requireSynchronize);
742         TimeLogger.log("Synchronized ontologies");
743
744         // 4. Assert simantics.cfg exists
745         boolean installProject = assertConfiguration(progressMonitor,workspacePolicy);
746
747         // 5. Assert Project Resource is installed in the database
748         installProject = assertProject(progressMonitor, workspacePolicy, installProject);
749
750         // 6. Install all features into project, if in debug mode
751         updateInstalledGroups(progressMonitor, installProject);
752         TimeLogger.log("Installed all features into project");
753
754         // 7. Assert L0.Session in database for this session
755         assertSessionModel(progressMonitor);
756
757         session.getService(XSupport.class).setServiceMode(false, false);
758
759         try {
760             session.sync(new WriteRequest() {
761
762                 @Override
763                 public void perform(WriteGraph graph) throws DatabaseException {
764                     QueryControl qc = graph.getService(QueryControl.class);
765                     qc.flush(graph);
766                 }
767
768             });
769             TimeLogger.log("Flushed queries");
770         } catch (DatabaseException e) {
771             Logger.defaultLogError(e);
772         }
773         boolean loadProject = true;
774         try {
775
776                 sessionContext = SimanticsPlatform.INSTANCE.createSessionContext(true);
777                 // This must be before setSessionContext since some listeners might query this
778             sessionContext.setHint(SimanticsKeys.KEY_PROJECT, SimanticsPlatform.INSTANCE.projectResource);
779
780             Simantics.setSessionContext(sessionContext);
781
782             // 1. Put ResourceBinding that throws an exception to General Bindings
783             simanticsBindings = new SimanticsBindings( null );
784             Bindings.classBindingFactory.addFactory( simanticsBindings );
785
786
787             // 2. Create session-specific second Binding context (Databoard) and
788             //    put that to Session as a service
789             Session session = sessionContext.getSession();
790             Databoard sessionDataboard = new Databoard();
791             session.registerService(Databoard.class, sessionDataboard);
792             simanticsBindings2 = new SimanticsBindings( session );
793             sessionDataboard.classBindingFactory.addFactory( simanticsBindings2 );
794
795             // Register datatype bindings
796             Bindings.defaultBindingFactory.getRepository().put(RGB.Integer.BINDING.type(), RGB.Integer.BINDING);
797             Bindings.defaultBindingFactory.getRepository().put(Font.BINDING.type(), Font.BINDING);
798
799             if(loadProject) {
800
801                 TimeLogger.log("Load projects");
802                 project = Projects.loadProject(sessionContext.getSession(), SimanticsPlatform.INSTANCE.projectResource);
803
804                 sessionContext.setHint(ProjectKeys.KEY_PROJECT, project);
805                 TimeLogger.log("Loading projects complete");
806
807                 project.activate();
808                 TimeLogger.log("Project activated");
809             }
810
811         } catch (DatabaseException e) {
812             Logger.defaultLogError(e);
813             throw new PlatformException(e);
814         } catch (ProjectException e) {
815             boolean hasStackTrace = e.getStackTrace().length > 0;
816             if (!hasStackTrace)
817                 throw new PlatformException(e.getMessage(), hasStackTrace);
818             throw new PlatformException(e, hasStackTrace);
819         }
820
821         running = true;
822
823         return sessionContext;
824
825     }
826
827     public SessionContext createSessionContext(boolean init) throws PlatformException {
828         try {
829             // Construct and initialize SessionContext from Session.
830             SessionContext sessionContext = SessionContext.create(session, init);
831             if (init)
832                 sessionContext.registerServices();
833             return sessionContext;
834         } catch (DatabaseException e) {
835             throw new PlatformException(e);
836         }
837     }
838
839 //    private static File getIgnorePrerequisitesFile(URL workspaceUrl) {
840 //        if (workspaceUrl == null)
841 //            return null;
842 //        return new File(workspaceUrl.getPath(), ".ignorePrerequisites");
843 //    }
844 //
845 //    private void ensurePrerequisites(IProgressMonitor progressMonitor, PlatformUserAgent userAgent) throws PlatformException {
846 //        Location loc = Platform.getInstanceLocation();
847 //        File ignorePrerequisites = getIgnorePrerequisitesFile(loc.getURL());
848 //        if (loc.isSet() && ignorePrerequisites != null) {
849 //            if (ignorePrerequisites.exists() || ignorePrerequisites.isFile())
850 //                return;
851 //        }
852 //
853 //        try {
854 //            ServerEnvironment.ensureServerDependenciesMet();
855 //        } catch (ExecutionEnvironmentException e) {
856 //            // Not installed properly, ask user whether to try installation.
857 //            try {
858 //                StringBuilder msg = new StringBuilder();
859 //                msg.append("Your system seems to be missing the following prerequisites for running this application:\n\n");
860 //                for (Product product : e.requiredProducts)
861 //                    msg.append("\t" + product.getDescription() + "\n");
862 //                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.");
863 //                msg.append("\n\nSelecting Cancel will close the application.");
864 //
865 //                int selection = 0;
866 //                if (userAgent != null) {
867 //                    selection = userAgent.showPrompt("Missing Prerequisites", msg.toString(), new String[] {
868 //                        "Install Pre-requisites",
869 //                        "Ignore Now",
870 //                        "Ignore Always",
871 //                        "Cancel"
872 //                    }, selection);
873 //                }
874 //                boolean tryInstall = false;
875 //                switch (selection) {
876 //                    case 0:
877 //                        tryInstall = true;
878 //                        break;
879 //                    case 2:
880 //                        ignorePrerequisites.createNewFile();
881 //                    case 1:
882 //                        break;
883 //                    case 3:
884 //                    case -1:
885 //                        throw new CancelStartupException();
886 //                }
887 //
888 //                if (tryInstall) {
889 //                    // Try to install it and check for success afterwards.
890 //                    ServerEnvironment.tryInstallDependencies(progressMonitor);
891 //                    ServerEnvironment.ensureServerDependenciesMet();
892 //                }
893 //            } catch (InstallException ie) {
894 //                throw new PlatformException(ie);
895 //            } catch (ExecutionEnvironmentException eee) {
896 //                throw new PlatformException(eee);
897 //            } catch (IOException ie) {
898 //                throw new PlatformException(ie);
899 //            }
900 //        }
901 //    }
902
903     /**
904      * Shutdown Simantics Platform.
905      *
906      * In SWB this is handled in SimanticsWorkbenchAdvisor#disconnectFromWorkspace.
907      *
908      * @param progressMonitor optional progress monitor
909      * @throws PlatformException
910      */
911     public void shutdown(IProgressMonitor progressMonitor) throws PlatformException
912     {
913         SubMonitor progress = SubMonitor.convert(progressMonitor, 100);
914         PlatformException platformException = null;
915         try {
916             progress.subTask("Close Project");
917             if (project != null) {
918                 project.safeDispose();
919             }
920             progress.worked(10);
921
922             running = false;
923             progress.subTask("Close Database Session");
924             Databoard databoard = null;
925             if (sessionContext != null) {
926                 Session s = sessionContext.peekSession();
927                 if (s != null) {
928                     databoard = s.peekService(Databoard.class);
929
930                     progress.subTask("Flushing Index Caches");
931                     try {
932                         Simantics.flushIndexCaches(progress.newChild(20), s);
933                     } catch (Throwable t) {
934                         Logger.defaultLogError(t);
935                     }
936                 }
937
938                 progress.subTask("Close Database Session");
939                 sessionContext.safeDispose();
940                 sessionContext = null;
941                 Simantics.setSessionContext(null);
942             }
943             if (simanticsBindings != null) {
944                 Bindings.classBindingFactory.removeFactory( simanticsBindings );
945                 simanticsBindings = null;
946             }
947             if (databoard != null) {
948                 if (simanticsBindings2 != null) {
949                         databoard.classBindingFactory.removeFactory( simanticsBindings2 );
950                         simanticsBindings2 = null;
951                 }
952                 databoard.clear();
953             }
954
955             // Make sure Simantics clipboard doesn't store unwanted session data references.
956             Simantics.setClipboard(new SimanticsClipboardImpl());
957
958             progress.worked(30);
959
960             session = null;
961             projectResource = null;
962
963             DependenciesRelation.assertFinishedTracking();
964
965         } catch (Exception e) {
966             platformException = new PlatformException("Failed to shutdown Simantics Platform", e);
967         }
968
969         progress.worked(10);
970         progress.subTask("Shutting down database");
971         try {
972             if (null != databasebManagement)
973                 databasebManagement.shutdown();
974         } catch (Throwable t) {
975             Logger.defaultLogError(t);
976         }
977         progress.worked(10);
978
979         progress.subTask("Clearing Workspace Temporary Directory");
980         try {
981             Simantics.clearTemporaryDirectory();
982         } catch (Throwable t) {
983             Logger.defaultLogError(t);
984         }
985         progress.worked(10);
986         if (null != platformException)
987             throw platformException;
988     }
989
990     // TODO: consider removing this in the future ??
991     @Override
992     public void stateChanged(LifecycleState newState) {
993         if(newState == LifecycleState.CLOSED) {
994             if(running) {
995                 if(Platform.isRunning()) {
996                     mainThread.interrupt();
997                 }
998             }
999         }
1000     }
1001
1002     /**
1003      * @return <code>true</code> if discard was successful, <code>false</code>
1004      *         if there was no session, {@link UndoRedoSupport} or
1005      *         {@link UndoContext} to discard through
1006      */
1007     public boolean discardSessionUndoHistory() {
1008         Session s = session;
1009         if (s != null) {
1010             UndoRedoSupport urs = s.peekService(UndoRedoSupport.class);
1011             if (urs != null) {
1012                 UndoContext uc = urs.getUndoContext(s);
1013                 if (uc != null) {
1014                     uc.clear();
1015                     return true;
1016                 }
1017             }
1018         }
1019         return false;
1020     }
1021
1022     public void reconnect(String databaseDriverId) throws Exception {
1023         // Starts database server.
1024         SimanticsPlatform.INSTANCE.startUp(databaseDriverId, null, RecoveryPolicy.ThrowError, OntologyRecoveryPolicy.ThrowError, true, null);
1025     }
1026
1027     private void dumpPlatformBundleState() {
1028         BundleDescription[] bs = Platform.getPlatformAdmin().getState().getBundles();
1029         System.out.println("Total bundles: " + bs.length);
1030         for (BundleDescription b : bs) {
1031             System.out.format("%-80s @ %s\n", b.toString(), b.getLocation());
1032         }
1033     }
1034
1035 }
1036