]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.db.procore/src/fi/vtt/simantics/procore/internal/VirtualGraphServerSupportImpl.java
Multiple reader thread support for db client
[simantics/platform.git] / bundles / org.simantics.db.procore / src / fi / vtt / simantics / procore / internal / VirtualGraphServerSupportImpl.java
index 227d9bcff1d533b02793c665f0ed214ed4b409cb..4c850c4d162091d21f5f0536a4bb8286eeed2de6 100644 (file)
-package fi.vtt.simantics.procore.internal;\r
-\r
-import gnu.trove.set.hash.TIntHashSet;\r
-\r
-import java.io.File;\r
-import java.io.FileInputStream;\r
-import java.io.FileOutputStream;\r
-import java.io.FilenameFilter;\r
-import java.io.IOException;\r
-import java.io.InputStream;\r
-import java.io.ObjectInputStream;\r
-import java.io.ObjectOutputStream;\r
-import java.io.OutputStream;\r
-import java.util.ArrayList;\r
-import java.util.Collection;\r
-import java.util.HashMap;\r
-import java.util.Map;\r
-import java.util.concurrent.CopyOnWriteArrayList;\r
-import java.util.concurrent.atomic.AtomicInteger;\r
-\r
-import org.eclipse.core.runtime.IStatus;\r
-import org.eclipse.core.runtime.Status;\r
-import org.simantics.db.ReadGraph;\r
-import org.simantics.db.Resource;\r
-import org.simantics.db.Statement;\r
-import org.simantics.db.VirtualGraph;\r
-import org.simantics.db.VirtualGraph.Persistency;\r
-import org.simantics.db.WriteOnlyGraph;\r
-import org.simantics.db.common.utils.NameUtils;\r
-import org.simantics.db.exception.DatabaseException;\r
-import org.simantics.db.impl.ClusterI;\r
-import org.simantics.db.impl.ResourceImpl;\r
-import org.simantics.db.impl.TransientGraph;\r
-import org.simantics.db.impl.graph.ReadGraphImpl;\r
-import org.simantics.db.impl.support.VirtualGraphServerSupport;\r
-import org.simantics.db.request.Read;\r
-import org.simantics.db.service.SerialisationSupport;\r
-import org.simantics.db.service.ServerInformation;\r
-import org.simantics.db.service.TransferableGraphSupport;\r
-import org.simantics.db.service.VirtualGraphSupport;\r
-import org.simantics.db.service.XSupport;\r
-import org.simantics.layer0.Layer0;\r
-import org.simantics.utils.FileUtils;\r
-\r
-public class VirtualGraphServerSupportImpl implements VirtualGraphSupport, VirtualGraphServerSupport {\r
-\r
-       final private static boolean DEBUG = false;\r
-       final private SessionImplSocket session;\r
-\r
-       final public File virtualGraphStoragePath;\r
-       public String dbString = null;\r
-\r
-       public TIntHashSet virtuals = new TIntHashSet();\r
-\r
-       final public CopyOnWriteArrayList<TransientGraph>  providers          = new CopyOnWriteArrayList<TransientGraph>();\r
-       final private CopyOnWriteArrayList<TransientGraph> workspaceProviders = new CopyOnWriteArrayList<TransientGraph>();\r
-       final private CopyOnWriteArrayList<TransientGraph> memoryProviders    = new CopyOnWriteArrayList<TransientGraph>();\r
-\r
-       public AtomicInteger virtualId;\r
-       private boolean hasVirtuals = false;\r
-\r
-       public VirtualGraphServerSupportImpl(SessionImplSocket session, File path) {\r
-               this.session = session;\r
-               this.virtualGraphStoragePath = path;\r
-       }\r
-\r
-    void connect(String dbString) throws Exception {\r
-        virtualId = new AtomicInteger(-2);\r
-        this.dbString = dbString;\r
-\r
-        XSupport support = session.getService(XSupport.class);\r
-        if (support.rolledback()) {\r
-            for (File file : virtualGraphStoragePath.listFiles()) {\r
-                if (!file.delete()) {\r
-                    throw new IOException("Could not delete file " + file.getAbsolutePath());\r
-                }\r
-            }\r
-        }\r
-\r
-               File file = new File(virtualGraphStoragePath, "virtualGraphs." + dbString + ".dat");\r
-               \r
-               //      System.out.println("scanning " + file.getAbsolutePath());\r
-\r
-               if(file.exists()) {\r
-                       try {\r
-                               InputStream stream = new FileInputStream(file);\r
-                               final ObjectInputStream os = new ObjectInputStream(stream);\r
-                               virtualId = new AtomicInteger(os.readInt());\r
-                               //                      System.out.println("virtualId=" + virtualId.get());\r
-                               os.close();\r
-                               stream.close();\r
-\r
-                               hasVirtuals = true;\r
-\r
-                               String databaseId = session.getService(ServerInformation.class).getDatabaseId();\r
-                               String matcher = ".W." + databaseId + ".vg.";\r
-                               \r
-                               // Load existing workspace persistent graphs\r
-                               for(File virtualGraph : virtualGraphStoragePath.listFiles(new FilenameFilter() {\r
-                                       @Override\r
-                                       public boolean accept(File dir, String name) {\r
-                                           boolean matches = name.contains(matcher);\r
-                                               return matches;\r
-                                       }\r
-                               })) {\r
-                                       String name = virtualGraph.getName();\r
-                                       String[] parts = name.split("\\x2E", 2);\r
-                                       getWorkspacePersistent(parts[0]);\r
-                               }\r
-\r
-                       } catch (IOException e) {\r
-                               e.printStackTrace();\r
-                       }\r
-               } else {\r
-                       if (DEBUG)\r
-                               System.out.println("No stored virtual graphs.");\r
-               }\r
-\r
-       }\r
-\r
-       public void saveVirtualGraphState(SessionImplSocket session) {\r
-\r
-               if(!hasVirtuals) return;\r
-\r
-               try {\r
-\r
-                       String databaseId = session.getService(ServerInformation.class).getDatabaseId();\r
-                       String serverId = session.getService(ServerInformation.class).getServerId();\r
-                       File file = new File(virtualGraphStoragePath, "virtualGraphs." + databaseId + "." + serverId + ".dat");\r
-\r
-                       OutputStream stream = new FileOutputStream(file);\r
-                       final ObjectOutputStream os = new ObjectOutputStream(stream);\r
-                       os.writeInt(virtualId.get());\r
-                       os.flush();\r
-                       stream.close();\r
-\r
-               } catch (IOException e) {\r
-                       e.printStackTrace();\r
-               }\r
-\r
-       }\r
-\r
-       public void disposeVirtualGraphs() {\r
-\r
-               if(!hasVirtuals) return;\r
-\r
-               saveVirtualGraphState(session);\r
-               for(TransientGraph graph : workspaceProviders) graph.dispose();\r
-\r
-       }\r
-\r
-       public void saveVirtualGraphs() {\r
-\r
-               if(!hasVirtuals) return;\r
-\r
-               saveVirtualGraphState(session);\r
-               for(TransientGraph graph : workspaceProviders) graph.save();\r
-\r
-       }\r
-\r
-       @Override\r
-       public void saveAll() {\r
-               saveVirtualGraphs();\r
-       }\r
-\r
-       @Override\r
-       public VirtualGraph getMemoryPersistent(String identifier) {\r
-\r
-               if(identifier == null) throw new IllegalArgumentException("Argument cannot be null!");\r
-\r
-               for(TransientGraph graph : memoryProviders) {\r
-                       if(identifier.equals(graph.getIdentifier())) return graph;\r
-               }\r
-\r
-               String databaseId =  session.getService(ServerInformation.class).getDatabaseId();\r
-               VirtualGraphServerSupport vgss = session.getService(VirtualGraphServerSupport.class);\r
-\r
-        TransientGraph result = TransientGraph.memoryPersistent(new SerialisationSupportImpl(session), vgss, session.resourceSupport, session, databaseId, identifier);\r
-        memoryProviders.add(result);\r
-        providers.add(result);\r
-        return result;\r
-       }\r
-\r
-       private TransientGraph createWorkspacePersistentInternal(String identifier) {\r
-\r
-           String databaseId = session.getService(ServerInformation.class).getDatabaseId();\r
-           VirtualGraphServerSupport vgss = session.getService(VirtualGraphServerSupport.class);\r
-\r
-           try {\r
-               return TransientGraph.workspacePersistent(new SerialisationSupportImpl(session), vgss, session.resourceSupport, session, databaseId, identifier);\r
-           } catch (Exception e) {\r
-               Activator.log(new Status(IStatus.WARNING, Activator.PLUGIN_ID, "Failed to restore contents of previous virtual graph with identifier '" + identifier + "'. Resetting its contents to empty. See exception for problem details.", e));\r
-            return TransientGraph.memoryPersistent(new SerialisationSupportImpl(session), vgss, session.resourceSupport, session, databaseId, identifier);\r
-           }\r
-\r
-       }\r
-       \r
-       @Override\r
-       public VirtualGraph getWorkspacePersistent(String identifier) {\r
-\r
-               if(identifier == null) throw new IllegalArgumentException("Argument cannot be null!");\r
-\r
-               for(TransientGraph graph : workspaceProviders) {\r
-                       if(identifier.equals(graph.getIdentifier())) return graph;\r
-               }\r
-\r
-               TransientGraph result = createWorkspacePersistentInternal(identifier);\r
-               \r
-        workspaceProviders.add(result);\r
-        providers.add(result);\r
-        hasVirtuals = true;\r
-        return result;\r
-\r
-       }\r
-\r
-       @Override\r
-       public boolean discard(VirtualGraph provider) {\r
-               if (!(provider instanceof TransientGraph))\r
-                       return false;\r
-               if (!providers.remove(provider))\r
-                       return false;\r
-\r
-               TransientGraph tg = (TransientGraph) provider;\r
-\r
-               if (workspaceProviders.remove(provider)) {\r
-                       // TODO: remove possibly existing data from disk\r
-                       tg.dispose();\r
-               } else if (memoryProviders.remove(provider)) {\r
-                       tg.dispose();\r
-               }\r
-               return true;\r
-       }\r
-\r
-       public Resource getPersistentResource(WriteOnlyGraph graph, Resource resource, Map<Resource, Resource> creation) throws DatabaseException {\r
-               if(resource.isPersistent()) return resource;\r
-               else {\r
-                       Resource result = creation.get(resource);\r
-                       if(result == null) {\r
-                               result = graph.newResource();\r
-                               creation.put(resource, result);\r
-                       }\r
-                       return result;\r
-               }\r
-       }\r
-       \r
-       @Override\r
-       public boolean integrate(WriteOnlyGraph graph, VirtualGraph provider) throws DatabaseException {\r
-\r
-               if (!(provider instanceof TransientGraph))\r
-                       return false;\r
-               if (!providers.remove(provider))\r
-                       return false;\r
-\r
-               workspaceProviders.remove(provider);\r
-               memoryProviders.remove(provider);\r
-               \r
-               TransferableGraphSupport tgSupport = graph.getService(TransferableGraphSupport.class);\r
-               TransientGraph tg = (TransientGraph) provider;\r
-               \r
-               Map<Resource, Resource> creation = new HashMap<Resource, Resource>();\r
-               for(Statement stm : tg.listStatements()) {\r
-                       Resource subject = getPersistentResource(graph, stm.getSubject(), creation);\r
-                       Resource predicate = getPersistentResource(graph, stm.getPredicate(), creation);\r
-                       Resource object = getPersistentResource(graph, stm.getObject(), creation);\r
-                       graph.claim(subject, predicate, null, object);\r
-               }\r
-               for(Resource r : tg.listValues()) {\r
-                       byte[] value = tg.getValue(((ResourceImpl)r).id);\r
-                       tgSupport.setValue(graph, getPersistentResource(graph, r, creation), null, value);\r
-               }\r
-               discard(provider);\r
-               return true;\r
-               \r
-       }\r
-\r
-       @Override\r
-       public Collection<TransientGraph> getVirtualGraphs(int subject) {\r
-               if(subject < 0 || virtuals.contains(subject)) return providers;\r
-               else return null; \r
-       }\r
-\r
-       @Override\r
-       public void removeVirtual(int id) {\r
-               virtuals.remove(id);\r
-       }\r
-\r
-       @Override\r
-       public void addVirtual(int id) {\r
-               assert(id > 0);\r
-               //              System.err.println("addVirtual " + id);\r
-               virtuals.add(id);\r
-               ClusterI cluster = session.clusterTable.getClusterByResourceKey(id);\r
-               cluster.markVirtual();\r
-       }\r
-\r
-       @Override\r
-       public int createVirtual() {\r
-               return virtualId.decrementAndGet();\r
-       }\r
-\r
-       @Override\r
-       public File storagePath() {\r
-               return virtualGraphStoragePath;\r
-       }\r
-\r
-       @Override\r
-       public Collection<Statement> listStatements(VirtualGraph graph_) {\r
-               TransientGraph graph = (TransientGraph)graph_;\r
-               return graph.listStatements();\r
-       }\r
-\r
-       @Override\r
-       public Collection<Resource> listValues(VirtualGraph graph_) {\r
-               TransientGraph graph = (TransientGraph)graph_;\r
-               return graph.listValues();\r
-       }\r
-\r
-       @Override\r
-       public Collection<VirtualGraph> listGraphs() {\r
-               ArrayList<VirtualGraph> result = new ArrayList<VirtualGraph>();\r
-               result.addAll(memoryProviders);\r
-               result.addAll(workspaceProviders);\r
-               return result;\r
-       }\r
-\r
-       public String report(final File file) {\r
-\r
-               session.asyncRequest(new Read<String>() {\r
-\r
-                       @Override\r
-                       public String perform(ReadGraph graph) throws DatabaseException {\r
-\r
-                               SerialisationSupport ss = session.getService(SerialisationSupport.class);\r
-                               StringBuilder b = new StringBuilder();\r
-                               try {\r
-                                       for(VirtualGraph vg : listGraphs()) {\r
-                                               TransientGraph tg = (TransientGraph)vg;\r
-                                               if(Persistency.MEMORY == tg.getPersistency()) b.append("Memory persistent virtual graph '" + tg.getIdentifier() + "'\n");\r
-                                               if(Persistency.WORKSPACE == tg.getPersistency()) b.append("Workspace persistent virtual graph '" + tg.getIdentifier() + "'\n");\r
-                                               for(Statement stm : listStatements(tg)) {\r
-                                                       int s = ss.getTransientId(stm.getSubject());\r
-                                                       int p = ss.getTransientId(stm.getPredicate());\r
-                                                       int o = ss.getTransientId(stm.getObject());\r
-                                                       String sName = NameUtils.getSafeName(graph, stm.getSubject());\r
-                                                       String pName = NameUtils.getSafeName(graph, stm.getPredicate());\r
-                                                       String oName = NameUtils.getSafeName(graph, stm.getObject());\r
-                                                       b.append(" S '" + sName + "' '" + pName + "' '" + oName + "' " + s + " " + p + " " + o + "\n");\r
-                                               }\r
-                                               for(Resource r : listValues(tg)) {\r
-                                                       String sName = NameUtils.getSafeName(graph, r);\r
-                                                       Object value = graph.getPossibleValue(r);\r
-                                                       b.append(" V '" + sName + "' '" + value + "'\n");\r
-                                               }\r
-                                       }\r
-                                       FileUtils.writeFile(file, b.toString().getBytes());\r
-                               } catch (IOException e) {\r
-                                       e.printStackTrace();\r
-                                       return "ERROR";\r
-                               } catch (DatabaseException e) {\r
-                                       e.printStackTrace();\r
-                                       return "ERROR";\r
-                               }\r
-                               return "OK";\r
-\r
-                       }\r
-\r
-               });\r
-               return "OK";\r
-\r
-       }\r
-\r
-       @Override\r
-       public VirtualGraph getGraph(ReadGraph graph, Resource subject, Resource predicate, Resource object) throws DatabaseException {\r
-               ReadGraphImpl impl = (ReadGraphImpl)graph;\r
-               return impl.processor.getProvider(subject, predicate, object);\r
-       }\r
-\r
-       @Override\r
-       public VirtualGraph getGraph(ReadGraph graph, Resource subject, Resource predicate) throws DatabaseException {\r
-               ReadGraphImpl impl = (ReadGraphImpl)graph;\r
-               return impl.processor.getProvider(subject, predicate);\r
-       }\r
-\r
-       @Override\r
-       public VirtualGraph getGraph(ReadGraph graph, Resource subject) throws DatabaseException {\r
-               Layer0 L0 = Layer0.getInstance(graph);\r
-               if(graph.hasStatement(subject, L0.InstanceOf)) {\r
-                       return getGraph(graph, subject, L0.InstanceOf);\r
-               } else if (graph.hasStatement(subject, L0.Inherits)) {\r
-                       return getGraph(graph, subject, L0.Inherits);\r
-               } else if (graph.hasStatement(subject, L0.SubrelationOf)) {\r
-                       return getGraph(graph, subject, L0.SubrelationOf);\r
-               } else {\r
-                       throw new DatabaseException("Resource is invalid, should have a statement with either L0.InstanceOf, L0.Inherits or L0.SubrelationOf " + subject);\r
-               }\r
-       }\r
-       \r
-}\r
+package fi.vtt.simantics.procore.internal;
+
+import gnu.trove.set.hash.TIntHashSet;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.Statement;
+import org.simantics.db.VirtualGraph;
+import org.simantics.db.VirtualGraph.Persistency;
+import org.simantics.db.WriteOnlyGraph;
+import org.simantics.db.common.utils.NameUtils;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.impl.ClusterI;
+import org.simantics.db.impl.ResourceImpl;
+import org.simantics.db.impl.TransientGraph;
+import org.simantics.db.impl.graph.ReadGraphImpl;
+import org.simantics.db.impl.support.VirtualGraphServerSupport;
+import org.simantics.db.request.Read;
+import org.simantics.db.service.SerialisationSupport;
+import org.simantics.db.service.ServerInformation;
+import org.simantics.db.service.TransferableGraphSupport;
+import org.simantics.db.service.VirtualGraphSupport;
+import org.simantics.db.service.XSupport;
+import org.simantics.layer0.Layer0;
+import org.simantics.utils.FileUtils;
+
+public class VirtualGraphServerSupportImpl implements VirtualGraphSupport, VirtualGraphServerSupport {
+
+       final private static boolean DEBUG = false;
+       final private SessionImplSocket session;
+
+       final public File virtualGraphStoragePath;
+       public String dbString = null;
+
+       public TIntHashSet virtuals = new TIntHashSet();
+
+       final public CopyOnWriteArrayList<TransientGraph>  providers          = new CopyOnWriteArrayList<TransientGraph>();
+       final private CopyOnWriteArrayList<TransientGraph> workspaceProviders = new CopyOnWriteArrayList<TransientGraph>();
+       final private CopyOnWriteArrayList<TransientGraph> memoryProviders    = new CopyOnWriteArrayList<TransientGraph>();
+
+       public AtomicInteger virtualId;
+       private boolean hasVirtuals = false;
+
+       public VirtualGraphServerSupportImpl(SessionImplSocket session, File path) {
+               this.session = session;
+               this.virtualGraphStoragePath = path;
+       }
+
+    void connect(String dbString) throws Exception {
+        virtualId = new AtomicInteger(-2);
+        this.dbString = dbString;
+
+        XSupport support = session.getService(XSupport.class);
+        if (support.rolledback()) {
+            for (File file : virtualGraphStoragePath.listFiles()) {
+                if (!file.delete()) {
+                    throw new IOException("Could not delete file " + file.getAbsolutePath());
+                }
+            }
+        }
+
+               File file = new File(virtualGraphStoragePath, "virtualGraphs." + dbString + ".dat");
+               
+               //      System.out.println("scanning " + file.getAbsolutePath());
+
+               if(file.exists()) {
+                       try {
+                               InputStream stream = new FileInputStream(file);
+                               final ObjectInputStream os = new ObjectInputStream(stream);
+                               virtualId = new AtomicInteger(os.readInt());
+                               //                      System.out.println("virtualId=" + virtualId.get());
+                               os.close();
+                               stream.close();
+
+                               hasVirtuals = true;
+
+                               String databaseId = session.getService(ServerInformation.class).getDatabaseId();
+                               String matcher = ".W." + databaseId + ".vg.";
+                               
+                               // Load existing workspace persistent graphs
+                               for(File virtualGraph : virtualGraphStoragePath.listFiles(new FilenameFilter() {
+                                       @Override
+                                       public boolean accept(File dir, String name) {
+                                           boolean matches = name.contains(matcher);
+                                               return matches;
+                                       }
+                               })) {
+                                       String name = virtualGraph.getName();
+                                       String[] parts = name.split("\\x2E", 2);
+                                       getWorkspacePersistent(parts[0]);
+                               }
+
+                       } catch (IOException e) {
+                               e.printStackTrace();
+                       }
+               } else {
+                       if (DEBUG)
+                               System.out.println("No stored virtual graphs.");
+               }
+
+       }
+
+       public void saveVirtualGraphState(SessionImplSocket session) {
+
+               if(!hasVirtuals) return;
+
+               try {
+
+                       String databaseId = session.getService(ServerInformation.class).getDatabaseId();
+                       String serverId = session.getService(ServerInformation.class).getServerId();
+                       File file = new File(virtualGraphStoragePath, "virtualGraphs." + databaseId + "." + serverId + ".dat");
+
+                       OutputStream stream = new FileOutputStream(file);
+                       final ObjectOutputStream os = new ObjectOutputStream(stream);
+                       os.writeInt(virtualId.get());
+                       os.flush();
+                       stream.close();
+
+               } catch (IOException e) {
+                       e.printStackTrace();
+               }
+
+       }
+
+       public void disposeVirtualGraphs() {
+
+               if(!hasVirtuals) return;
+
+               saveVirtualGraphState(session);
+               for(TransientGraph graph : workspaceProviders) graph.dispose();
+
+       }
+
+       public void saveVirtualGraphs() {
+
+               if(!hasVirtuals) return;
+
+               saveVirtualGraphState(session);
+               for(TransientGraph graph : workspaceProviders) graph.save();
+
+       }
+
+       @Override
+       public void saveAll() {
+               saveVirtualGraphs();
+       }
+
+       @Override
+       public VirtualGraph getMemoryPersistent(String identifier) {
+
+               if(identifier == null) throw new IllegalArgumentException("Argument cannot be null!");
+
+               for(TransientGraph graph : memoryProviders) {
+                       if(identifier.equals(graph.getIdentifier())) return graph;
+               }
+
+               String databaseId =  session.getService(ServerInformation.class).getDatabaseId();
+               VirtualGraphServerSupport vgss = session.getService(VirtualGraphServerSupport.class);
+
+        TransientGraph result = TransientGraph.memoryPersistent(new SerialisationSupportImpl(session), vgss, session.resourceSupport, session, databaseId, identifier);
+        memoryProviders.add(result);
+        providers.add(result);
+        return result;
+       }
+
+       private TransientGraph createWorkspacePersistentInternal(String identifier) {
+
+           String databaseId = session.getService(ServerInformation.class).getDatabaseId();
+           VirtualGraphServerSupport vgss = session.getService(VirtualGraphServerSupport.class);
+
+           try {
+               return TransientGraph.workspacePersistent(new SerialisationSupportImpl(session), vgss, session.resourceSupport, session, databaseId, identifier);
+           } catch (Exception e) {
+               Activator.log(new Status(IStatus.WARNING, Activator.PLUGIN_ID, "Failed to restore contents of previous virtual graph with identifier '" + identifier + "'. Resetting its contents to empty. See exception for problem details.", e));
+            return TransientGraph.memoryPersistent(new SerialisationSupportImpl(session), vgss, session.resourceSupport, session, databaseId, identifier);
+           }
+
+       }
+       
+       @Override
+       public VirtualGraph getWorkspacePersistent(String identifier) {
+
+               if(identifier == null) throw new IllegalArgumentException("Argument cannot be null!");
+
+               for(TransientGraph graph : workspaceProviders) {
+                       if(identifier.equals(graph.getIdentifier())) return graph;
+               }
+
+               TransientGraph result = createWorkspacePersistentInternal(identifier);
+               
+        workspaceProviders.add(result);
+        providers.add(result);
+        hasVirtuals = true;
+        return result;
+
+       }
+
+       @Override
+       public boolean discard(VirtualGraph provider) {
+               if (!(provider instanceof TransientGraph))
+                       return false;
+               if (!providers.remove(provider))
+                       return false;
+
+               TransientGraph tg = (TransientGraph) provider;
+
+               if (workspaceProviders.remove(provider)) {
+                       // TODO: remove possibly existing data from disk
+                       tg.dispose();
+               } else if (memoryProviders.remove(provider)) {
+                       tg.dispose();
+               }
+               return true;
+       }
+
+       public Resource getPersistentResource(WriteOnlyGraph graph, Resource resource, Map<Resource, Resource> creation) throws DatabaseException {
+               if(resource.isPersistent()) return resource;
+               else {
+                       Resource result = creation.get(resource);
+                       if(result == null) {
+                               result = graph.newResource();
+                               creation.put(resource, result);
+                       }
+                       return result;
+               }
+       }
+       
+       @Override
+       public boolean integrate(WriteOnlyGraph graph, VirtualGraph provider) throws DatabaseException {
+
+               if (!(provider instanceof TransientGraph))
+                       return false;
+               if (!providers.remove(provider))
+                       return false;
+
+               workspaceProviders.remove(provider);
+               memoryProviders.remove(provider);
+               
+               TransferableGraphSupport tgSupport = graph.getService(TransferableGraphSupport.class);
+               TransientGraph tg = (TransientGraph) provider;
+               
+               Map<Resource, Resource> creation = new HashMap<Resource, Resource>();
+               for(Statement stm : tg.listStatements()) {
+                       Resource subject = getPersistentResource(graph, stm.getSubject(), creation);
+                       Resource predicate = getPersistentResource(graph, stm.getPredicate(), creation);
+                       Resource object = getPersistentResource(graph, stm.getObject(), creation);
+                       graph.claim(subject, predicate, null, object);
+               }
+               for(Resource r : tg.listValues()) {
+                       byte[] value = tg.getValue(((ResourceImpl)r).id);
+                       tgSupport.setValue(graph, getPersistentResource(graph, r, creation), null, value);
+               }
+               discard(provider);
+               return true;
+               
+       }
+
+       @Override
+       public Collection<TransientGraph> getVirtualGraphs(int subject) {
+               if(subject < 0 || virtuals.contains(subject)) return providers;
+               else return null; 
+       }
+
+       @Override
+       public void removeVirtual(int id) {
+               virtuals.remove(id);
+       }
+
+       @Override
+       public void addVirtual(int id) {
+               assert(id > 0);
+               //              System.err.println("addVirtual " + id);
+               virtuals.add(id);
+               ClusterI cluster = session.clusterTable.getClusterByResourceKey(id);
+               cluster.markVirtual();
+       }
+
+       @Override
+       public int createVirtual() {
+               return virtualId.decrementAndGet();
+       }
+
+       @Override
+       public File storagePath() {
+               return virtualGraphStoragePath;
+       }
+
+       @Override
+       public Collection<Statement> listStatements(VirtualGraph graph_) {
+               TransientGraph graph = (TransientGraph)graph_;
+               return graph.listStatements();
+       }
+
+       @Override
+       public Collection<Resource> listValues(VirtualGraph graph_) {
+               TransientGraph graph = (TransientGraph)graph_;
+               return graph.listValues();
+       }
+
+       @Override
+       public Collection<VirtualGraph> listGraphs() {
+               ArrayList<VirtualGraph> result = new ArrayList<VirtualGraph>();
+               result.addAll(memoryProviders);
+               result.addAll(workspaceProviders);
+               return result;
+       }
+
+       public String report(final File file) {
+
+               session.asyncRequest(new Read<String>() {
+
+                       @Override
+                       public String perform(ReadGraph graph) throws DatabaseException {
+
+                               SerialisationSupport ss = session.getService(SerialisationSupport.class);
+                               StringBuilder b = new StringBuilder();
+                               try {
+                                       for(VirtualGraph vg : listGraphs()) {
+                                               TransientGraph tg = (TransientGraph)vg;
+                                               if(Persistency.MEMORY == tg.getPersistency()) b.append("Memory persistent virtual graph '" + tg.getIdentifier() + "'\n");
+                                               if(Persistency.WORKSPACE == tg.getPersistency()) b.append("Workspace persistent virtual graph '" + tg.getIdentifier() + "'\n");
+                                               for(Statement stm : listStatements(tg)) {
+                                                       int s = ss.getTransientId(stm.getSubject());
+                                                       int p = ss.getTransientId(stm.getPredicate());
+                                                       int o = ss.getTransientId(stm.getObject());
+                                                       String sName = NameUtils.getSafeName(graph, stm.getSubject());
+                                                       String pName = NameUtils.getSafeName(graph, stm.getPredicate());
+                                                       String oName = NameUtils.getSafeName(graph, stm.getObject());
+                                                       b.append(" S '" + sName + "' '" + pName + "' '" + oName + "' " + s + " " + p + " " + o + "\n");
+                                               }
+                                               for(Resource r : listValues(tg)) {
+                                                       String sName = NameUtils.getSafeName(graph, r);
+                                                       Object value = graph.getPossibleValue(r);
+                                                       b.append(" V '" + sName + "' '" + value + "'\n");
+                                               }
+                                       }
+                                       FileUtils.writeFile(file, b.toString().getBytes());
+                               } catch (IOException e) {
+                                       e.printStackTrace();
+                                       return "ERROR";
+                               } catch (DatabaseException e) {
+                                       e.printStackTrace();
+                                       return "ERROR";
+                               }
+                               return "OK";
+
+                       }
+
+               });
+               return "OK";
+
+       }
+
+       @Override
+       public VirtualGraph getGraph(ReadGraph graph, Resource subject, Resource predicate, Resource object) throws DatabaseException {
+               ReadGraphImpl impl = (ReadGraphImpl)graph;
+               return impl.processor.getProvider(subject, predicate, object);
+       }
+
+       @Override
+       public VirtualGraph getGraph(ReadGraph graph, Resource subject, Resource predicate) throws DatabaseException {
+               ReadGraphImpl impl = (ReadGraphImpl)graph;
+               return impl.processor.getProvider(subject, predicate);
+       }
+
+       @Override
+       public VirtualGraph getGraph(ReadGraph graph, Resource subject) throws DatabaseException {
+               Layer0 L0 = Layer0.getInstance(graph);
+               if(graph.hasStatement(subject, L0.InstanceOf)) {
+                       return getGraph(graph, subject, L0.InstanceOf);
+               } else if (graph.hasStatement(subject, L0.Inherits)) {
+                       return getGraph(graph, subject, L0.Inherits);
+               } else if (graph.hasStatement(subject, L0.SubrelationOf)) {
+                       return getGraph(graph, subject, L0.SubrelationOf);
+               } else {
+                       throw new DatabaseException("Resource is invalid, should have a statement with either L0.InstanceOf, L0.Inherits or L0.SubrelationOf " + subject);
+               }
+       }
+       
+}