]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics/src/org/simantics/SimanticsPlatform.java
Sync git svn branch with SVN repository r33199.
[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 does not match! platformBundle" + platformBundle.getName() + ".getHashCode()=" + platformBundle.getHashcode() + " existingBundle" + existingBundle.getName() + ".getHashCode()=" + existingBundle.getHashcode());\r
288                     reinstallTGs.put(platformBundle, existingBundle);\r
289                 }\r
290             }\r
291             // INSTALL\r
292             // Database is missing graphs\r
293             if (!installTGs.isEmpty() || !reinstallTGs.isEmpty()) {\r
294                 session.getService(XSupport.class).setServiceMode(true, true);\r
295 \r
296                 // Throw error\r
297                 if (ontologyPolicy == OntologyRecoveryPolicy.ThrowError) {\r
298                     StringBuilder sb = new StringBuilder("The following graphs are not installed in the database: ");\r
299                     if (!installTGs.isEmpty()) {\r
300                         int i = 0;\r
301                         for (GraphBundleEx e : installTGs) {\r
302                             if (i>0) sb.append(", ");\r
303                             i++;\r
304                             sb.append(e.toString());\r
305                         }\r
306                         sb.append(" is missing from the database.\n");\r
307                     }\r
308                     if (!reinstallTGs.isEmpty()) {\r
309                         int i = 0;\r
310                         for (Entry<GraphBundleEx, GraphBundleEx> e : reinstallTGs.entrySet()) {\r
311                             if (i>0) sb.append(", ");\r
312                             i++;\r
313                             sb.append(e.getKey().toString());\r
314                         }\r
315                         sb.append(" Database/Platform Bundle version mismatch.\n");\r
316                     }\r
317                     sb.append("Hint: Use -fixErrors to install the graphs.");\r
318                     throw new PlatformException(sb.toString());\r
319                 }\r
320                 // Reinstall database\r
321                 if (ontologyPolicy == OntologyRecoveryPolicy.ReinstallDatabase) {\r
322                     log.log(new Status(IStatus.INFO, Activator.PLUGIN_ID, "Reinstalling the database."));\r
323                     // TODO Install DB\r
324                     // Stop Session\r
325                     // Kill Process\r
326                     // Delete Database\r
327                     // Create Database\r
328                     // Start Database\r
329                     // Open Session\r
330                     // Install TGs\r
331                     throw new PlatformException("Reinstalling Database, NOT IMPLEMENTED");\r
332                 }\r
333 \r
334                 if (ontologyPolicy == OntologyRecoveryPolicy.Merge) {\r
335                     progressMonitor.subTask("Merging ontology changes");\r
336                     // Sort missing TGs into install order\r
337                     GraphDependencyAnalyzer<GraphBundle> analyzer = new GraphDependencyAnalyzer<GraphBundle>();\r
338                     for(GraphBundle tg : installTGs) analyzer.addGraph(tg, tg.getGraph());\r
339                     for(GraphBundle tg : reinstallTGs.keySet()) analyzer.addGraph(tg, tg.getGraph());\r
340                     if(!analyzer.analyzeDependency()) {\r
341                         Collection<Pair<GraphBundle, GraphBundle>> problems = analyzer.getConflicts();\r
342                         StringBuilder sb = new StringBuilder();\r
343                         for (Pair<GraphBundle, GraphBundle> problem : problems) {\r
344                             sb.append("Conflict with "+problem.first+" and "+problem.second+".\n");\r
345                         }\r
346                         throw new PlatformException(sb.toString());\r
347                     }\r
348                     else if(!session.syncRequest( analyzer.queryExternalDependenciesSatisfied )) {\r
349                         Collection<IdentityNode> unsatisfiedDependencies = analyzer.getUnsatisfiedDependencies();\r
350                         StringBuilder sb = new StringBuilder();\r
351                         for (IdentityNode dep: unsatisfiedDependencies) {\r
352                             sb.append("Unsatisfied Dependency "+dep+". Required by\n");\r
353                             for(IU iu : GraphDependencyAnalyzer.toCollection(dep.getRequires())) {\r
354                                 sb.append("    " + ((GraphBundle)iu.getId()).getId() + "\n");\r
355                             }\r
356                         }\r
357                         throw new PlatformException(sb.toString());\r
358                     }\r
359                     \r
360                     List<GraphBundle> sortedBundles = analyzer.getSortedGraphs();\r
361                     if(!sortedBundles.isEmpty()) {\r
362                         \r
363                         session.syncRequest(new WriteRequest() {\r
364                             @Override\r
365                             public void perform(WriteGraph graph) throws DatabaseException {\r
366                                 try {\r
367                                     graph.newClusterSet(graph.getRootLibrary());\r
368                                 } catch (ClusterSetExistException e) {\r
369                                     // Cluster set exist already, no problem.\r
370                                 }\r
371                                 graph.setClusterSet4NewResource(graph.getRootLibrary());\r
372                                 graph.flushCluster();\r
373                             }\r
374                         });\r
375 \r
376                         boolean mergedOntologies = false;\r
377 \r
378                         // Install TGs\r
379                         for(final GraphBundle tg : sortedBundles) {\r
380 \r
381                                 final IImportAdvisor advisor = new OntologyImportAdvisor(tg, mgmt);\r
382                                 final GraphBundle oldTG = reinstallTGs.get(tg);\r
383 \r
384                                 if (oldTG==null) {\r
385                                         \r
386                                         boolean createImmutable = tg.getImmutable();\r
387                                 session.getService(XSupport.class).setServiceMode(true, createImmutable);\r
388 \r
389                                         // Install TG\r
390                                         log.log(new Status(IStatus.INFO, Activator.PLUGIN_ID, "Installing "+tg.toString()+" - "+tg.getName()));\r
391                                         TransferableGraphs.importGraph1(session, new TGTransferableGraphSource(tg.getGraph()), advisor, null);\r
392                                 } else {\r
393                                         // Merge TG\r
394                                         startTransaction(session, false);\r
395                                         TransferableGraphDelta1 delta = new Diff(oldTG.getGraph(), tg.getGraph()).diff();\r
396                                         final long[] oldResources = oldTG.getResourceArray();\r
397                                         boolean changes = TransferableGraphs.hasChanges(readGraph(), oldResources, delta);\r
398                                         endTransaction();\r
399                                         if (!changes) {\r
400                                             log.log(new Status(IStatus.INFO, Activator.PLUGIN_ID, "Nothing to merge for "+tg.toString()));\r
401                                             continue;\r
402                                         }\r
403 \r
404                                 log.log(new Status(IStatus.INFO, Activator.PLUGIN_ID, "Merging new version of "+tg.toString()));\r
405 \r
406                                         startTransaction(session, true);\r
407                                         \r
408                                         //delta.print();\r
409                                         try {\r
410                                                 \r
411                                                 \r
412                                                 long[] resourceArray = TransferableGraphs.applyDelta(writeGraph(), oldResources, delta);\r
413                                                 tg.setResourceArray(resourceArray);\r
414                                                 mgmt.setGraphBundleEntry(tg);\r
415                                                 commit();\r
416                                                 mergedOntologies = true;\r
417                                         } catch (Throwable t) {\r
418                                                 throw new PlatformException(t);\r
419                                         } finally {\r
420                                                 endTransaction();\r
421                                         }\r
422                                 }\r
423                         }\r
424                         \r
425                         session.syncRequest(new WriteRequest() {\r
426                             @Override\r
427                             public void perform(WriteGraph graph) throws DatabaseException {\r
428                                 graph.setClusterSet4NewResource(graph.getRootLibrary());\r
429                                 graph.flushCluster();\r
430                             }\r
431                         });\r
432 \r
433                         if (mergedOntologies)\r
434                             DatabaseIndexing.deleteAllIndexes();\r
435                     }\r
436 \r
437                     TimeLogger.log("Ontologies synchronized.");\r
438                     \r
439                 }\r
440                 session.getService(XSupport.class).setServiceMode(false, false);\r
441             }\r
442             progressMonitor.worked(20);\r
443         } catch (IOException e) {\r
444             throw new PlatformException(e);\r
445         } catch (DatabaseException e) {\r
446             throw new PlatformException(e);\r
447         }\r
448 \r
449     }\r
450 \r
451     public boolean assertConfiguration(IProgressMonitor progressMonitor, RecoveryPolicy workspacePolicy) throws PlatformException {\r
452 \r
453         if (progressMonitor == null) progressMonitor = new NullProgressMonitor();\r
454 \r
455         File workspaceLocation = Platform.getLocation().toFile();\r
456 \r
457         boolean installProject = false;\r
458         progressMonitor.setTaskName("Asserting simantics.cfg is installed");\r
459         try {\r
460             File propertyFile = new File(workspaceLocation, "simantics.cfg");\r
461             Properties properties;\r
462             try {\r
463                 properties = WorkspaceUtil.readProperties(propertyFile);\r
464             } catch (IOException e) {\r
465                 if (workspacePolicy == RecoveryPolicy.ThrowError) throw new PlatformException("Could not load "+propertyFile);\r
466 \r
467                 // Create a project and write Property file\r
468                 properties = new Properties();\r
469                 properties.setProperty("project_uri", "http://Projects/Development%20Project");\r
470                 properties.setProperty("project_name", "Development Project");\r
471                 WorkspaceUtil.writeProperties(propertyFile, properties);\r
472                 installProject |= true;\r
473             }\r
474             projectURI = properties.getProperty("project_uri");\r
475             projectName = properties.getProperty("project_name");\r
476             progressMonitor.worked(10);\r
477         } catch (IOException e) {\r
478             throw new PlatformException(e);\r
479         }\r
480 \r
481         return installProject;\r
482 \r
483     }\r
484 \r
485     public boolean assertProject(IProgressMonitor progressMonitor, RecoveryPolicy workspacePolicy, boolean installProject) throws PlatformException {\r
486 \r
487         if (progressMonitor == null) progressMonitor = new NullProgressMonitor();\r
488 \r
489         final DatabaseManagement mgmt = new DatabaseManagement();\r
490 \r
491         progressMonitor.setTaskName("Asserting project resource exists in the database");\r
492         try {\r
493             projectResource = session.syncRequest( Queries.resource( projectURI ) );\r
494         } catch (ResourceNotFoundException nfe) {\r
495             // Project was not found\r
496             if (workspacePolicy == RecoveryPolicy.ThrowError)\r
497                 throw new PlatformException("Project Resource "+projectURI+" is not found in the database.");\r
498             // Create empty project with no features\r
499             try {\r
500                 Transaction.startTransaction(session, true);\r
501                 try {\r
502                     // The project needs to be created mutable.\r
503                     session.getService(XSupport.class).setServiceMode(true, false);\r
504 \r
505                     ArrayList<String> empty = new ArrayList<String>();\r
506                     projectResource = mgmt.createProject(projectName, empty);\r
507                     installProject |= true;\r
508 \r
509                     session.getService(XSupport.class).setServiceMode(false, false);\r
510                     Transaction.commit();\r
511                 } finally {\r
512                     Transaction.endTransaction();\r
513                 }\r
514                 //session.getService( LifecycleSupport.class ).save();\r
515             } catch (DatabaseException e) {\r
516                 throw new PlatformException("Failed to create "+projectURI, e);\r
517             }\r
518         } catch (DatabaseException e) {\r
519             throw new PlatformException("Failed to create "+projectURI, e);\r
520         }\r
521         progressMonitor.worked(10);\r
522 \r
523         return installProject;\r
524 \r
525     }\r
526 \r
527     public void updateInstalledGroups(IProgressMonitor progressMonitor, boolean installProject) throws PlatformException {\r
528 \r
529         if (installProject)\r
530         {\r
531             // Attach all feature groups available in platform to created project\r
532             progressMonitor.setTaskName("Install all features");\r
533             Set<GroupReference> publishedFeatureGroups = ProjectFeatures.getInstallGroupsOfPublishedFeatures();\r
534             Collection<GroupReference> groupsWithoutVersion = GroupReference.stripVersions(publishedFeatureGroups);\r
535 \r
536     //        final List<String> Platform_Features = new ArrayList<String>();\r
537     //\r
538     //        // Convert graph instances\r
539     //        Collection<TransferableGraph1> platformGraphs = new ArrayList<TransferableGraph1>();\r
540     //        for (GraphBundleEx e : platformTGs.values()) platformGraphs.add( e.getGraph() );\r
541     //        IGraph graph = Graphs.createGraph(platformGraphs);\r
542     //\r
543     //        Res    PublishedProjectFeatures = UriUtils.uriToPath( ProjectResource.URIs.PublishedProjectFeatures );\r
544     //        Path   HasFeature = UriUtils.uriToPath( ProjectResource.URIs.HasFeature );\r
545     //        for(Res feature : graph.getObjects(PublishedProjectFeatures, HasFeature)) {\r
546     //            System.out.println("Installing Project Feature: "+feature.toString());\r
547     //            Platform_Features.add( feature.toString() );\r
548     //        }\r
549 \r
550             try {\r
551                 Transaction.startTransaction(session, true);\r
552                 try {\r
553     //                for (String feature : Platform_Features) {\r
554     //                    try {\r
555     //                        getResource(feature);\r
556     //                    } catch(ResourceNotFoundException e) {\r
557     //                        System.out.println(feature+" not found");\r
558     //                    }\r
559     //                    mgmt.installFeature(projectResource, feature);\r
560     //                }\r
561                     Projects.setProjectInstalledGroups(writeGraph(), projectResource, groupsWithoutVersion);\r
562                     Transaction.commit();\r
563                 } finally {\r
564                     Transaction.endTransaction();\r
565                 }\r
566                 //session.getService( LifecycleSupport.class ).save();\r
567             } catch(DatabaseException ae) {\r
568                 throw new PlatformException("Failed to install features", ae);\r
569             }\r
570             progressMonitor.worked(10);\r
571         }\r
572 \r
573     }\r
574 \r
575     public void assertSessionModel(IProgressMonitor progressMonitor) throws PlatformException {\r
576 \r
577         Properties properties = session.getService(Properties.class);\r
578         final String clientId = properties.getProperty("clientId");\r
579 \r
580         try {\r
581 \r
582             // Currently this needs to be done before data becomes available\r
583             VirtualGraphSupport support = session.getService(VirtualGraphSupport.class);\r
584             VirtualGraph activations = support.getWorkspacePersistent("activations");\r
585 \r
586             Resource sessionModel = session.syncRequest(new Read<Resource>() {\r
587 \r
588                 @Override\r
589                 public Resource perform(ReadGraph graph) throws DatabaseException {\r
590 \r
591                     Layer0X L0X = Layer0X.getInstance(graph);\r
592                     for(Resource sessionModel : graph.syncRequest(new ObjectsWithType(graph.getRootLibrary(), L0X.HasSession, L0X.Session))) {\r
593                         String id = graph.getPossibleRelatedValue(sessionModel, L0X.Session_HasClientId);\r
594                         if(id != null && id.equals(clientId)) return sessionModel;\r
595                     }\r
596                     return null;\r
597 \r
598                 }\r
599 \r
600             });\r
601 \r
602             if(sessionModel == null) {\r
603 \r
604                 sessionModel = session.syncRequest(new WriteResultRequest<Resource>(activations) {\r
605 \r
606                     @Override\r
607                     public Resource perform(WriteGraph graph) throws DatabaseException {\r
608                         Layer0 L0 = Layer0.getInstance(graph);\r
609                         Layer0X L0X = Layer0X.getInstance(graph);\r
610                         Resource session = graph.newResource();\r
611                         graph.claim(session, L0.InstanceOf, null, L0X.Session);\r
612                         graph.claim(session, L0X.Session_HasUser, null, graph.getResource("http://Users/AdminUser"));\r
613                         graph.addLiteral(session, L0X.Session_HasClientId, L0X.Session_HasClientId_Inverse, clientId, Bindings.STRING);\r
614                         graph.claim(graph.getRootLibrary(), L0X.HasSession, session);\r
615                         return session;\r
616                     }\r
617                 });\r
618 \r
619             }\r
620 \r
621             session.registerService(SessionModel.class, new PlatformSessionModel(sessionModel));\r
622         } catch (DatabaseException e) {\r
623             throw new PlatformException(e);\r
624         }\r
625 \r
626     }\r
627 \r
628     static class PlatformSessionModel implements SessionModel {\r
629         private final Resource sessionModel;\r
630 \r
631         public PlatformSessionModel(Resource model) {\r
632             this.sessionModel = model;\r
633         }\r
634 \r
635         @Override\r
636         public Resource getResource() {\r
637             return sessionModel;\r
638         }\r
639     }\r
640 \r
641     public void resetDatabase(IProgressMonitor monitor) throws PlatformException {\r
642         File dbLocation = Platform.getLocation().append("db").toFile();\r
643         if(!dbLocation.exists()) return;\r
644         try { // Load driver\r
645             Driver driver = Manager.getDriver("procore");\r
646             Management management = driver.getManagement(dbLocation.getAbsolutePath(), null);\r
647             management.delete();\r
648         } catch (DatabaseException e) {\r
649             throw new PlatformException("Failed to remove database at " + dbLocation.getAbsolutePath(), e);\r
650         }\r
651         // We have created extra files to database folder which have to be deleted also.\r
652         // This is an awful idea! Do not create extra files to database folder!\r
653         Throwable t = null;\r
654         for (int i=0; i<10; ++i) {\r
655             try {\r
656                 FileUtils.deleteAll(dbLocation);\r
657                 t = null;\r
658                 break;\r
659             } catch (IOException e) {\r
660                 // Assuming this has been thrown because delete file/folder failed.\r
661                 t = e;\r
662             }\r
663             try {\r
664                 Thread.sleep(200);\r
665             } catch (InterruptedException e) {\r
666                 // Ignoring interrupted exception.\r
667             }\r
668         }\r
669         if (null != t)\r
670             throw new PlatformException("Failed to remove database folder at " + dbLocation.getAbsolutePath(), t);\r
671     }\r
672     public void resetWorkspace(IProgressMonitor monitor, ArrayList<String> fileFilter) throws PlatformException, IllegalStateException, IOException {\r
673         File file = Platform.getLocation().toFile();\r
674         if (null != fileFilter)\r
675             FileUtils.deleteAllWithFilter(file , fileFilter);\r
676         resetDatabase(monitor);\r
677     }\r
678 \r
679     /**\r
680      * Start-up the platform. The procedure consists of 8 steps. Once everything\r
681      * is up and running, all fields are set property.\r
682      * <p>\r
683      *\r
684      * If workspacePolicy is FixErrors, there is an attempt to fix unexpected\r
685      * errors. It includes installing database files, installing ontologies, and\r
686      * installing project features.\r
687      * <p>\r
688      *\r
689      * In SWB this is handled in SimanticsWorkbenchAdvisor#openWindows().\r
690      * <p>\r
691      *\r
692      * If remote server is given, simantics plaform takes connection there\r
693      * instead of local server at "db/".\r
694      *\r
695      * @param workspacePolicy action to take on workspace/database related\r
696      *        errors\r
697      * @param ontologyPolicy action to take on ontology mismatch\r
698      * @param progressMonitor optional progress monitor\r
699      * @param userAgent interface for resorting to user feedback during platform\r
700      *        startup or <code>null</code> to resort to default measures\r
701      * @throws PlatformException\r
702      */\r
703     public SessionContext startUp(String databaseDriverId, IProgressMonitor progressMonitor, RecoveryPolicy workspacePolicy,\r
704             OntologyRecoveryPolicy ontologyPolicy, boolean requireSynchronize, PlatformUserAgent userAgent)\r
705     throws PlatformException\r
706     {\r
707 \r
708         assert(!running);\r
709         TimeLogger.log("Beginning of SimanticsPlatform.startUp");\r
710 \r
711         if (progressMonitor == null) progressMonitor = new NullProgressMonitor();\r
712 \r
713         // For debugging on what kind of platform automatic tests are running in\r
714         // case there are problems.\r
715         if ("true".equals(System.getProperty("org.simantics.dumpBundleState")))\r
716             dumpPlatformBundleState();\r
717 \r
718         // 0. Consult all startup extensions before doing anything with the workspace.\r
719         StartupExtensions.consultStartupExtensions();\r
720         TimeLogger.log("Consulted platform pre-startup extensions");\r
721 \r
722         // 0.1. Clear all temporary files\r
723         Simantics.clearTemporaryDirectory();\r
724         TimeLogger.log("Cleared temporary directory");\r
725 \r
726         // 0.2 Clear VariableRepository.repository static map which holds references to SessionImplDb\r
727         VariableRepository.clear();\r
728         \r
729         // 1. Assert there is a database at <workspace>/db\r
730         session = setupDatabase(databaseDriverId, progressMonitor, workspacePolicy, userAgent);\r
731         TimeLogger.log("Database setup complete");\r
732 \r
733         // 2. Assert all graphs, and correct versions, are installed to the database\r
734         synchronizeOntologies(progressMonitor, ontologyPolicy, requireSynchronize);\r
735         TimeLogger.log("Synchronized ontologies");\r
736 \r
737         // 4. Assert simantics.cfg exists\r
738         boolean installProject = assertConfiguration(progressMonitor,workspacePolicy);\r
739 \r
740         // 5. Assert Project Resource is installed in the database\r
741         installProject = assertProject(progressMonitor, workspacePolicy, installProject);\r
742 \r
743         // 6. Install all features into project, if in debug mode\r
744         updateInstalledGroups(progressMonitor, installProject);\r
745         TimeLogger.log("Installed all features into project");\r
746 \r
747         // 7. Assert L0.Session in database for this session\r
748         assertSessionModel(progressMonitor);\r
749 \r
750         session.getService(XSupport.class).setServiceMode(false, false);\r
751 \r
752         try {\r
753             session.sync(new WriteRequest() {\r
754 \r
755                 @Override\r
756                 public void perform(WriteGraph graph) throws DatabaseException {\r
757                     QueryControl qc = graph.getService(QueryControl.class);\r
758                     qc.flush(graph);\r
759                 }\r
760 \r
761             });\r
762             TimeLogger.log("Flushed queries");\r
763         } catch (DatabaseException e) {\r
764             Logger.defaultLogError(e);\r
765         }\r
766         boolean loadProject = true;\r
767         try {\r
768 \r
769                 sessionContext = SimanticsPlatform.INSTANCE.createSessionContext(true);\r
770                 // This must be before setSessionContext since some listeners might query this\r
771             sessionContext.setHint(SimanticsKeys.KEY_PROJECT, SimanticsPlatform.INSTANCE.projectResource);\r
772 \r
773             Simantics.setSessionContext(sessionContext);\r
774 \r
775             // 1. Put ResourceBinding that throws an exception to General Bindings\r
776             simanticsBindings = new SimanticsBindings( null );\r
777             Bindings.classBindingFactory.addFactory( simanticsBindings );\r
778 \r
779 \r
780             // 2. Create session-specific second Binding context (Databoard) and\r
781             //    put that to Session as a service\r
782             Session session = sessionContext.getSession();\r
783             Databoard sessionDataboard = new Databoard();\r
784             session.registerService(Databoard.class, sessionDataboard);\r
785             simanticsBindings2 = new SimanticsBindings( session );\r
786             sessionDataboard.classBindingFactory.addFactory( simanticsBindings2 );\r
787 \r
788             // Register datatype bindings\r
789             Bindings.defaultBindingFactory.getRepository().put(RGB.Integer.BINDING.type(), RGB.Integer.BINDING);\r
790             Bindings.defaultBindingFactory.getRepository().put(Font.BINDING.type(), Font.BINDING);\r
791 \r
792             if(loadProject) {\r
793 \r
794                 TimeLogger.log("Load projects");\r
795                 project = Projects.loadProject(sessionContext.getSession(), SimanticsPlatform.INSTANCE.projectResource);\r
796 \r
797                 sessionContext.setHint(ProjectKeys.KEY_PROJECT, project);\r
798                 TimeLogger.log("Loading projects complete");\r
799 \r
800                 project.activate();\r
801                 TimeLogger.log("Project activated");\r
802             }\r
803 \r
804         } catch (DatabaseException e) {\r
805             Logger.defaultLogError(e);\r
806             throw new PlatformException(e);\r
807         } catch (ProjectException e) {\r
808             boolean hasStackTrace = e.getStackTrace().length > 0;\r
809             if (!hasStackTrace)\r
810                 throw new PlatformException(e.getMessage(), hasStackTrace);\r
811             throw new PlatformException(e, hasStackTrace);\r
812         }\r
813 \r
814         running = true;\r
815 \r
816         return sessionContext;\r
817 \r
818     }\r
819 \r
820     public SessionContext createSessionContext(boolean init) throws PlatformException {\r
821         try {\r
822             // Construct and initialize SessionContext from Session.\r
823             SessionContext sessionContext = SessionContext.create(session, init);\r
824             if (init)\r
825                 sessionContext.registerServices();\r
826             return sessionContext;\r
827         } catch (DatabaseException e) {\r
828             throw new PlatformException(e);\r
829         }\r
830     }\r
831 \r
832 //    private static File getIgnorePrerequisitesFile(URL workspaceUrl) {\r
833 //        if (workspaceUrl == null)\r
834 //            return null;\r
835 //        return new File(workspaceUrl.getPath(), ".ignorePrerequisites");\r
836 //    }\r
837 //\r
838 //    private void ensurePrerequisites(IProgressMonitor progressMonitor, PlatformUserAgent userAgent) throws PlatformException {\r
839 //        Location loc = Platform.getInstanceLocation();\r
840 //        File ignorePrerequisites = getIgnorePrerequisitesFile(loc.getURL());\r
841 //        if (loc.isSet() && ignorePrerequisites != null) {\r
842 //            if (ignorePrerequisites.exists() || ignorePrerequisites.isFile())\r
843 //                return;\r
844 //        }\r
845 //\r
846 //        try {\r
847 //            ServerEnvironment.ensureServerDependenciesMet();\r
848 //        } catch (ExecutionEnvironmentException e) {\r
849 //            // Not installed properly, ask user whether to try installation.\r
850 //            try {\r
851 //                StringBuilder msg = new StringBuilder();\r
852 //                msg.append("Your system seems to be missing the following prerequisites for running this application:\n\n");\r
853 //                for (Product product : e.requiredProducts)\r
854 //                    msg.append("\t" + product.getDescription() + "\n");\r
855 //                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
856 //                msg.append("\n\nSelecting Cancel will close the application.");\r
857 //\r
858 //                int selection = 0;\r
859 //                if (userAgent != null) {\r
860 //                    selection = userAgent.showPrompt("Missing Prerequisites", msg.toString(), new String[] {\r
861 //                        "Install Pre-requisites",\r
862 //                        "Ignore Now",\r
863 //                        "Ignore Always",\r
864 //                        "Cancel"\r
865 //                    }, selection);\r
866 //                }\r
867 //                boolean tryInstall = false;\r
868 //                switch (selection) {\r
869 //                    case 0:\r
870 //                        tryInstall = true;\r
871 //                        break;\r
872 //                    case 2:\r
873 //                        ignorePrerequisites.createNewFile();\r
874 //                    case 1:\r
875 //                        break;\r
876 //                    case 3:\r
877 //                    case -1:\r
878 //                        throw new CancelStartupException();\r
879 //                }\r
880 //\r
881 //                if (tryInstall) {\r
882 //                    // Try to install it and check for success afterwards.\r
883 //                    ServerEnvironment.tryInstallDependencies(progressMonitor);\r
884 //                    ServerEnvironment.ensureServerDependenciesMet();\r
885 //                }\r
886 //            } catch (InstallException ie) {\r
887 //                throw new PlatformException(ie);\r
888 //            } catch (ExecutionEnvironmentException eee) {\r
889 //                throw new PlatformException(eee);\r
890 //            } catch (IOException ie) {\r
891 //                throw new PlatformException(ie);\r
892 //            }\r
893 //        }\r
894 //    }\r
895 \r
896     /**\r
897      * Shutdown Simantics Platform.\r
898      *\r
899      * In SWB this is handled in SimanticsWorkbenchAdvisor#disconnectFromWorkspace.\r
900      *\r
901      * @param progressMonitor optional progress monitor\r
902      * @throws PlatformException\r
903      */\r
904     public void shutdown(IProgressMonitor progressMonitor) throws PlatformException\r
905     {\r
906         SubMonitor progress = SubMonitor.convert(progressMonitor, 100);\r
907         PlatformException platformException = null;\r
908         try {\r
909             progress.subTask("Close Project");\r
910             if (project != null) {\r
911                 project.safeDispose();\r
912             }\r
913             progress.worked(10);\r
914 \r
915             running = false;\r
916             progress.subTask("Close Database Session");\r
917             Databoard databoard = null;\r
918             if (sessionContext != null) {\r
919                 Session s = sessionContext.peekSession();\r
920                 if (s != null) {\r
921                     databoard = s.peekService(Databoard.class);\r
922 \r
923                     progress.subTask("Flushing Index Caches");\r
924                     try {\r
925                         Simantics.flushIndexCaches(progress.newChild(20), s);\r
926                     } catch (Throwable t) {\r
927                         Logger.defaultLogError(t);\r
928                     }\r
929                 }\r
930 \r
931                 progress.subTask("Close Database Session");\r
932                 sessionContext.safeDispose();\r
933                 sessionContext = null;\r
934                 Simantics.setSessionContext(null);\r
935             }\r
936             if (simanticsBindings != null) {\r
937                 Bindings.classBindingFactory.removeFactory( simanticsBindings );\r
938                 simanticsBindings = null;\r
939             }\r
940             if (databoard != null) {\r
941                 if (simanticsBindings2 != null) {\r
942                         databoard.classBindingFactory.removeFactory( simanticsBindings2 );\r
943                         simanticsBindings2 = null;\r
944                 }\r
945                 databoard.clear();\r
946             }\r
947 \r
948             // Make sure Simantics clipboard doesn't store unwanted session data references.\r
949             Simantics.setClipboard(new SimanticsClipboardImpl());\r
950 \r
951             progress.worked(30);\r
952 \r
953             session = null;\r
954             projectResource = null;\r
955 \r
956             DependenciesRelation.assertFinishedTracking();\r
957 \r
958         } catch (Exception e) {\r
959             platformException = new PlatformException("Failed to shutdown Simantics Platform", e);\r
960         }\r
961 \r
962         progress.worked(10);\r
963         progress.subTask("Shutting down database");\r
964         try {\r
965             if (null != databasebManagement)\r
966                 databasebManagement.shutdown();\r
967         } catch (Throwable t) {\r
968             Logger.defaultLogError(t);\r
969         }\r
970         progress.worked(10);\r
971 \r
972         progress.subTask("Clearing Workspace Temporary Directory");\r
973         try {\r
974             Simantics.clearTemporaryDirectory();\r
975         } catch (Throwable t) {\r
976             Logger.defaultLogError(t);\r
977         }\r
978         progress.worked(10);\r
979         if (null != platformException)\r
980             throw platformException;\r
981     }\r
982 \r
983     // TODO: consider removing this in the future ??\r
984     @Override\r
985     public void stateChanged(LifecycleState newState) {\r
986         if(newState == LifecycleState.CLOSED) {\r
987             if(running) {\r
988                 if(Platform.isRunning()) {\r
989                     mainThread.interrupt();\r
990                 }\r
991             }\r
992         }\r
993     }\r
994 \r
995     /**\r
996      * @return <code>true</code> if discard was successful, <code>false</code>\r
997      *         if there was no session, {@link UndoRedoSupport} or\r
998      *         {@link UndoContext} to discard through\r
999      */\r
1000     public boolean discardSessionUndoHistory() {\r
1001         Session s = session;\r
1002         if (s != null) {\r
1003             UndoRedoSupport urs = s.peekService(UndoRedoSupport.class);\r
1004             if (urs != null) {\r
1005                 UndoContext uc = urs.getUndoContext(s);\r
1006                 if (uc != null) {\r
1007                     uc.clear();\r
1008                     return true;\r
1009                 }\r
1010             }\r
1011         }\r
1012         return false;\r
1013     }\r
1014 \r
1015     public void reconnect(String databaseDriverId) throws Exception {\r
1016         // Starts database server.\r
1017         SimanticsPlatform.INSTANCE.startUp(databaseDriverId, null, RecoveryPolicy.ThrowError, OntologyRecoveryPolicy.ThrowError, true, null);\r
1018     }\r
1019 \r
1020     private void dumpPlatformBundleState() {\r
1021         BundleDescription[] bs = Platform.getPlatformAdmin().getState().getBundles();\r
1022         System.out.println("Total bundles: " + bs.length);\r
1023         for (BundleDescription b : bs) {\r
1024             System.out.format("%-80s @ %s\n", b.toString(), b.getLocation());\r
1025         }\r
1026     }\r
1027 \r
1028 }\r
1029 \r