--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.project.management;\r
+\r
+import static org.simantics.db.common.utils.Transaction.writeGraph;\r
+\r
+import java.util.Collection;\r
+import java.util.HashSet;\r
+import java.util.Set;\r
+import java.util.regex.Matcher;\r
+\r
+import org.simantics.databoard.Bindings;\r
+import org.simantics.databoard.binding.Binding;\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.WriteGraph;\r
+import org.simantics.db.WriteOnlyGraph;\r
+import org.simantics.db.common.utils.Transaction;\r
+import org.simantics.db.exception.AssumptionException;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.db.layer0.util.Layer0Utils;\r
+import org.simantics.db.request.Read;\r
+import org.simantics.graph.db.IImportAdvisor;\r
+import org.simantics.graph.db.ImportAdvisor;\r
+import org.simantics.graph.db.TransferableGraphException;\r
+import org.simantics.graph.db.TransferableGraphs;\r
+import org.simantics.graph.diff.Diff;\r
+import org.simantics.graph.diff.TransferableGraphDelta1;\r
+import org.simantics.graph.representation.TransferableGraph1;\r
+import org.simantics.layer0.DatabaseManagementResource;\r
+import org.simantics.layer0.Layer0;\r
+import org.simantics.project.ontology.ProjectResource;\r
+\r
+/**\r
+ * Database Management is a utility class for managing a database.\r
+ * The following management operations are supported:\r
+ * \r
+ * o Install Builtins\r
+ * o Install Layer0\r
+ * o Install & Update GraphBundles\r
+ * o Manage Projects (Install/Uninstall/Discover)\r
+ * o Manage Features (Install/Uninstall/Discover)\r
+ * o Manage GraphBundles\r
+ * \r
+ * This utility is based on Transaction class. The active graph must be\r
+ * set for the current thread.\r
+ *\r
+ * @author Toni Kalajainen <toni.kalajainen@vtt.fi>\r
+ */\r
+public class DatabaseManagement {\r
+\r
+ public static final String PROJECTS_URI = "http://Projects"; \r
+ Binding tg_binding = Bindings.getBindingUnchecked( TransferableGraph1.class );\r
+\r
+ public DatabaseManagement() {\r
+ }\r
+\r
+ /////////////////////////////////////////////////////////////////////////// \r
+ /////////////// Project Managemenet ////////////////////// \r
+ /////////////////////////////////////////////////////////////////////////// \r
+ /**\r
+ * Create a new project. A new resource is created and linked to Projects \r
+ * library. \r
+ * \r
+ * @param g\r
+ * @param name\r
+ * @param features a list of features\r
+ * @return resource to the project\r
+ * @throws DatabaseException\r
+ */\r
+ public Resource createProject(String name, Collection<String> features) throws DatabaseException {\r
+ \r
+ WriteGraph g = Transaction.writeGraph();\r
+ g.setClusterSet4NewResource(g.getRootLibrary());\r
+\r
+ Resource root = g.getResource(PROJECTS_URI);\r
+ Layer0 L0 = Layer0.getInstance(g);\r
+ ProjectResource PROJ = ProjectResource.getInstance(g);\r
+\r
+ Resource project = g.newResource();\r
+ g.claim(project, L0.InstanceOf, null, PROJ.Project);\r
+ g.claim(project, L0.PartOf, root);\r
+ g.claimLiteral(project, L0.HasName, name);\r
+ \r
+ // Create library for temporary resources\r
+ if(Layer0Utils.getPossibleChild(g, root, "Temp") == null) {\r
+ Resource tempLibrary = g.newResource();\r
+ g.claim(tempLibrary, L0.InstanceOf, null, L0.Library);\r
+ g.claimLiteral(tempLibrary, L0.HasName, "Temp");\r
+ g.claim(root, L0.ConsistsOf, tempLibrary);\r
+ }\r
+\r
+ // Create library for trash\r
+ if(Layer0Utils.getPossibleChild(g, root, "TrashBin") == null) {\r
+ Resource trashLibrary = g.newResource();\r
+ g.claim(trashLibrary, L0.InstanceOf, null, L0.Library);\r
+ g.claimLiteral(trashLibrary, L0.HasName, "TrashBin");\r
+ g.claim(root, L0.ConsistsOf, trashLibrary);\r
+ }\r
+\r
+ // Create library for document sessions\r
+ if(Layer0Utils.getPossibleChild(g, root, "DocumentSessions") == null) {\r
+ Resource documentSessions = g.newResource();\r
+ g.claim(documentSessions, L0.InstanceOf, null, L0.Library);\r
+ g.claimLiteral(documentSessions, L0.HasName, "DocumentSessions");\r
+ g.claim(root, L0.ConsistsOf, documentSessions);\r
+ }\r
+\r
+ // Link features\r
+ for (String feature_uri : features) {\r
+ Resource r = g.getResource(feature_uri);\r
+ g.claim(project, PROJ.HasFeature, r);\r
+ }\r
+ \r
+ return project;\r
+ }\r
+ \r
+ \r
+ /**\r
+ * Delete project. Project resource is unlinked it from the Projects library.\r
+ * The rest is left for garbage collection.\r
+ * \r
+ * @param g\r
+ * @param projectResource\r
+ * @throws DatabaseException\r
+ */\r
+ public void deleteProject(Resource projectResource) throws DatabaseException {\r
+ WriteGraph g = Transaction.writeGraph();\r
+ Resource root = g.getResource(PROJECTS_URI);\r
+ Layer0 l0 = Layer0.getInstance(g);\r
+ g.denyStatement(projectResource, l0.PartOf, root);\r
+ }\r
+ \r
+ \r
+ /**\r
+ * A query that reads all project URIs \r
+ * \r
+ * @param g graph\r
+ * @return a query for graphs\r
+ * @throws DatabaseException\r
+ */\r
+ public final Read<Set<String>> ProjectURIQuery =\r
+ new Read<Set<String>>() {\r
+ @Override\r
+ public Set<String> perform(ReadGraph g) throws DatabaseException {\r
+ Layer0 b = Layer0.getInstance(g);\r
+ Resource root = g.getResource(PROJECTS_URI);\r
+ Set<String> result = new HashSet<String>();\r
+ for (Resource r : g.getObjects(root, b.ConsistsOf) )\r
+ result.add( g.getURI(r) );\r
+ return result;\r
+ }\r
+ }; \r
+ \r
+ /**\r
+ * A query that reads all project resources \r
+ * \r
+ * @param g graph\r
+ * @return a query for graphs\r
+ * @throws DatabaseException\r
+ */\r
+ public final Read<Set<Resource>> ProjectsQuery =\r
+ new Read<Set<Resource>>() {\r
+ @Override\r
+ public Set<Resource> perform(ReadGraph g) throws DatabaseException {\r
+ Layer0 b = Layer0.getInstance(g);\r
+ Resource root = g.getResource(PROJECTS_URI);\r
+ return new HashSet<Resource>( g.getObjects(root, b.ConsistsOf) );\r
+ }\r
+ };\r
+ \r
+ \r
+ /**\r
+ * Get a list of all projects in the database. \r
+ * \r
+ * @param g\r
+ * @return\r
+ * @throws DatabaseException\r
+ */\r
+ public Collection<Resource> getProjects() throws DatabaseException {\r
+ ReadGraph g = Transaction.readGraph();\r
+ Layer0 b = Layer0.getInstance(g);\r
+ Resource root = g.getResource(PROJECTS_URI);\r
+ return g.getObjects(root, b.ConsistsOf);\r
+ }\r
+\r
+ /////////////////////////////////////////////////////////////////////////// \r
+ /////////////// Feature Management ////////////////////// \r
+ /////////////////////////////////////////////////////////////////////////// \r
+ \r
+// /**\r
+// * Get all features in the database. \r
+// * \r
+// * @param features a collection to be filled with features\r
+// */\r
+// public void getFeatures(Collection<FeatureInfo> features) throws DatabaseException \r
+// {\r
+// ReadGraph g = Transaction.readGraph();\r
+// ProjectResource PROJ = ProjectResource.getInstance(g);\r
+// Layer0 L0 = Layer0.getInstance(g);\r
+//\r
+// for (Resource r : g.getObjects(PROJ.PublishedProjectFeatures, L0.ConsistsOf)) {\r
+// String URI = g.getURI(r);\r
+// String name = g.getRelatedValue(r, L0.HasLabel);\r
+// String vid_ = g.getRelatedValue(r, L0.HasName);\r
+// VersionedId vid = (VersionedId) VersionedId.parse(vid_);\r
+// FeatureInfo fi = new FeatureInfo(name, URI, vid); \r
+// features.add( fi ); \r
+// }\r
+// \r
+// }\r
+// \r
+// /**\r
+// * Get all features installed to a project\r
+// * \r
+// * @param g graph\r
+// * @param features a list of bundles\r
+// * @throws DatabaseException\r
+// */\r
+// public void getProjectFeatures(Resource project, Collection<FeatureInfo> features) throws DatabaseException {\r
+// ReadGraph g = Transaction.readGraph();\r
+// ProjectResource PROJ = ProjectResource.getInstance(g);\r
+// Layer0 L0 = Layer0.getInstance(g);\r
+// \r
+// for (Resource r : g.getObjects(project, PROJ.HasFeature)) {\r
+// String URI = g.getURI(r);\r
+// String name = g.getRelatedValue(r, L0.HasLabel);\r
+// String vid_ = g.getRelatedValue(r, L0.HasName);\r
+// VersionedId vid = (VersionedId) VersionedId.parse(vid_);\r
+// FeatureInfo fi = new FeatureInfo(name, URI, vid); \r
+// features.add( fi ); \r
+// }\r
+// }\r
+// \r
+// /**\r
+// * Configure project to use a feature.\r
+// * \r
+// * @param g\r
+// * @param project\r
+// * @param featureUri feature URI\r
+// * @throws DatabaseException\r
+// */\r
+// public void installFeature(Resource project, String featureUri)\r
+// throws DatabaseException\r
+// {\r
+// WriteGraph g = Transaction.writeGraph();\r
+// Resource feature = g.getResource(featureUri);\r
+// ProjectResource PROJ = ProjectResource.getInstance(g);\r
+// g.claim(project, PROJ.HasFeature, feature);\r
+// }\r
+// \r
+// /**\r
+// * Configure project not to use a feature.\r
+// * \r
+// * @param g\r
+// * @param project\r
+// * @param featureuri feature URI\r
+// * @throws DatabaseException\r
+// */\r
+// public void uninstallFeature(Resource project, String featureUri) \r
+// throws DatabaseException\r
+// {\r
+// WriteGraph g = Transaction.writeGraph();\r
+// Resource feature = g.getResource(featureUri);\r
+// ProjectResource PROJ = ProjectResource.getInstance(g);\r
+// g.denyStatement(project, PROJ.HasFeature, feature);\r
+// }\r
+ \r
+ \r
+ /////////////////////////////////////////////////////////////////////////// \r
+ /////////////// Transferable Graph Management ////////////////////// \r
+ /////////////////////////////////////////////////////////////////////////// \r
+ \r
+ /**\r
+ * Install transferable graph into database and manage install info.\r
+ * If different but exact same version is already installed, the new \r
+ * graph is merged.<p>\r
+ * \r
+ * Resource array field of tg argument is updated.\r
+ * \r
+ * @param tg transferable graph\r
+ */\r
+ public void installGraphBundle(GraphBundle tg)\r
+ throws DatabaseException, TransferableGraphException \r
+ {\r
+ Resource oldResource = getGraphBundleResource(tg.getId(), tg.getMajor());\r
+ // Install New\r
+ if (oldResource == null) { \r
+ IImportAdvisor advisor = new ImportAdvisor();\r
+ long[] resourceArray = TransferableGraphs.importGraph(writeGraph(), tg.getGraph(), advisor);\r
+ tg.setResourceArray(resourceArray);\r
+ setGraphBundleEntry(tg);\r
+ } else\r
+ // Merge with old\r
+ {\r
+ // Merge &\r
+ GraphBundle oldTG = getGraphBundle(oldResource);\r
+ TransferableGraphDelta1 delta = new Diff(oldTG.getGraph(), tg.getGraph()).diff();\r
+ long[] oldResourceArray = oldTG.getResourceArray();\r
+ long[] newResourceArray = TransferableGraphs.applyDelta(writeGraph(), oldResourceArray, delta);\r
+ tg.setResourceArray(newResourceArray);\r
+ // Manage\r
+ setGraphBundleEntry(tg); \r
+ }\r
+ }\r
+\r
+ /**\r
+ * A query that reads all graphs in the database \r
+ * \r
+ * @param g graph\r
+ * @return a query for graphs\r
+ * @throws DatabaseException\r
+ */\r
+ public final Read<Set<GraphBundleRef>> GraphBundleRefQuery =\r
+ new Read<Set<GraphBundleRef>>() {\r
+ @Override\r
+ public Set<GraphBundleRef> perform(ReadGraph g) throws DatabaseException {\r
+ Object oldGraph = Transaction.setGraph(g);\r
+ try {\r
+ return getGraphBundleReferences();\r
+ } finally {\r
+ Transaction.setGraph(oldGraph);\r
+ }\r
+ }\r
+ }; \r
+\r
+ \r
+ /**\r
+ * A query that reads all graphs in the database \r
+ * \r
+ * @param g graph\r
+ * @return a query for graphs\r
+ * @throws DatabaseException\r
+ */\r
+ public final Read<Set<GraphBundle>> GraphBundleQuery =\r
+ new Read<Set<GraphBundle>>() {\r
+ @Override\r
+ public Set<GraphBundle> perform(ReadGraph g) throws DatabaseException {\r
+ Object oldGraph = Transaction.setGraph(g);\r
+ try {\r
+ return getGraphBundles();\r
+ } finally {\r
+ Transaction.setGraph(oldGraph);\r
+ }\r
+ }\r
+ }; \r
+\r
+ public Set<GraphBundle> getGraphBundles() \r
+ throws DatabaseException {\r
+ ReadGraph g = Transaction.readGraph();\r
+ Set<GraphBundle> result = new HashSet<GraphBundle>();\r
+\r
+ DatabaseManagementResource DatabaseManagement = DatabaseManagementResource.getInstance(g);\r
+ Layer0 L0 = Layer0.getInstance(g); \r
+ \r
+ for (Resource tg : g.getObjects(DatabaseManagement.InstalledGraphBundles, L0.ConsistsOf)) {\r
+ if ( !g.isInstanceOf(tg, DatabaseManagement.GraphBundle) ) continue; \r
+ String name = g.getPossibleRelatedValue(tg, L0.HasName);\r
+ String vid = g.getPossibleRelatedValue(tg, DatabaseManagement.HasVersionedId);\r
+ Integer hash = g.getPossibleRelatedValue(tg, DatabaseManagement.HasHashCode);\r
+ //System.out.println("Found in Database: " + vid);\r
+ //TransferableGraph1 data = g.getRelatedValue(tg, DatabaseManagement.HasFile, tg_binding);\r
+ GraphBundle entry = new GraphBundle(name, null, vid);\r
+ entry.resource = tg;\r
+ entry.hashcode = hash;\r
+ long[] resourceArray = g.getPossibleRelatedValue(tg, DatabaseManagement.HasInstallInfo, Bindings.LONG_ARRAY);\r
+ if (resourceArray!=null) entry.setResourceArray(resourceArray);\r
+ result.add(entry);\r
+ } \r
+ return result; \r
+ }\r
+\r
+ public Set<GraphBundleRef> getGraphBundleReferences() \r
+ throws DatabaseException {\r
+ ReadGraph g = Transaction.readGraph();\r
+ Set<GraphBundleRef> result = new HashSet<GraphBundleRef>();\r
+\r
+ DatabaseManagementResource DatabaseManagement = DatabaseManagementResource.getInstance(g);\r
+ Layer0 L0 = Layer0.getInstance(g); \r
+ \r
+ for (Resource tg : g.getObjects(DatabaseManagement.InstalledGraphBundles, L0.ConsistsOf)) {\r
+ if ( !g.isInstanceOf(tg, DatabaseManagement.GraphBundle) ) continue;\r
+ String vid = g.getPossibleRelatedValue(tg, DatabaseManagement.HasVersionedId);\r
+ result.add( GraphBundleRef.of( vid ) );\r
+ } \r
+ return result; \r
+ }\r
+ \r
+ \r
+ /**\r
+ * Get TransferableGraph resource that is attached to InstalledTransferableGraphs. \r
+ * \r
+ * @param id id <symbolic_name>_<version>\r
+ * @return TG resource or <tt>null</tt>\r
+ * @throws DatabaseException\r
+ */\r
+ public GraphBundle getGraphBundle(Resource r) throws DatabaseException {\r
+ ReadGraph g = Transaction.readGraph();\r
+ Layer0 L0 = Layer0.getInstance(g);\r
+ DatabaseManagementResource DatabaseManagement = DatabaseManagementResource.getInstance(g);\r
+ \r
+ String name = g.getPossibleRelatedValue(r, L0.HasName);\r
+ String vid = g.getPossibleRelatedValue(r, DatabaseManagement.HasVersionedId);\r
+ TransferableGraph1 data = g.getRelatedValue(r, DatabaseManagement.HasFile, tg_binding);\r
+ GraphBundle entry = new GraphBundle(name, data, vid);\r
+ long[] resourceArray = g.getPossibleRelatedValue(r, DatabaseManagement.HasInstallInfo, Bindings.LONG_ARRAY);\r
+ if (resourceArray!=null) entry.setResourceArray(resourceArray); \r
+ return entry;\r
+ } \r
+ \r
+ /**\r
+ * Get TransferableGraph resource that is attached to InstalledTransferableGraphs. \r
+ * \r
+ * @param id id <symbolic_name>/<version>\r
+ * @param major major version\r
+ * @return resource or <tt>null</tt>\r
+ * @throws DatabaseException\r
+ */\r
+ public Resource getGraphBundleResource(String id, int major) throws DatabaseException {\r
+ ReadGraph g = Transaction.readGraph();\r
+ Layer0 L0 = Layer0.getInstance(g);\r
+ DatabaseManagementResource DatabaseManagement = DatabaseManagementResource.getInstance(g);\r
+ \r
+ for (Resource r : g.getObjects(DatabaseManagement.InstalledGraphBundles, L0.ConsistsOf)) {\r
+ if ( !g.isInstanceOf(r, DatabaseManagement.GraphBundle) ) continue; \r
+ String vid = g.getRelatedValue(r, DatabaseManagement.HasVersionedId);\r
+ \r
+ Matcher m = GraphBundle.VERSIONED_ID_PATTERN.matcher(vid);\r
+ if (!m.matches()) continue;\r
+ \r
+ String rid = m.group(1);\r
+ int rmajor = Integer.valueOf( m.group(2) );\r
+ if (rid.equals(id) && rmajor==major) return r;\r
+ }\r
+ \r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * Create a GraphBundle entry into the database and attach to \r
+ * InstalledTransferableGraphs-library.\r
+ * \r
+ * @param entry\r
+ * @param resourceArray\r
+ * @return Resource\r
+ * @throws DatabaseException\r
+ */\r
+ Resource createGraphBundle(GraphBundle entry) throws DatabaseException {\r
+ WriteGraph g = Transaction.writeGraph();\r
+ Layer0 L0 = Layer0.getInstance(g);\r
+ DatabaseManagementResource DatabaseManagement = DatabaseManagementResource.getInstance(g);\r
+\r
+ Resource r = g.newResource();\r
+ g.claim(r, L0.InstanceOf, DatabaseManagement.GraphBundle);\r
+ g.addLiteral(r, L0.HasName, L0.NameOf, L0.String, entry.getName(), Bindings.STRING);\r
+ g.addLiteral(r, DatabaseManagement.HasVersionedId, DatabaseManagement.HasVersionedId_Inverse, L0.String, entry.getId()+"/"+entry.getMajor()+"."+entry.getMinor()+"."+entry.getService()+"."+entry.getQualifier(), Bindings.STRING);\r
+ g.addLiteral(r, DatabaseManagement.HasFile, DatabaseManagement.HasFile_Inverse, L0.Graph, entry.graph, tg_binding);\r
+ g.addLiteral(r, DatabaseManagement.HasHashCode, DatabaseManagement.HasHashCode_Inverse, L0.Integer, entry.hashcode, Bindings.INTEGER);\r
+ g.addLiteral(r, DatabaseManagement.HasInstallInfo, DatabaseManagement.HasInstallInfo_Inverse, L0.LongArray, entry.getResourceArray(), Bindings.LONG_ARRAY);\r
+ g.claim(DatabaseManagement.InstalledGraphBundles, L0.ConsistsOf, r);\r
+ return r;\r
+ \r
+ }\r
+\r
+ public Resource createGraphBundle(WriteOnlyGraph g, GraphBundle entry) throws DatabaseException {\r
+\r
+ Layer0 L0 = g.getService(Layer0.class);\r
+ DatabaseManagementResource DatabaseManagement = g.getService(DatabaseManagementResource.class);\r
+\r
+ Resource r = g.newResource();\r
+ g.claim(r, L0.InstanceOf, null, DatabaseManagement.GraphBundle);\r
+ g.addLiteral(r, L0.HasName, L0.NameOf, L0.String, entry.getName(), Bindings.STRING);\r
+ g.addLiteral(r, DatabaseManagement.HasVersionedId, DatabaseManagement.HasVersionedId_Inverse, L0.String, entry.getId()+"/"+entry.getMajor()+"."+entry.getMinor()+"."+entry.getService()+"."+entry.getQualifier(), Bindings.STRING);\r
+ g.addLiteral(r, DatabaseManagement.HasFile, DatabaseManagement.HasFile_Inverse, L0.Graph, entry.graph, tg_binding);\r
+ g.addLiteral(r, DatabaseManagement.HasHashCode, DatabaseManagement.HasHashCode_Inverse, L0.Integer, entry.hashcode, Bindings.INTEGER);\r
+ g.addLiteral(r, DatabaseManagement.HasInstallInfo, DatabaseManagement.HasInstallInfo_Inverse, L0.LongArray, entry.getResourceArray(), Bindings.LONG_ARRAY);\r
+ g.claim(DatabaseManagement.InstalledGraphBundles, L0.ConsistsOf, L0.PartOf, r); \r
+ return r;\r
+ \r
+ }\r
+ \r
+ /**\r
+ * Set TransferableGraph info.\r
+ * \r
+ * @param entry \r
+ * @param resourceArray\r
+ * @return new or existing feature resource\r
+ * @throws DatabaseException\r
+ * @throws AssumptionException thrown if bundle exists but is not a GraphBundle \r
+ */\r
+ public Resource setGraphBundleEntry(GraphBundle entry) throws DatabaseException {\r
+ Resource r = getGraphBundleResource(entry.getId(), entry.getMajor());\r
+ \r
+ // Create a new resource\r
+ if (r==null) {\r
+ r = createGraphBundle(entry);\r
+ return r;\r
+ }\r
+ \r
+ // Update values of an existing resource\r
+ {\r
+ WriteGraph g = Transaction.writeGraph(); \r
+ Layer0 L0 = Layer0.getInstance(g);\r
+ DatabaseManagementResource DatabaseManagement = DatabaseManagementResource.getInstance(g);\r
+ g.claimLiteral(r, L0.HasName, entry.getName(), Bindings.STRING);\r
+ g.claimLiteral(r, DatabaseManagement.HasVersionedId, entry.getId()+"/"+entry.getMajor()+"."+entry.getMinor()+"."+entry.getService()+"."+entry.getQualifier(), Bindings.STRING);\r
+ g.claimLiteral(r, DatabaseManagement.HasFile, DatabaseManagement.HasFile_Inverse, L0.Graph, entry.graph, tg_binding);\r
+ g.claimLiteral(r, DatabaseManagement.HasHashCode, entry.hashcode, Bindings.INTEGER);\r
+ g.claimLiteral(r, DatabaseManagement.HasInstallInfo, entry.getResourceArray(), Bindings.LONG_ARRAY);\r
+ return r;\r
+ } \r
+ } \r
+ \r
+}\r
+\r