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