]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.db.impl/src/org/simantics/db/impl/graph/WriteGraphImpl.java
Merge "Multiple reader thread support for db client"
[simantics/platform.git] / bundles / org.simantics.db.impl / src / org / simantics / db / impl / graph / WriteGraphImpl.java
index f560008e3d009639652585626bd46a90879589a6..56fb9ec72f977e967b6a3db6a9366d4d20433657 100644 (file)
-/*******************************************************************************\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.db.impl.graph;\r
-\r
-import java.io.IOException;\r
-import java.io.PrintWriter;\r
-import java.io.StringWriter;\r
-import java.util.TreeMap;\r
-\r
-import org.simantics.databoard.Bindings;\r
-import org.simantics.databoard.accessor.Accessor;\r
-import org.simantics.databoard.binding.Binding;\r
-import org.simantics.databoard.binding.error.BindingConstructionException;\r
-import org.simantics.databoard.binding.mutable.Variant;\r
-import org.simantics.databoard.primitives.MutableBoolean;\r
-import org.simantics.databoard.primitives.MutableByte;\r
-import org.simantics.databoard.primitives.MutableDouble;\r
-import org.simantics.databoard.primitives.MutableFloat;\r
-import org.simantics.databoard.primitives.MutableInteger;\r
-import org.simantics.databoard.primitives.MutableLong;\r
-import org.simantics.databoard.primitives.MutableString;\r
-import org.simantics.databoard.serialization.SerializationException;\r
-import org.simantics.databoard.serialization.Serializer;\r
-import org.simantics.databoard.type.Datatype;\r
-import org.simantics.databoard.util.binary.RandomAccessBinary;\r
-import org.simantics.db.DevelopmentKeys;\r
-import org.simantics.db.ExternalValueSupport;\r
-import org.simantics.db.Metadata;\r
-import org.simantics.db.Resource;\r
-import org.simantics.db.Statement;\r
-import org.simantics.db.VirtualGraph;\r
-import org.simantics.db.WriteGraph;\r
-import org.simantics.db.WriteOnlyGraph;\r
-import org.simantics.db.common.request.WriteOnlyRequest;\r
-import org.simantics.db.common.utils.Literals;\r
-import org.simantics.db.common.utils.Logger;\r
-import org.simantics.db.exception.ArgumentException;\r
-import org.simantics.db.exception.DatabaseException;\r
-import org.simantics.db.exception.InternalException;\r
-import org.simantics.db.exception.ManyObjectsForFunctionalRelationException;\r
-import org.simantics.db.exception.ServiceException;\r
-import org.simantics.db.impl.DatabaseUtils;\r
-import org.simantics.db.impl.MemWatch;\r
-import org.simantics.db.impl.ResourceImpl;\r
-import org.simantics.db.impl.internal.RandomAccessValueSupport;\r
-import org.simantics.db.impl.internal.ResourceData;\r
-import org.simantics.db.impl.query.CacheEntry;\r
-import org.simantics.db.impl.query.QueryProcessor;\r
-import org.simantics.db.impl.support.WriteRequestScheduleSupport;\r
-import org.simantics.db.procedure.Procedure;\r
-import org.simantics.db.request.DelayedWrite;\r
-import org.simantics.db.request.DelayedWriteResult;\r
-import org.simantics.db.request.Write;\r
-import org.simantics.db.request.WriteOnly;\r
-import org.simantics.db.request.WriteOnlyResult;\r
-import org.simantics.db.request.WriteResult;\r
-import org.simantics.db.request.WriteTraits;\r
-import org.simantics.layer0.Layer0;\r
-import org.simantics.utils.Development;\r
-import org.simantics.utils.datastructures.Callback;\r
-import org.simantics.utils.datastructures.Pair;\r
-\r
-import gnu.trove.map.hash.THashMap;\r
-\r
-\r
-final public class WriteGraphImpl extends ReadGraphImpl implements WriteGraph {\r
-\r
-    final public static Binding DATA_TYPE_BINDING = Bindings.getBindingUnchecked(Datatype.class);\r
-    \r
-    final public WriteSupport writeSupport;\r
-    final public VirtualGraph provider;\r
-    \r
-    private String resourceName(Resource resource) throws DatabaseException {\r
-       if(Development.DEVELOPMENT) {\r
-               Boolean names = Development.getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG_NAMES, Bindings.BOOLEAN); \r
-               if(names && !writeSupport.writeOnly()) return DatabaseUtils.getReadableName(this, resource);\r
-               else return resource.toString();\r
-       }\r
-       throw new IllegalStateException();\r
-    }\r
-    \r
-    private Layer0 getBuiltins() {\r
-        return getService(Layer0.class);\r
-    }\r
-\r
-    private WriteRequestScheduleSupport getWriteRequestScheduler() {\r
-        return (WriteRequestScheduleSupport) getSession();\r
-    }\r
-\r
-    private WriteGraphImpl(CacheEntry parent2, QueryProcessor readSupport,\r
-            WriteSupport writeSupport, VirtualGraph provider) {\r
-        super(parent2, readSupport);\r
-        this.writeSupport = writeSupport;\r
-        this.provider = provider;\r
-    }\r
-\r
-    public final static WriteGraphImpl create(QueryProcessor support, WriteSupport writeSupport, VirtualGraph provider) {\r
-\r
-       WriteGraphImpl impl = new WriteGraphImpl(null, support, writeSupport, provider); \r
-        \r
-        try {\r
-                       writeSupport.setDefaultClusterSet(null);\r
-               } catch (ServiceException e) {\r
-                       Logger.defaultLogError(e);\r
-               }\r
-        \r
-        return impl;\r
-        \r
-    }\r
-\r
-    final public WriteGraphImpl newAsync() {\r
-        return this;\r
-    }\r
-    \r
-    public WriteGraphImpl newSync(final VirtualGraph provider) {\r
-        return new WriteGraphImpl(parent, processor, writeSupport, provider);\r
-    }\r
-    final public WriteGraphImpl newSync(final CacheEntry parent) {\r
-        return new WriteGraphImpl(parent, processor, writeSupport, provider);\r
-    }\r
-\r
-    @Override\r
-    final public ReadGraphImpl withAsyncParent(CacheEntry parent2) {\r
-        return new WriteGraphImpl(parent2, processor, writeSupport, provider);\r
-    }\r
-\r
-    @Override\r
-    public ReadGraphImpl newRestart(ReadGraphImpl impl) {\r
-\r
-        WriteGraphImpl write = processor.getSession().getService(WriteGraphImpl.class);\r
-\r
-        return write;\r
-        \r
-    }\r
-\r
-    @Override\r
-    final public Resource newResource() throws ServiceException {\r
-        \r
-        try {\r
-               \r
-               Resource result = writeSupport.createResource(provider); \r
-\r
-               if(Development.DEVELOPMENT) {\r
-                       if(Development.<Boolean>getProperty(DevelopmentKeys.WRITELOGGER_LOG, Bindings.BOOLEAN))\r
-                       WriteLogger.logNewResource(this, result);\r
-               }\r
-               \r
-            return result;\r
-            \r
-        } catch (DatabaseException e) {\r
-            throw new ServiceException(e);\r
-        }\r
-        \r
-    }\r
-\r
-    @Override\r
-    final public Resource newResource(long clusterId) throws ServiceException {\r
-        \r
-        try {\r
-            \r
-               Resource result = writeSupport.createResource(provider, clusterId); \r
-\r
-               if(Development.DEVELOPMENT) {\r
-                       if(Development.<Boolean>getProperty(DevelopmentKeys.WRITELOGGER_LOG, Bindings.BOOLEAN))\r
-                       WriteLogger.logNewResource(this, result);\r
-               }\r
-               \r
-            return result;\r
-            \r
-        } catch (DatabaseException e) {\r
-            throw new ServiceException(e);\r
-        }\r
-        \r
-    }\r
-\r
-    @Override\r
-    public Resource newResource(Resource clusterSet) throws ServiceException {\r
-        try {\r
-               Resource result;\r
-               if (provider != null)\r
-                       result = writeSupport.createResource(provider);\r
-               else\r
-                       result = writeSupport.createResource(provider, clusterSet); \r
-            if(Development.DEVELOPMENT) {\r
-                if(Development.<Boolean>getProperty(DevelopmentKeys.WRITELOGGER_LOG, Bindings.BOOLEAN))\r
-                    WriteLogger.logNewResource(this, result);\r
-            }\r
-            return result;\r
-        } catch (ServiceException e) {\r
-            throw e;\r
-        } catch (DatabaseException e) {\r
-            throw new ServiceException(e);\r
-        }\r
-    }\r
-\r
-    @Override\r
-    public void newClusterSet(Resource clusterSet) throws ServiceException {\r
-        try {\r
-               if (provider == null)\r
-                       writeSupport.createClusterSet(provider, clusterSet); \r
-            if(Development.DEVELOPMENT) {\r
-                if(Development.<Boolean>getProperty(DevelopmentKeys.WRITELOGGER_LOG, Bindings.BOOLEAN))\r
-                    WriteLogger.logNewResource(this, clusterSet);\r
-            }\r
-        } catch (ServiceException e) {\r
-            throw e;\r
-        } catch (DatabaseException e) {\r
-            throw new ServiceException(e);\r
-        }\r
-    }\r
-\r
-    @Override\r
-    public Resource setClusterSet4NewResource(Resource clusterSet)\r
-    throws ServiceException {\r
-        return writeSupport.setDefaultClusterSet(clusterSet);\r
-    }\r
-    /**\r
-     * Compares two object for equality, allowing null objects also.\r
-     * \r
-     * @param o1 obj1\r
-     * @param o2 obj2\r
-     * @return true if both arguments are <code>null</code> or equal\r
-     */\r
-    static boolean safeEquals(Object o1, Object o2) {\r
-        if (o1 == o2)\r
-            return true;\r
-        if (o1 == null && o2 == null)\r
-            return true;\r
-        if (o1 == null || o2 == null)\r
-            return false;\r
-        return o1.equals(o2);\r
-    }\r
-\r
-    @Override\r
-    public void asyncRequest(DelayedWrite request) {\r
-        assert (request != null);\r
-        getWriteRequestScheduler().scheduleRequest(request, new Callback<DatabaseException>() {\r
-\r
-                       @Override\r
-                       public void run(DatabaseException parameter) {\r
-                               if(parameter != null)\r
-                                       Logger.defaultLogError(parameter);\r
-                       }\r
-               \r
-        }, null, Boolean.TRUE);\r
-    }\r
-\r
-    @Override\r
-    public void asyncRequest(DelayedWrite request, Callback<DatabaseException> callback) {\r
-        assert (request != null);\r
-        getWriteRequestScheduler().scheduleRequest(request, callback, null, Boolean.TRUE);\r
-    }\r
-\r
-    @Override\r
-    public <T> void asyncRequest(DelayedWriteResult<T> request, Procedure<T> procedure) {\r
-        assert (request != null);\r
-        getWriteRequestScheduler().scheduleRequest(request, procedure, null, Boolean.TRUE);\r
-    }\r
-\r
-    @Override\r
-    public void asyncRequest(final Write r) {\r
-        assert (r != null);\r
-        getWriteRequestScheduler().scheduleRequest(r, new Callback<DatabaseException>() {\r
-\r
-                       @Override\r
-                       public void run(DatabaseException parameter) {\r
-                               if(parameter != null)\r
-                                       Logger.defaultLogError(parameter);\r
-                       }\r
-               \r
-        }, null, Boolean.TRUE);\r
-    }\r
-\r
-    @Override\r
-    public void asyncRequest(Write request, Callback<DatabaseException> callback) {\r
-        assert (request != null);\r
-        getWriteRequestScheduler().scheduleRequest(request, callback, null, Boolean.TRUE);\r
-    }\r
-\r
-    @Override\r
-    public void asyncRequest(WriteOnly request) {\r
-        assert (request != null);\r
-        getWriteRequestScheduler().scheduleRequest(request, new Callback<DatabaseException>() {\r
-\r
-                       @Override\r
-                       public void run(DatabaseException parameter) {\r
-                               if(parameter != null)\r
-                                       Logger.defaultLogError(parameter);\r
-                       }\r
-               \r
-        }, null, Boolean.TRUE);\r
-    }\r
-    \r
-    @Override\r
-    public void asyncRequest(WriteOnly request, Callback<DatabaseException> callback) {\r
-        assert (request != null);\r
-        getWriteRequestScheduler().scheduleRequest(request, callback, null, Boolean.TRUE);\r
-    }\r
-\r
-    @Override\r
-    public <T> void asyncRequest(WriteOnlyResult<T> request, Procedure<T> procedure) {\r
-        assert (request != null);\r
-        getWriteRequestScheduler().scheduleRequest(request, procedure, null, Boolean.TRUE);\r
-    }\r
-\r
-    @Override\r
-    public <T> void asyncRequest(WriteResult<T> request, Procedure<T> procedure) {\r
-        assert (request != null);\r
-        getWriteRequestScheduler().scheduleRequest(request, procedure, null, Boolean.TRUE);\r
-    }\r
-\r
-    @Override\r
-    public void syncRequest(Write request) throws DatabaseException {\r
-        \r
-       Resource defaultClusterSet = setClusterSet4NewResource(null);\r
-       \r
-        WriteGraphImpl graph = (WriteGraphImpl)newSync(request.getProvider()); \r
-        try {\r
-            writeSupport.performWriteRequest(graph, request);\r
-        } catch (DatabaseException e) {\r
-            throw e;\r
-        } catch (Throwable t) {\r
-            throw new DatabaseException(t);\r
-        } finally {\r
-            setClusterSet4NewResource(defaultClusterSet);\r
-        }\r
-        \r
-        \r
-    }\r
-    \r
-    @Override\r
-    public <T> T syncRequest(WriteResult<T> request) throws DatabaseException {\r
-\r
-       Resource defaultClusterSet = setClusterSet4NewResource(null);\r
-\r
-       WriteGraphImpl graph = (WriteGraphImpl)newSync(request.getProvider()); \r
-        try {\r
-            return writeSupport.performWriteRequest(graph, request);\r
-        } catch (DatabaseException e) {\r
-            throw e;\r
-        } catch (Throwable t) {\r
-            throw new DatabaseException(t);\r
-        } finally {\r
-            setClusterSet4NewResource(defaultClusterSet);\r
-        }\r
-    }\r
-\r
-    @Override\r
-    public void syncRequest(final DelayedWrite request) throws DatabaseException {\r
-        \r
-        try {\r
-\r
-               final DelayedWriteGraph dwg = new DelayedWriteGraph(this);\r
-               request.perform(dwg);\r
-\r
-               syncRequest(new WriteOnlyRequest() {\r
-\r
-                       @Override\r
-                       public void perform(WriteOnlyGraph graph) throws DatabaseException {\r
-                               dwg.commit(graph, request);\r
-                       }\r
-\r
-               });\r
-\r
-        } catch (DatabaseException e) {\r
-               \r
-               throw e;\r
-               \r
-        } catch (Throwable e) {\r
-               \r
-               throw new DatabaseException(e);\r
-               \r
-        } finally {\r
-               \r
-        }\r
-        \r
-    }\r
-\r
-    @Override\r
-    public void syncRequest(WriteOnly request) throws DatabaseException {\r
-       \r
-       Resource defaultClusterSet = setClusterSet4NewResource(null);\r
-       \r
-        try {\r
-            writeSupport.performWriteRequest(this, request);\r
-        } catch (DatabaseException e) {\r
-            throw e;\r
-        } catch (Throwable t) {\r
-            throw new DatabaseException(t);\r
-        }  finally {\r
-            setClusterSet4NewResource(defaultClusterSet);\r
-        }\r
-        \r
-    }\r
-    \r
-    @Override\r
-    public void claim(Resource subject, Resource predicate, Resource object) throws ServiceException {\r
-\r
-        if(subject == null || predicate == null || object == null) {\r
-               throw new ServiceException("Claim does not accept null arguments (subject=" + subject + ",predicate=" + predicate + ",object=" + object + ").");\r
-        }\r
-\r
-        if(processor.isImmutable(object)) {\r
-               claim(subject, predicate, null, object);                \r
-        } else {\r
-               claim(subject, predicate, getPossibleInverse(predicate), object);               \r
-        }\r
-\r
-    }\r
-\r
-    @Override\r
-    public void claim(Resource subject, Resource predicate, Resource inverse, Resource object) throws ServiceException {\r
-        \r
-        if (MemWatch.isLowOnMemory())\r
-            writeSupport.gc();\r
-\r
-        if(subject == null) throw new ServiceException("Claim does not accept null arguments (subject).");\r
-        if(predicate == null) throw new ServiceException("Claim does not accept null arguments (predicate).");\r
-        if(object == null) throw new ServiceException("Claim does not accept null arguments (object).");\r
-        if(provider == null && subject.isPersistent() && !object.isPersistent())\r
-            throw new ServiceException("Cannot claim persistent statements where subject is persistent and object is virtual. Persistent database cannot contain statements that refer to virtual graphs.");\r
-\r
-               try {\r
-                       if(Development.DEVELOPMENT) {\r
-                               try {\r
-                                       if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG, Bindings.BOOLEAN)) {\r
-                                               String text = "claim(" + resourceName(subject) + ":" + subject + ", " + resourceName(predicate) + ":" + predicate + ", " + resourceName(object) + ":" + object + ")";\r
-                                               if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG_STACK, Bindings.BOOLEAN)) {\r
-                                                       StringWriter writer = new StringWriter();\r
-                                                       PrintWriter out = new PrintWriter(writer);\r
-                                                       new Exception(text).printStackTrace(out);\r
-                                                       Development.dispatchEvent(new ClaimEventImpl(this, provider, subject, predicate, object, writer.toString()));\r
-                                               } else {\r
-                                                       Development.dispatchEvent(new ClaimEventImpl(this, provider, subject, predicate, object, text));\r
-                                               }\r
-                                       }\r
-                                       if(Development.<Boolean>getProperty(DevelopmentKeys.WRITELOGGER_LOG, Bindings.BOOLEAN))\r
-                                               WriteLogger.logClaim(this, subject, predicate, inverse, object);\r
-                               } catch (DatabaseException e) {\r
-                                       Logger.defaultLogError(e);\r
-                               }\r
-                       }\r
-                       writeSupport.claim(provider, subject, predicate, object);\r
-               } catch(RuntimeException e) {\r
-                       throw new ServiceException(e);\r
-               }\r
-    \r
-        if(inverse == null) return;\r
-        if(subject.equals(object) && predicate.equals(inverse)) {\r
-            return;\r
-        }\r
-\r
-        // Do as claim(s,p,o) already works -\r
-        // don't write inverse relations if object is immutable.\r
-        if(processor.isImmutable(object))\r
-            return;\r
-\r
-        try {\r
-            writeSupport.claim(provider, object, inverse, subject);\r
-        } catch(RuntimeException e) {\r
-            throw new ServiceException(e);\r
-        }\r
-        \r
-    }\r
-\r
-    @Override\r
-    public void deny(Resource subject) throws ServiceException {\r
-        \r
-        assert(subject != null);\r
-\r
-        try {\r
-        \r
-            for(Statement stm : getStatements(subject, getBuiltins().IsWeaklyRelatedTo)) {\r
-                if(subject.equals(stm.getSubject())) {\r
-                    Resource inverse = getPossibleInverse(stm.getPredicate());\r
-//                    if(inverse == null) {\r
-//                        try {\r
-//                            String name = adapt(stm.getPredicate(), String.class);\r
-//                            throw new ResourceDoesNotSatisfyAssumptionException("Resource '" + name + "' does not have an inverse.");\r
-//                        } catch (AdaptionException e) {\r
-//                            throw new ResourceDoesNotSatisfyAssumptionException("Resource '" + stm.getPredicate() + "' does not have an inverse.");\r
-//                        }\r
-//                    }\r
-                    deny(stm.getSubject(), stm.getPredicate(), inverse, stm.getObject());\r
-                }\r
-            }\r
-            \r
-        } catch(DatabaseException e) {\r
-            \r
-            throw new ServiceException(INTERNAL_ERROR_STRING, e);\r
-            \r
-        }\r
-        \r
-    }\r
-\r
-    @Override\r
-    public void deny(Resource subject, Resource predicate) throws ServiceException {\r
-        \r
-        assert(subject != null);\r
-        assert(predicate != null);\r
-\r
-        try {\r
-\r
-            for (Statement stm : getStatements(subject, predicate)) {\r
-                if (subject.equals(stm.getSubject())) {\r
-                    Resource inverse = getPossibleInverse(stm.getPredicate());\r
-//                    if(inverse == null) {\r
-//                        try {\r
-//                            String name = adapt(stm.getPredicate(), String.class);\r
-//                            throw new ResourceDoesNotSatisfyAssumptionException("Resource '" + name + "' does not have an inverse.");\r
-//                        } catch (AdaptionException e) {\r
-//                            throw new ResourceDoesNotSatisfyAssumptionException("Resource '" + stm.getPredicate() + "' does not have an inverse.");\r
-//                        }\r
-//                    }\r
-                    deny(stm.getSubject(), stm.getPredicate(), inverse, stm.getObject());\r
-            }\r
-        }\r
-\r
-        } catch(DatabaseException e) {\r
-            \r
-            throw new ServiceException(INTERNAL_ERROR_STRING, e);\r
-            \r
-        }\r
-\r
-    }\r
-\r
-    @Override\r
-    public void deny(Resource subject, Resource predicate, Resource object) throws ServiceException {\r
-\r
-        assert(subject != null);\r
-        assert(predicate != null);\r
-        assert(object != null);\r
-\r
-        try { \r
-            for (Statement stm : getStatements(subject, predicate)) {\r
-                if (subject.equals(stm.getSubject()) && object.equals(stm.getObject())) {\r
-                    Resource inverse = getPossibleInverse(stm.getPredicate());\r
-                    deny(stm.getSubject(), stm.getPredicate(), inverse, stm.getObject());\r
-                }\r
-            }\r
-        } catch (DatabaseException e) {\r
-            throw new ServiceException(INTERNAL_ERROR_STRING, e);\r
-        }\r
-\r
-    }\r
-\r
-    @Override\r
-    public void denyStatement(Resource subject, Resource predicate, Resource object) throws ServiceException {\r
-        \r
-        assert(subject != null);\r
-        assert(predicate != null);\r
-        assert(object != null);\r
-        \r
-        Resource inverse = getPossibleInverse(predicate);\r
-//        if(inverse == null) {\r
-//            try {\r
-//                String name = adapt(predicate, String.class);\r
-//                throw new ResourceDoesNotSatisfyAssumptionException("Resource '" + name + "' does not have an inverse.");\r
-//            } catch (AdaptionException e) {\r
-//                throw new ResourceDoesNotSatisfyAssumptionException("Resource '" + predicate + "' does not have an inverse.");\r
-//            }\r
-//        }\r
-        deny(subject, predicate, inverse, object);\r
-        \r
-    }\r
-\r
-    @Override\r
-    public void deny(Statement statement) throws ServiceException {\r
-        assert(statement != null);\r
-        denyStatement(statement.getSubject(), statement.getPredicate(), statement.getObject());\r
-    }\r
-\r
-    @Override\r
-    public void deny(Resource subject, Resource predicate, Resource inverse, Resource object) throws ServiceException {\r
-\r
-        assert(subject != null);\r
-        assert(predicate != null);\r
-        assert(object != null);\r
-        \r
-        VirtualGraph provider = processor.getProvider(subject, predicate, object);\r
-        \r
-        deny(subject, predicate, inverse, object, provider);\r
-        \r
-    }\r
-\r
-    /*\r
-     * Note: We assume here that all inverse pairs of statements are stored in the same virtual graph.\r
-     */\r
-    @Override\r
-    public void deny(Resource subject, Resource predicate, Resource inverse, Resource object, VirtualGraph provider) throws ServiceException {\r
-        \r
-        assert(subject != null);\r
-        assert(predicate != null);\r
-        assert(object != null);\r
-\r
-               if(Development.DEVELOPMENT) {\r
-                       try {\r
-                               if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG, Bindings.BOOLEAN)) {\r
-                                       String text = "deny(" + resourceName(subject) + ":" + subject + ", " + resourceName(predicate) + ":" + predicate + ", " + resourceName(object) + ":" + object + ")";\r
-                                       if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG_STACK, Bindings.BOOLEAN)) {\r
-                                               StringWriter writer = new StringWriter();\r
-                                               PrintWriter out = new PrintWriter(writer);\r
-                                               new Exception(text).printStackTrace(out);\r
-                                               Development.dispatchEvent(new DenyEventImpl(this, provider, subject, predicate, object, writer.toString()));\r
-                                       } else {\r
-                                               Development.dispatchEvent(new DenyEventImpl(this, provider, subject, predicate, object, text));\r
-                                       }\r
-                               }\r
-                       } catch (DatabaseException e) {\r
-                               Logger.defaultLogError(e);\r
-                       }\r
-               }\r
-        \r
-        try {\r
-            writeSupport.removeStatement(provider, subject, predicate, object);\r
-        } catch(Throwable e) {\r
-            throw new InternalException("bug in deny(s,p,i,o)", e);\r
-        }\r
-    \r
-        if(inverse == null || (subject.equals(object) && predicate.equals(inverse))) return;\r
-\r
-        // don't deny inverse relations if object is immutable.\r
-        if(processor.isImmutable(object))\r
-            return;\r
-        \r
-        try {\r
-            writeSupport.removeStatement(provider, object, inverse, subject);\r
-        } catch(Throwable e) {\r
-            throw new InternalException("bug in deny(s,p,i,o)", e);\r
-        }\r
-        \r
-    }\r
-    \r
-    @Override\r
-    public void claimValue(Resource resource, Object value) throws ServiceException {\r
-        \r
-        try {\r
-            Binding b = Bindings.getBinding(value.getClass());\r
-            claimValue(resource, value, b);\r
-        } catch(BindingConstructionException e) {\r
-            throw new IllegalArgumentException(e);\r
-        }\r
-        \r
-    }\r
-\r
-    @Override\r
-    public void claimValue(Resource resource, Object value, Binding binding) throws ServiceException {\r
-        \r
-        if(value == null) throw new ServiceException("claimValue does not accept null value");\r
-        if(binding == null) throw new ServiceException("claimValue does not accept null binding");\r
-        \r
-               if(Development.DEVELOPMENT) {\r
-               try {\r
-                               if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG, Bindings.BOOLEAN)) {\r
-                                       String valueText = Literals.shortString(value.toString());\r
-                                       String text = "claimValue(" + resourceName(resource) + ", " + valueText + ")";\r
-                                       if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG_STACK, Bindings.BOOLEAN)) {\r
-                                               StringWriter writer = new StringWriter();\r
-                                               PrintWriter out = new PrintWriter(writer);\r
-                                               new Exception(text).printStackTrace(out);\r
-                                               Development.dispatchEvent(new ClaimValueEventImpl(this, provider, resource, value, binding, writer.toString()));\r
-                                       } else {\r
-                                               Development.dispatchEvent(new ClaimValueEventImpl(this, provider, resource, value, binding, text));\r
-                                       }\r
-                               }\r
-               } catch (DatabaseException e) {\r
-                               Logger.defaultLogError(e);\r
-               }\r
-               }\r
-        \r
-        try {\r
-               Serializer serializer = getSerializer(binding);\r
-            //Serializer serializer = binding.serializer();\r
-            byte[] data = serializer.serialize(value);\r
-\r
-               if(Development.DEVELOPMENT) {\r
-               try {\r
-                   if(Development.<Boolean>getProperty(DevelopmentKeys.WRITELOGGER_LOG, Bindings.BOOLEAN))\r
-                       WriteLogger.logValue(this, resource, data);\r
-               } catch (DatabaseException e) {\r
-                               Logger.defaultLogError(e);\r
-               }\r
-               }\r
-            \r
-            writeSupport.claimValue(provider, resource, data);\r
-            \r
-        } catch (DatabaseException e) {\r
-            throw new ServiceException(e);\r
-        } catch (SerializationException e) {\r
-            throw new ServiceException(e);\r
-        } catch (IOException e) {\r
-            throw new ServiceException(e);\r
-        } \r
-        \r
-    }\r
-\r
-    THashMap<Class<?>, Resource> builtinValues = new THashMap<Class<?>, Resource>(32);\r
-\r
-    private void initBuiltinValues(Layer0 b) {\r
-\r
-        if(!builtinValues.isEmpty()) return;\r
-\r
-        builtinValues.put(String.class, b.String);\r
-        builtinValues.put(Double.class, b.Double);\r
-        builtinValues.put(Float.class, b.Float);\r
-        builtinValues.put(Long.class, b.Long);\r
-        builtinValues.put(Integer.class, b.Integer);\r
-        builtinValues.put(Byte.class, b.Byte);\r
-        builtinValues.put(Boolean.class, b.Boolean);\r
-        builtinValues.put(Variant.class, b.Variant);\r
-\r
-        builtinValues.put(String[].class, b.StringArray);\r
-        builtinValues.put(double[].class, b.DoubleArray);\r
-        builtinValues.put(float[].class, b.FloatArray);\r
-        builtinValues.put(long[].class, b.LongArray);\r
-        builtinValues.put(int[].class, b.IntegerArray);\r
-        builtinValues.put(byte[].class, b.ByteArray);\r
-        builtinValues.put(boolean[].class, b.BooleanArray);\r
-\r
-        builtinValues.put(MutableString.class, b.String);\r
-        builtinValues.put(MutableDouble.class, b.Double);\r
-        builtinValues.put(MutableFloat.class, b.Float);\r
-        builtinValues.put(MutableLong.class, b.Long);\r
-        builtinValues.put(MutableInteger.class, b.Integer);\r
-        builtinValues.put(MutableByte.class, b.Byte);\r
-        builtinValues.put(MutableBoolean.class, b.Boolean);\r
-\r
-    }\r
-\r
-    @Override\r
-    public void addLiteral(Resource resource, Resource predicate, Resource inverse, Resource type, Object value, Binding binding) throws ManyObjectsForFunctionalRelationException, ServiceException {\r
-\r
-        Layer0 b = getBuiltins();\r
-        Resource valueResource = newResource();\r
-        claim(valueResource, b.InstanceOf, null, type);\r
-        claim(resource, predicate, inverse, valueResource);\r
-        claimValue(valueResource, value, binding);\r
-        \r
-    }\r
-\r
-    @Override\r
-    public void addLiteral(Resource resource, Resource predicate, Resource inverse, Object value, Binding binding) throws ManyObjectsForFunctionalRelationException, ServiceException {\r
-\r
-        Layer0 b = getBuiltins();\r
-        initBuiltinValues(b);\r
-        Resource literal = newResource();\r
-        \r
-        Class<?> clazz = value.getClass();\r
-        Resource type = builtinValues.get(clazz);\r
-        if (type == null) {\r
-            type = b.Literal;\r
-            Resource dataType = newResource();\r
-            claim(dataType, b.InstanceOf, null, b.DataType);\r
-            claimValue(dataType, binding.type(), DATA_TYPE_BINDING);\r
-            claim(literal, b.HasDataType, b.HasDataType_Inverse, dataType);\r
-        }\r
-        \r
-        claim(literal, b.InstanceOf, null, type);\r
-        claim(resource, predicate, inverse, literal);\r
-        claimValue(literal, value, binding);\r
-        \r
-    }\r
-\r
-    @Override\r
-    public <T extends Accessor> T newLiteral(Resource resource, Resource predicate, Datatype datatype, Object initialValue)\r
-    throws DatabaseException {\r
-        Layer0 b = getBuiltins();\r
-        initBuiltinValues(b);       \r
-        Resource literal = newResource();\r
-        claim(literal, b.InstanceOf, null, b.Literal);\r
-        Resource dataType = newResource();\r
-        claim(dataType, b.InstanceOf, null, b.DataType);\r
-        claimValue(dataType, datatype, DATA_TYPE_BINDING);\r
-        claim(literal, b.HasDataType, dataType);\r
-        claim(resource, predicate, literal);\r
-        return createAccessor(literal, datatype, initialValue);\r
-    }\r
-    @Override\r
-    public RandomAccessBinary createRandomAccessBinary\r
-    (Resource resource, Resource predicate, Datatype datatype, Object initiaValue)\r
-    throws DatabaseException {\r
-        Layer0 b = getBuiltins();\r
-        initBuiltinValues(b);       \r
-        Resource literal = newResource();\r
-        claim(literal, b.InstanceOf, null, b.Literal);\r
-        Resource dataType = newResource();\r
-        claim(dataType, b.InstanceOf, null, b.DataType);\r
-        claimValue(dataType, datatype, DATA_TYPE_BINDING);\r
-        claim(literal, b.HasDataType, dataType);\r
-        claim(resource, predicate, literal);\r
-        return createRandomAccessBinary(literal, datatype, initiaValue);\r
-    }\r
-    @Override\r
-    public void claimLiteral(Resource resource, Resource predicate, Object value) throws ManyObjectsForFunctionalRelationException, ServiceException {\r
-\r
-        try {\r
-            Binding binding = Bindings.getBinding(value.getClass());\r
-            claimLiteral(resource, predicate, value, binding);\r
-        } catch(BindingConstructionException e) {\r
-            throw new IllegalArgumentException(e);\r
-        } \r
-\r
-    }\r
-\r
-    @Override\r
-    public void claimLiteral(Resource resource, Resource predicate, Object value, Binding binding) throws ManyObjectsForFunctionalRelationException, ServiceException {\r
-        \r
-        Layer0 b = getBuiltins();\r
-        initBuiltinValues(b);       \r
-        \r
-        Statement literalStatement = getPossibleStatement(resource, predicate);\r
-\r
-        if(literalStatement != null && resource.equals(literalStatement.getSubject())) {\r
-\r
-            claimValue(literalStatement.getObject(), value, binding);\r
-\r
-        } else {\r
-\r
-            Class<?> clazz = value.getClass();\r
-            Resource type = builtinValues.get(clazz);\r
-            Resource literal = newResource();\r
-            if (type == null) {\r
-                type = b.Literal;\r
-                Resource dataType = newResource();\r
-                claim(dataType, b.InstanceOf, null, b.DataType);\r
-                claimValue(dataType, binding.type(), DATA_TYPE_BINDING);\r
-                claim(literal, b.HasDataType, null, dataType);\r
-            }\r
-            claim(literal, b.InstanceOf, null, type);\r
-            claimValue(literal, value, binding); \r
-            claim(resource, predicate, literal);\r
-\r
-        }\r
-\r
-        if(Development.DEVELOPMENT) {\r
-               try {\r
-                       getPossibleStatement(resource, predicate);\r
-               } catch (ManyObjectsForFunctionalRelationException e) {\r
-                       System.err.println("err " + ((ResourceImpl)resource).id + " " + ((ResourceImpl)predicate).id);\r
-                       getPossibleStatement(resource, predicate);\r
-               }\r
-        }\r
-        \r
-    }\r
-    \r
-    @Override\r
-    public void claimLiteral(Resource resource, Resource predicate, Resource type, Object value)\r
-            throws org.simantics.db.exception.BindingException, ManyObjectsForFunctionalRelationException, ServiceException {\r
-        \r
-        try {\r
-            Binding binding = Bindings.getBinding(value.getClass());\r
-            claimLiteral(resource, predicate, type, value, binding);\r
-        } catch(BindingConstructionException e) {\r
-            throw new IllegalArgumentException(e);\r
-        } \r
-        \r
-    }\r
-    \r
-    @Override\r
-    public void claimLiteral(Resource resource, Resource predicate, Resource type, Object value, Binding binding)\r
-            throws org.simantics.db.exception.BindingException, ManyObjectsForFunctionalRelationException, ServiceException {\r
-        \r
-        Layer0 b = getBuiltins();\r
-        \r
-        Statement literalStatement = getPossibleStatement(resource, predicate);\r
-\r
-        if(literalStatement != null && resource.equals(literalStatement.getSubject())) {\r
-\r
-            claimValue(literalStatement.getObject(), value, binding);\r
-\r
-        } else {\r
-\r
-            Resource literal = newResource();\r
-            claim(literal, b.InstanceOf, null, type);\r
-            claimValue(literal, value, binding); \r
-            claim(resource, predicate, literal);\r
-\r
-        }\r
-        \r
-    }\r
-    \r
-    @Override\r
-    public void claimLiteral(Resource resource, Resource predicate, Resource inverse, Resource type, Object value)\r
-            throws org.simantics.db.exception.BindingException, ManyObjectsForFunctionalRelationException, ServiceException {\r
-\r
-        try {\r
-            Binding binding = Bindings.getBinding(value.getClass());\r
-            claimLiteral(resource, predicate, inverse, type, value, binding);\r
-        } catch(BindingConstructionException e) {\r
-            throw new IllegalArgumentException(e);\r
-        } \r
-        \r
-    }\r
-    \r
-    @Override\r
-    public void claimLiteral(Resource resource, Resource predicate, Resource inverse, Resource type, Object value, Binding binding)\r
-            throws org.simantics.db.exception.BindingException, ManyObjectsForFunctionalRelationException, ServiceException {\r
-        \r
-        Layer0 b = getBuiltins();\r
-        \r
-        Statement literalStatement = getPossibleStatement(resource, predicate);\r
-\r
-        if(literalStatement != null && resource.equals(literalStatement.getSubject())) {\r
-\r
-            claimValue(literalStatement.getObject(), value, binding);\r
-\r
-        } else {\r
-\r
-            Resource literal = newResource();\r
-            claim(literal, b.InstanceOf, null, type);\r
-            claimValue(literal, value, binding); \r
-            claim(resource, predicate, inverse, literal);\r
-\r
-        }\r
-        \r
-    }\r
-\r
-\r
-    @Override\r
-    public void denyValue(Resource resource) throws ServiceException {\r
-\r
-        denyValue0(resource, null);\r
-\r
-    }\r
-\r
-    @Override\r
-    public void denyValue(Resource resource, VirtualGraph valueProvider) throws ServiceException {\r
-\r
-        denyValue0(resource, valueProvider);\r
-\r
-    }\r
-    \r
-    /**\r
-     * @param resource resource to remove value from\r
-     * @param valueProvider <code>null</code> means the caller doesn't know and\r
-     *        this method must find out\r
-     * @throws ServiceException\r
-     */\r
-    private void denyValue0(Resource resource, VirtualGraph valueProvider) throws ServiceException {\r
-\r
-       if(Development.DEVELOPMENT) {\r
-               try {\r
-                               if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG, Bindings.BOOLEAN)) {\r
-                                       Object oldValue = getPossibleValue(resource);\r
-                                       String valueText = oldValue == null ? "<no value>" : oldValue.toString();\r
-                                       String text = "denyValue(" + resourceName(resource) + ", " + valueText + ")";\r
-                                       if (oldValue != null) {\r
-                                               if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG_STACK, Bindings.BOOLEAN)) {\r
-                                                       StringWriter writer = new StringWriter();\r
-                                                       PrintWriter out = new PrintWriter(writer);\r
-                                                       new Exception(text).printStackTrace(out);\r
-                                                       Development.dispatchEvent(new DenyValueEventImpl(this, provider, resource, oldValue, writer.toString()));\r
-                                               } else {\r
-                                                       Development.dispatchEvent(new DenyValueEventImpl(this, provider, resource, oldValue, text));\r
-                                               }\r
-                                       }\r
-                               }\r
-               } catch (DatabaseException e) {\r
-                               Logger.defaultLogError(e);\r
-               }\r
-               }\r
-       \r
-        if (provider != null) {\r
-\r
-            // Can only remove from the specified VirtualGraph\r
-            if (resource.isPersistent())\r
-                throw new ArgumentException("Tried to remove literal value from persistent resource " + resource\r
-                        + " using virtual graph '" + provider.getIdentifier() + "'");\r
-            writeSupport.denyValue(provider, resource);\r
-\r
-        } else {\r
-\r
-            // Can remove from any provider\r
-            if (resource.isPersistent()) {\r
-                // A persistent resource cannot have a virtual literal\r
-                // so this should be safe.\r
-                writeSupport.denyValue(provider, resource);\r
-            } else {\r
-                // A virtual resource cannot have a persistent literal.\r
-                // Must look for a virtual graph for resource.\r
-                Layer0 L0 = getBuiltins();\r
-                if (valueProvider == null) {\r
-                    if (hasStatement(resource, L0.InstanceOf)) {\r
-                        valueProvider = processor.getProvider(resource, L0.InstanceOf);\r
-                    } else if (hasStatement(resource, L0.Inherits)) {\r
-                        valueProvider = processor.getProvider(resource, L0.Inherits);\r
-                    } else if (hasStatement(resource, L0.SubrelationOf)) {\r
-                        valueProvider = processor.getProvider(resource, L0.SubrelationOf);\r
-                    }\r
-                }\r
-                if (valueProvider != null)\r
-                    writeSupport.denyValue(valueProvider, resource);\r
-            }\r
-\r
-        }\r
-\r
-    }\r
-\r
-    @Override\r
-    public void denyValue(Resource resource, Resource predicate) throws ManyObjectsForFunctionalRelationException, ServiceException {\r
-\r
-        Statement valueStatement = getPossibleStatement(resource, predicate);\r
-\r
-        if (valueStatement != null && !valueStatement.isAsserted(resource)) {\r
-\r
-            Resource value = valueStatement.getObject();\r
-            Resource inverse = getPossibleInverse(predicate);\r
-\r
-            if (provider != null) {\r
-\r
-                // Can only remove from the specified provider\r
-                deny(resource, predicate, inverse, value, provider);\r
-                writeSupport.denyValue(provider, value);\r
-\r
-            } else {\r
-\r
-                // Can remove from any provider\r
-                VirtualGraph statementProvider = processor.getProvider(resource, valueStatement.getPredicate(), value);\r
-                deny(resource, predicate, inverse, value, statementProvider);\r
-                denyValue0(resource, statementProvider);\r
-\r
-            }\r
-\r
-        }\r
-\r
-    }\r
-\r
-    @Override\r
-    public void flushCluster() {\r
-        \r
-        writeSupport.flushCluster();\r
-        \r
-    }\r
-\r
-    @Override\r
-    public void flushCluster(Resource r) {\r
-        writeSupport.flushCluster(r);\r
-    }\r
-    \r
-    Object serialize(Object value) {\r
-        \r
-        Class<?> clazz = value.getClass();\r
-        \r
-        if(clazz.isArray()) return value;\r
-        \r
-        if(Double.class == clazz) return new double[] { (Double)value };\r
-        else if(String.class == clazz) return new String[] { (String)value };\r
-        else if(Integer.class == clazz) return new int[] { (Integer)value };\r
-        else if(Boolean.class == clazz) return new boolean[] { (Boolean)value };\r
-        else if(Long.class == clazz) return new long[] { (Long)value };\r
-        else if(Byte.class == clazz) return new byte[] { (Byte)value };\r
-        else if(Float.class == clazz) return new float[] { (Float)value };\r
-        else return value;\r
-        \r
-    }\r
-    \r
-    @Override\r
-    public <T> void addMetadata(Metadata data) throws ServiceException {\r
-        writeSupport.addMetadata(data);\r
-    }\r
-\r
-    @Override\r
-    public <T extends Metadata> T getMetadata(Class<T> clazz) throws ServiceException {\r
-        return writeSupport.getMetadata(clazz);\r
-    }\r
-\r
-    @Override\r
-    public TreeMap<String, byte[]> getMetadata() {\r
-        return writeSupport.getMetadata();\r
-    }\r
-\r
-    @Override\r
-    public String toString() {\r
-        return "WriteGraphImpl[thread=" + Thread.currentThread() + "]";\r
-    }     \r
-\r
-    public void commitAccessorChanges(WriteTraits writeTraits) {\r
-        try {\r
-            RandomAccessValueSupport ravs = getSession().peekService(RandomAccessValueSupport.class);\r
-            if (ravs == null)\r
-                return;\r
-\r
-            for(Pair<Resource, ResourceData> entry : ravs.entries()) {\r
-                ResourceData rd = entry.second;\r
-               if(!rd.isChanged()) continue;\r
-                Resource resource = entry.first;\r
-                try {\r
-                    ExternalValueSupport evs = getService(ExternalValueSupport.class);\r
-                    long vsize = rd.oldExternalValue ? evs.getValueSize(this, resource) : -1;\r
-                    long bsize = rd.binaryFile.length();\r
-                    final int N = 1<<20;\r
-                    final int LIMIT = 10 * 1000 * 1000;\r
-                    long left = bsize;\r
-                    long offset = 0;\r
-                    byte[] bytes = new byte[N];\r
-                    rd.binaryFile.reset();\r
-                    rd.binaryFile.position(0);\r
-                    int count = 0;\r
-//                    if (LIMIT < left)\r
-//                        evs.moveValue(this, resource);\r
-                    while (left > 0) {\r
-                        int length = N < left ? N : (int)left;\r
-                        rd.binaryFile.readFully(bytes, 0, length);\r
-                        evs.writeValue(this, resource, offset, length, bytes);\r
-                        offset += length;\r
-                        left -= length;\r
-                        count += length;\r
-                        if (count > LIMIT) {\r
-                            count = 0;\r
-//                            evs.commitAndContinue(this, writeTraits, resource);\r
-//                            evs.moveValue(this, resource);\r
-                            // This is needed so we don't create too many requests and run out of heap.\r
-                            evs.wait4RequestsLess(1);\r
-                        }\r
-                    }\r
-                    if (bsize < vsize) // truncate\r
-                        evs.writeValue(this, resource, bsize, 0, new byte[0]);\r
-                } catch (DatabaseException e) {\r
-                    Logger.defaultLogError(e);\r
-                }\r
-            }\r
-        } catch(IOException e) {\r
-            Logger.defaultLogError(e);\r
-        } catch (RuntimeException e) {\r
-          Logger.defaultLogError(e);\r
-        }\r
-    }\r
-\r
-    @Override\r
-    public VirtualGraph getProvider() {\r
-        return provider;\r
-    }\r
-\r
-    @Override\r
-    public void clearUndoList(WriteTraits writeTraits) {\r
-        writeSupport.clearUndoList(writeTraits);\r
-    }\r
-    \r
-    public void markUndoPoint() {\r
-       writeSupport.startUndo();\r
-    }\r
-    \r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2007, 2018 Association for Decentralized Information Management
+ * in Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+package org.simantics.db.impl.graph;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.TreeMap;
+import java.util.function.Consumer;
+
+import org.simantics.databoard.Bindings;
+import org.simantics.databoard.accessor.Accessor;
+import org.simantics.databoard.binding.Binding;
+import org.simantics.databoard.binding.error.BindingConstructionException;
+import org.simantics.databoard.binding.mutable.Variant;
+import org.simantics.databoard.primitives.MutableBoolean;
+import org.simantics.databoard.primitives.MutableByte;
+import org.simantics.databoard.primitives.MutableDouble;
+import org.simantics.databoard.primitives.MutableFloat;
+import org.simantics.databoard.primitives.MutableInteger;
+import org.simantics.databoard.primitives.MutableLong;
+import org.simantics.databoard.primitives.MutableString;
+import org.simantics.databoard.serialization.SerializationException;
+import org.simantics.databoard.serialization.Serializer;
+import org.simantics.databoard.type.Datatype;
+import org.simantics.databoard.util.binary.RandomAccessBinary;
+import org.simantics.db.DevelopmentKeys;
+import org.simantics.db.ExternalValueSupport;
+import org.simantics.db.Metadata;
+import org.simantics.db.Resource;
+import org.simantics.db.Statement;
+import org.simantics.db.VirtualGraph;
+import org.simantics.db.WriteGraph;
+import org.simantics.db.WriteOnlyGraph;
+import org.simantics.db.common.request.WriteOnlyRequest;
+import org.simantics.db.common.utils.Literals;
+import org.simantics.db.common.utils.Logger;
+import org.simantics.db.exception.ArgumentException;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.exception.InternalException;
+import org.simantics.db.exception.ManyObjectsForFunctionalRelationException;
+import org.simantics.db.exception.ServiceException;
+import org.simantics.db.impl.DatabaseUtils;
+import org.simantics.db.impl.MemWatch;
+import org.simantics.db.impl.ResourceImpl;
+import org.simantics.db.impl.internal.RandomAccessValueSupport;
+import org.simantics.db.impl.internal.ResourceData;
+import org.simantics.db.impl.query.CacheEntry;
+import org.simantics.db.impl.query.QueryProcessor;
+import org.simantics.db.impl.support.WriteRequestScheduleSupport;
+import org.simantics.db.procedure.Procedure;
+import org.simantics.db.request.DelayedWrite;
+import org.simantics.db.request.DelayedWriteResult;
+import org.simantics.db.request.Write;
+import org.simantics.db.request.WriteOnly;
+import org.simantics.db.request.WriteOnlyResult;
+import org.simantics.db.request.WriteResult;
+import org.simantics.db.request.WriteTraits;
+import org.simantics.layer0.Layer0;
+import org.simantics.utils.Development;
+import org.simantics.utils.datastructures.Pair;
+
+import gnu.trove.map.hash.THashMap;
+
+
+final public class WriteGraphImpl extends ReadGraphImpl implements WriteGraph {
+
+    final public static Binding DATA_TYPE_BINDING = Bindings.getBindingUnchecked(Datatype.class);
+    
+    final public WriteSupport writeSupport;
+    final public VirtualGraph provider;
+    
+    private String resourceName(Resource resource) throws DatabaseException {
+       if(Development.DEVELOPMENT) {
+               Boolean names = Development.getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG_NAMES, Bindings.BOOLEAN); 
+               if(names && !writeSupport.writeOnly()) return DatabaseUtils.getReadableName(this, resource);
+               else return resource.toString();
+       }
+       throw new IllegalStateException();
+    }
+    
+    private Layer0 getBuiltins() {
+        return getService(Layer0.class);
+    }
+
+    private WriteRequestScheduleSupport getWriteRequestScheduler() {
+        return (WriteRequestScheduleSupport) getSession();
+    }
+
+    private WriteGraphImpl(CacheEntry parent2, QueryProcessor readSupport,
+            WriteSupport writeSupport, VirtualGraph provider) {
+        super(parent2, readSupport);
+        this.writeSupport = writeSupport;
+        this.provider = provider;
+    }
+
+    public final static WriteGraphImpl create(QueryProcessor support, WriteSupport writeSupport, VirtualGraph provider) {
+
+       WriteGraphImpl impl = new WriteGraphImpl(null, support, writeSupport, provider); 
+        
+        try {
+                       writeSupport.setDefaultClusterSet(null);
+               } catch (ServiceException e) {
+                       Logger.defaultLogError(e);
+               }
+        
+        return impl;
+        
+    }
+
+    final public WriteGraphImpl newAsync() {
+        return this;
+    }
+    
+    public WriteGraphImpl newSync(final VirtualGraph provider) {
+        return new WriteGraphImpl(parent, processor, writeSupport, provider);
+    }
+    final public WriteGraphImpl newSync(final CacheEntry parent) {
+        return new WriteGraphImpl(parent, processor, writeSupport, provider);
+    }
+
+    @Override
+    public ReadGraphImpl newRestart(ReadGraphImpl impl) {
+
+        WriteGraphImpl write = processor.getSession().getService(WriteGraphImpl.class);
+
+        return write;
+        
+    }
+
+    @Override
+    final public Resource newResource() throws ServiceException {
+        
+        try {
+               
+               Resource result = writeSupport.createResource(provider); 
+
+               if(Development.DEVELOPMENT) {
+                       if(Development.<Boolean>getProperty(DevelopmentKeys.WRITELOGGER_LOG, Bindings.BOOLEAN))
+                       WriteLogger.logNewResource(this, result);
+               }
+               
+            return result;
+            
+        } catch (DatabaseException e) {
+            throw new ServiceException(e);
+        }
+        
+    }
+
+    @Override
+    final public Resource newResource(long clusterId) throws ServiceException {
+        
+        try {
+            
+               Resource result = writeSupport.createResource(provider, clusterId); 
+
+               if(Development.DEVELOPMENT) {
+                       if(Development.<Boolean>getProperty(DevelopmentKeys.WRITELOGGER_LOG, Bindings.BOOLEAN))
+                       WriteLogger.logNewResource(this, result);
+               }
+               
+            return result;
+            
+        } catch (DatabaseException e) {
+            throw new ServiceException(e);
+        }
+        
+    }
+
+    @Override
+    public Resource newResource(Resource clusterSet) throws ServiceException {
+        try {
+               Resource result;
+               if (provider != null)
+                       result = writeSupport.createResource(provider);
+               else
+                       result = writeSupport.createResource(provider, clusterSet); 
+            if(Development.DEVELOPMENT) {
+                if(Development.<Boolean>getProperty(DevelopmentKeys.WRITELOGGER_LOG, Bindings.BOOLEAN))
+                    WriteLogger.logNewResource(this, result);
+            }
+            return result;
+        } catch (ServiceException e) {
+            throw e;
+        } catch (DatabaseException e) {
+            throw new ServiceException(e);
+        }
+    }
+
+    @Override
+    public void newClusterSet(Resource clusterSet) throws ServiceException {
+        try {
+               if (provider == null)
+                       writeSupport.createClusterSet(provider, clusterSet); 
+            if(Development.DEVELOPMENT) {
+                if(Development.<Boolean>getProperty(DevelopmentKeys.WRITELOGGER_LOG, Bindings.BOOLEAN))
+                    WriteLogger.logNewResource(this, clusterSet);
+            }
+        } catch (ServiceException e) {
+            throw e;
+        } catch (DatabaseException e) {
+            throw new ServiceException(e);
+        }
+    }
+
+    @Override
+    public Resource setClusterSet4NewResource(Resource clusterSet)
+    throws ServiceException {
+        return writeSupport.setDefaultClusterSet(clusterSet);
+    }
+    /**
+     * Compares two object for equality, allowing null objects also.
+     * 
+     * @param o1 obj1
+     * @param o2 obj2
+     * @return true if both arguments are <code>null</code> or equal
+     */
+    static boolean safeEquals(Object o1, Object o2) {
+        if (o1 == o2)
+            return true;
+        if (o1 == null && o2 == null)
+            return true;
+        if (o1 == null || o2 == null)
+            return false;
+        return o1.equals(o2);
+    }
+
+    @Override
+    public void asyncRequest(DelayedWrite request) {
+        assert (request != null);
+        getWriteRequestScheduler().scheduleRequest(request, e -> {
+            if(e != null)
+                Logger.defaultLogError(e);
+        }, null, Boolean.TRUE);
+    }
+
+    @Override
+    public void asyncRequest(DelayedWrite request, Consumer<DatabaseException> callback) {
+        assert (request != null);
+        getWriteRequestScheduler().scheduleRequest(request, callback, null, Boolean.TRUE);
+    }
+
+    @Override
+    public <T> void asyncRequest(DelayedWriteResult<T> request, Procedure<T> procedure) {
+        assert (request != null);
+        getWriteRequestScheduler().scheduleRequest(request, procedure, null, Boolean.TRUE);
+    }
+
+    @Override
+    public void asyncRequest(final Write r) {
+        assert (r != null);
+        getWriteRequestScheduler().scheduleRequest(r, e -> {
+            if(e != null)
+                Logger.defaultLogError(e);
+        }, null, Boolean.TRUE);
+    }
+
+    @Override
+    public void asyncRequest(Write request, Consumer<DatabaseException> callback) {
+        assert (request != null);
+        getWriteRequestScheduler().scheduleRequest(request, callback, null, Boolean.TRUE);
+    }
+
+    @Override
+    public void asyncRequest(WriteOnly request) {
+        assert (request != null);
+        getWriteRequestScheduler().scheduleRequest(request, e -> {
+            if(e != null)
+                Logger.defaultLogError(e);
+        }, null, Boolean.TRUE);
+    }
+    
+    @Override
+    public void asyncRequest(WriteOnly request, Consumer<DatabaseException> callback) {
+        assert (request != null);
+        getWriteRequestScheduler().scheduleRequest(request, callback, null, Boolean.TRUE);
+    }
+
+    @Override
+    public <T> void asyncRequest(WriteOnlyResult<T> request, Procedure<T> procedure) {
+        assert (request != null);
+        getWriteRequestScheduler().scheduleRequest(request, procedure, null, Boolean.TRUE);
+    }
+
+    @Override
+    public <T> void asyncRequest(WriteResult<T> request, Procedure<T> procedure) {
+        assert (request != null);
+        getWriteRequestScheduler().scheduleRequest(request, procedure, null, Boolean.TRUE);
+    }
+
+    @Override
+    public void syncRequest(Write request) throws DatabaseException {
+        
+       Resource defaultClusterSet = setClusterSet4NewResource(null);
+       
+        WriteGraphImpl graph = (WriteGraphImpl)newSync(request.getProvider()); 
+        try {
+            writeSupport.performWriteRequest(graph, request);
+        } catch (DatabaseException e) {
+            throw e;
+        } catch (Throwable t) {
+            throw new DatabaseException(t);
+        } finally {
+            setClusterSet4NewResource(defaultClusterSet);
+        }
+        
+        
+    }
+    
+    @Override
+    public <T> T syncRequest(WriteResult<T> request) throws DatabaseException {
+
+       Resource defaultClusterSet = setClusterSet4NewResource(null);
+
+       WriteGraphImpl graph = (WriteGraphImpl)newSync(request.getProvider()); 
+        try {
+            return writeSupport.performWriteRequest(graph, request);
+        } catch (DatabaseException e) {
+            throw e;
+        } catch (Throwable t) {
+            throw new DatabaseException(t);
+        } finally {
+            setClusterSet4NewResource(defaultClusterSet);
+        }
+    }
+
+    @Override
+    public void syncRequest(final DelayedWrite request) throws DatabaseException {
+        
+        try {
+
+               final DelayedWriteGraph dwg = new DelayedWriteGraph(this);
+               request.perform(dwg);
+
+               syncRequest(new WriteOnlyRequest() {
+
+                       @Override
+                       public void perform(WriteOnlyGraph graph) throws DatabaseException {
+                               dwg.commit(graph, request);
+                       }
+
+               });
+
+        } catch (DatabaseException e) {
+               
+               throw e;
+               
+        } catch (Throwable e) {
+               
+               throw new DatabaseException(e);
+               
+        } finally {
+               
+        }
+        
+    }
+
+    @Override
+    public void syncRequest(WriteOnly request) throws DatabaseException {
+       
+       Resource defaultClusterSet = setClusterSet4NewResource(null);
+       
+        try {
+            writeSupport.performWriteRequest(this, request);
+        } catch (DatabaseException e) {
+            throw e;
+        } catch (Throwable t) {
+            throw new DatabaseException(t);
+        }  finally {
+            setClusterSet4NewResource(defaultClusterSet);
+        }
+        
+    }
+    
+    @Override
+    public void claim(Resource subject, Resource predicate, Resource object) throws ServiceException {
+
+        if(subject == null || predicate == null || object == null) {
+               throw new ServiceException("Claim does not accept null arguments (subject=" + subject + ",predicate=" + predicate + ",object=" + object + ").");
+        }
+
+        if(processor.isImmutable(object)) {
+               claim(subject, predicate, null, object);                
+        } else {
+               claim(subject, predicate, getPossibleInverse(predicate), object);               
+        }
+
+    }
+
+    @Override
+    public void claim(Resource subject, Resource predicate, Resource inverse, Resource object) throws ServiceException {
+        
+        if (MemWatch.isLowOnMemory())
+            writeSupport.gc();
+
+        if(subject == null) throw new ServiceException("Claim does not accept null arguments (subject).");
+        if(predicate == null) throw new ServiceException("Claim does not accept null arguments (predicate).");
+        if(object == null) throw new ServiceException("Claim does not accept null arguments (object).");
+        if(provider == null && subject.isPersistent() && !object.isPersistent())
+            throw new ServiceException("Cannot claim persistent statements where subject is persistent and object is virtual. Persistent database cannot contain statements that refer to virtual graphs.");
+
+               try {
+                       if(Development.DEVELOPMENT) {
+                               try {
+                                       if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG, Bindings.BOOLEAN)) {
+                                               String text = "claim(" + resourceName(subject) + ":" + subject + ", " + resourceName(predicate) + ":" + predicate + ", " + resourceName(object) + ":" + object + ")";
+                                               if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG_STACK, Bindings.BOOLEAN)) {
+                                                       StringWriter writer = new StringWriter();
+                                                       PrintWriter out = new PrintWriter(writer);
+                                                       new Exception(text).printStackTrace(out);
+                                                       Development.dispatchEvent(new ClaimEventImpl(this, provider, subject, predicate, object, writer.toString()));
+                                               } else {
+                                                       Development.dispatchEvent(new ClaimEventImpl(this, provider, subject, predicate, object, text));
+                                               }
+                                       }
+                                       if(Development.<Boolean>getProperty(DevelopmentKeys.WRITELOGGER_LOG, Bindings.BOOLEAN))
+                                               WriteLogger.logClaim(this, subject, predicate, inverse, object);
+                               } catch (DatabaseException e) {
+                                       Logger.defaultLogError(e);
+                               }
+                       }
+                       writeSupport.claim(provider, subject, predicate, object);
+               } catch(RuntimeException e) {
+                       throw new ServiceException(e);
+               }
+    
+        if(inverse == null) return;
+        if(subject.equals(object) && predicate.equals(inverse)) {
+            return;
+        }
+
+        // Do as claim(s,p,o) already works -
+        // don't write inverse relations if object is immutable.
+        if(processor.isImmutable(object))
+            return;
+
+        try {
+            writeSupport.claim(provider, object, inverse, subject);
+        } catch(RuntimeException e) {
+            throw new ServiceException(e);
+        }
+        
+    }
+
+    @Override
+    public void deny(Resource subject) throws ServiceException {
+        
+        assert(subject != null);
+
+        try {
+        
+            for(Statement stm : getStatements(subject, getBuiltins().IsWeaklyRelatedTo)) {
+                if(subject.equals(stm.getSubject())) {
+                    Resource inverse = getPossibleInverse(stm.getPredicate());
+//                    if(inverse == null) {
+//                        try {
+//                            String name = adapt(stm.getPredicate(), String.class);
+//                            throw new ResourceDoesNotSatisfyAssumptionException("Resource '" + name + "' does not have an inverse.");
+//                        } catch (AdaptionException e) {
+//                            throw new ResourceDoesNotSatisfyAssumptionException("Resource '" + stm.getPredicate() + "' does not have an inverse.");
+//                        }
+//                    }
+                    deny(stm.getSubject(), stm.getPredicate(), inverse, stm.getObject());
+                }
+            }
+            
+        } catch(DatabaseException e) {
+            
+            throw new ServiceException(INTERNAL_ERROR_STRING, e);
+            
+        }
+        
+    }
+
+    @Override
+    public void deny(Resource subject, Resource predicate) throws ServiceException {
+        
+        assert(subject != null);
+        assert(predicate != null);
+
+        try {
+
+            for (Statement stm : getStatements(subject, predicate)) {
+                if (subject.equals(stm.getSubject())) {
+                    Resource inverse = getPossibleInverse(stm.getPredicate());
+//                    if(inverse == null) {
+//                        try {
+//                            String name = adapt(stm.getPredicate(), String.class);
+//                            throw new ResourceDoesNotSatisfyAssumptionException("Resource '" + name + "' does not have an inverse.");
+//                        } catch (AdaptionException e) {
+//                            throw new ResourceDoesNotSatisfyAssumptionException("Resource '" + stm.getPredicate() + "' does not have an inverse.");
+//                        }
+//                    }
+                    deny(stm.getSubject(), stm.getPredicate(), inverse, stm.getObject());
+            }
+        }
+
+        } catch(DatabaseException e) {
+            
+            throw new ServiceException(INTERNAL_ERROR_STRING, e);
+            
+        }
+
+    }
+
+    @Override
+    public void deny(Resource subject, Resource predicate, Resource object) throws ServiceException {
+
+        assert(subject != null);
+        assert(predicate != null);
+        assert(object != null);
+
+        try { 
+            for (Statement stm : getStatements(subject, predicate)) {
+                if (subject.equals(stm.getSubject()) && object.equals(stm.getObject())) {
+                    Resource inverse = getPossibleInverse(stm.getPredicate());
+                    deny(stm.getSubject(), stm.getPredicate(), inverse, stm.getObject());
+                }
+            }
+        } catch (DatabaseException e) {
+            throw new ServiceException(INTERNAL_ERROR_STRING, e);
+        }
+
+    }
+
+    @Override
+    public void denyStatement(Resource subject, Resource predicate, Resource object) throws ServiceException {
+        
+        assert(subject != null);
+        assert(predicate != null);
+        assert(object != null);
+        
+        Resource inverse = getPossibleInverse(predicate);
+//        if(inverse == null) {
+//            try {
+//                String name = adapt(predicate, String.class);
+//                throw new ResourceDoesNotSatisfyAssumptionException("Resource '" + name + "' does not have an inverse.");
+//            } catch (AdaptionException e) {
+//                throw new ResourceDoesNotSatisfyAssumptionException("Resource '" + predicate + "' does not have an inverse.");
+//            }
+//        }
+        deny(subject, predicate, inverse, object);
+        
+    }
+
+    @Override
+    public void deny(Statement statement) throws ServiceException {
+        assert(statement != null);
+        denyStatement(statement.getSubject(), statement.getPredicate(), statement.getObject());
+    }
+
+    @Override
+    public void deny(Resource subject, Resource predicate, Resource inverse, Resource object) throws ServiceException {
+
+        assert(subject != null);
+        assert(predicate != null);
+        assert(object != null);
+        
+        VirtualGraph provider = processor.getProvider(subject, predicate, object);
+        
+        deny(subject, predicate, inverse, object, provider);
+        
+    }
+
+    /*
+     * Note: We assume here that all inverse pairs of statements are stored in the same virtual graph.
+     */
+    @Override
+    public void deny(Resource subject, Resource predicate, Resource inverse, Resource object, VirtualGraph provider) throws ServiceException {
+        
+        assert(subject != null);
+        assert(predicate != null);
+        assert(object != null);
+
+               if(Development.DEVELOPMENT) {
+                       try {
+                               if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG, Bindings.BOOLEAN)) {
+                                       String text = "deny(" + resourceName(subject) + ":" + subject + ", " + resourceName(predicate) + ":" + predicate + ", " + resourceName(object) + ":" + object + ")";
+                                       if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG_STACK, Bindings.BOOLEAN)) {
+                                               StringWriter writer = new StringWriter();
+                                               PrintWriter out = new PrintWriter(writer);
+                                               new Exception(text).printStackTrace(out);
+                                               Development.dispatchEvent(new DenyEventImpl(this, provider, subject, predicate, object, writer.toString()));
+                                       } else {
+                                               Development.dispatchEvent(new DenyEventImpl(this, provider, subject, predicate, object, text));
+                                       }
+                               }
+                       } catch (DatabaseException e) {
+                               Logger.defaultLogError(e);
+                       }
+               }
+        
+        try {
+            writeSupport.removeStatement(provider, subject, predicate, object);
+        } catch(Throwable e) {
+            throw new InternalException("bug in deny(s,p,i,o)", e);
+        }
+    
+        if(inverse == null || (subject.equals(object) && predicate.equals(inverse))) return;
+
+        // don't deny inverse relations if object is immutable.
+        if(processor.isImmutable(object))
+            return;
+        
+        try {
+            writeSupport.removeStatement(provider, object, inverse, subject);
+        } catch(Throwable e) {
+            throw new InternalException("bug in deny(s,p,i,o)", e);
+        }
+        
+    }
+    
+    @Override
+    public void claimValue(Resource resource, Object value) throws ServiceException {
+        
+        try {
+            Binding b = Bindings.getBinding(value.getClass());
+            claimValue(resource, value, b);
+        } catch(BindingConstructionException e) {
+            throw new IllegalArgumentException(e);
+        }
+        
+    }
+
+    @Override
+    public void claimValue(Resource resource, Object value, Binding binding) throws ServiceException {
+        
+        if(value == null) throw new ServiceException("claimValue does not accept null value");
+        if(binding == null) throw new ServiceException("claimValue does not accept null binding");
+        
+               if(Development.DEVELOPMENT) {
+               try {
+                               if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG, Bindings.BOOLEAN)) {
+                                       String valueText = Literals.shortString(value.toString());
+                                       String text = "claimValue(" + resourceName(resource) + ", " + valueText + ")";
+                                       if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG_STACK, Bindings.BOOLEAN)) {
+                                               StringWriter writer = new StringWriter();
+                                               PrintWriter out = new PrintWriter(writer);
+                                               new Exception(text).printStackTrace(out);
+                                               Development.dispatchEvent(new ClaimValueEventImpl(this, provider, resource, value, binding, writer.toString()));
+                                       } else {
+                                               Development.dispatchEvent(new ClaimValueEventImpl(this, provider, resource, value, binding, text));
+                                       }
+                               }
+               } catch (DatabaseException e) {
+                               Logger.defaultLogError(e);
+               }
+               }
+        
+        try {
+               Serializer serializer = getSerializer(binding);
+            //Serializer serializer = binding.serializer();
+            byte[] data = serializer.serialize(value);
+
+               if(Development.DEVELOPMENT) {
+               try {
+                   if(Development.<Boolean>getProperty(DevelopmentKeys.WRITELOGGER_LOG, Bindings.BOOLEAN))
+                       WriteLogger.logValue(this, resource, data);
+               } catch (DatabaseException e) {
+                               Logger.defaultLogError(e);
+               }
+               }
+            
+            writeSupport.claimValue(provider, resource, data);
+            
+        } catch (DatabaseException e) {
+            throw new ServiceException(e);
+        } catch (SerializationException e) {
+            throw new ServiceException(e);
+        } catch (IOException e) {
+            throw new ServiceException(e);
+        } 
+        
+    }
+
+    THashMap<Class<?>, Resource> builtinValues = new THashMap<Class<?>, Resource>(32);
+
+    private void initBuiltinValues(Layer0 b) {
+
+        if(!builtinValues.isEmpty()) return;
+
+        builtinValues.put(String.class, b.String);
+        builtinValues.put(Double.class, b.Double);
+        builtinValues.put(Float.class, b.Float);
+        builtinValues.put(Long.class, b.Long);
+        builtinValues.put(Integer.class, b.Integer);
+        builtinValues.put(Byte.class, b.Byte);
+        builtinValues.put(Boolean.class, b.Boolean);
+        builtinValues.put(Variant.class, b.Variant);
+
+        builtinValues.put(String[].class, b.StringArray);
+        builtinValues.put(double[].class, b.DoubleArray);
+        builtinValues.put(float[].class, b.FloatArray);
+        builtinValues.put(long[].class, b.LongArray);
+        builtinValues.put(int[].class, b.IntegerArray);
+        builtinValues.put(byte[].class, b.ByteArray);
+        builtinValues.put(boolean[].class, b.BooleanArray);
+
+        builtinValues.put(MutableString.class, b.String);
+        builtinValues.put(MutableDouble.class, b.Double);
+        builtinValues.put(MutableFloat.class, b.Float);
+        builtinValues.put(MutableLong.class, b.Long);
+        builtinValues.put(MutableInteger.class, b.Integer);
+        builtinValues.put(MutableByte.class, b.Byte);
+        builtinValues.put(MutableBoolean.class, b.Boolean);
+
+    }
+
+    @Override
+    public void addLiteral(Resource resource, Resource predicate, Resource inverse, Resource type, Object value, Binding binding) throws ManyObjectsForFunctionalRelationException, ServiceException {
+
+        Layer0 b = getBuiltins();
+        Resource valueResource = newResource();
+        claim(valueResource, b.InstanceOf, null, type);
+        claim(resource, predicate, inverse, valueResource);
+        claimValue(valueResource, value, binding);
+        
+    }
+
+    @Override
+    public void addLiteral(Resource resource, Resource predicate, Resource inverse, Object value, Binding binding) throws ManyObjectsForFunctionalRelationException, ServiceException {
+
+        Layer0 b = getBuiltins();
+        initBuiltinValues(b);
+        Resource literal = newResource();
+        
+        Class<?> clazz = value.getClass();
+        Resource type = builtinValues.get(clazz);
+        if (type == null) {
+            type = b.Literal;
+            Resource dataType = newResource();
+            claim(dataType, b.InstanceOf, null, b.DataType);
+            claimValue(dataType, binding.type(), DATA_TYPE_BINDING);
+            claim(literal, b.HasDataType, b.HasDataType_Inverse, dataType);
+        }
+        
+        claim(literal, b.InstanceOf, null, type);
+        claim(resource, predicate, inverse, literal);
+        claimValue(literal, value, binding);
+        
+    }
+
+    @Override
+    public <T extends Accessor> T newLiteral(Resource resource, Resource predicate, Datatype datatype, Object initialValue)
+    throws DatabaseException {
+        Layer0 b = getBuiltins();
+        initBuiltinValues(b);       
+        Resource literal = newResource();
+        claim(literal, b.InstanceOf, null, b.Literal);
+        Resource dataType = newResource();
+        claim(dataType, b.InstanceOf, null, b.DataType);
+        claimValue(dataType, datatype, DATA_TYPE_BINDING);
+        claim(literal, b.HasDataType, dataType);
+        claim(resource, predicate, literal);
+        return createAccessor(literal, datatype, initialValue);
+    }
+    @Override
+    public RandomAccessBinary createRandomAccessBinary
+    (Resource resource, Resource predicate, Datatype datatype, Object initiaValue)
+    throws DatabaseException {
+        Layer0 b = getBuiltins();
+        initBuiltinValues(b);       
+        Resource literal = newResource();
+        claim(literal, b.InstanceOf, null, b.Literal);
+        Resource dataType = newResource();
+        claim(dataType, b.InstanceOf, null, b.DataType);
+        claimValue(dataType, datatype, DATA_TYPE_BINDING);
+        claim(literal, b.HasDataType, dataType);
+        claim(resource, predicate, literal);
+        return createRandomAccessBinary(literal, datatype, initiaValue);
+    }
+    @Override
+    public void claimLiteral(Resource resource, Resource predicate, Object value) throws ManyObjectsForFunctionalRelationException, ServiceException {
+
+        try {
+            Binding binding = Bindings.getBinding(value.getClass());
+            claimLiteral(resource, predicate, value, binding);
+        } catch(BindingConstructionException e) {
+            throw new IllegalArgumentException(e);
+        } 
+
+    }
+
+    @Override
+    public void claimLiteral(Resource resource, Resource predicate, Object value, Binding binding) throws ManyObjectsForFunctionalRelationException, ServiceException {
+        
+        Layer0 b = getBuiltins();
+        initBuiltinValues(b);       
+
+        Statement literalStatement = getPossibleStatement(resource, predicate);
+
+        if(literalStatement != null && resource.equals(literalStatement.getSubject())) {
+
+            claimValue(literalStatement.getObject(), value, binding);
+
+        } else {
+
+            Class<?> clazz = value.getClass();
+            Resource type = builtinValues.get(clazz);
+            Resource literal = newResource();
+            if (type == null) {
+                type = b.Literal;
+                Resource dataType = newResource();
+                claim(dataType, b.InstanceOf, null, b.DataType);
+                claimValue(dataType, binding.type(), DATA_TYPE_BINDING);
+                claim(literal, b.HasDataType, null, dataType);
+            }
+            claim(literal, b.InstanceOf, null, type);
+            claimValue(literal, value, binding); 
+            claim(resource, predicate, literal);
+
+        }
+
+        if(Development.DEVELOPMENT) {
+               try {
+                       getPossibleStatement(resource, predicate);
+               } catch (ManyObjectsForFunctionalRelationException e) {
+                       System.err.println("err " + ((ResourceImpl)resource).id + " " + ((ResourceImpl)predicate).id);
+                       getPossibleStatement(resource, predicate);
+               }
+        }
+        
+    }
+    
+    @Override
+    public void claimLiteral(Resource resource, Resource predicate, Resource type, Object value)
+            throws org.simantics.db.exception.BindingException, ManyObjectsForFunctionalRelationException, ServiceException {
+        
+        try {
+            Binding binding = Bindings.getBinding(value.getClass());
+            claimLiteral(resource, predicate, type, value, binding);
+        } catch(BindingConstructionException e) {
+            throw new IllegalArgumentException(e);
+        } 
+        
+    }
+    
+    @Override
+    public void claimLiteral(Resource resource, Resource predicate, Resource type, Object value, Binding binding)
+            throws org.simantics.db.exception.BindingException, ManyObjectsForFunctionalRelationException, ServiceException {
+        
+        Layer0 b = getBuiltins();
+        
+        Statement literalStatement = getPossibleStatement(resource, predicate);
+
+        if(literalStatement != null && resource.equals(literalStatement.getSubject())) {
+
+            claimValue(literalStatement.getObject(), value, binding);
+
+        } else {
+
+            Resource literal = newResource();
+            claim(literal, b.InstanceOf, null, type);
+            claimValue(literal, value, binding); 
+            claim(resource, predicate, literal);
+
+        }
+        
+    }
+    
+    @Override
+    public void claimLiteral(Resource resource, Resource predicate, Resource inverse, Resource type, Object value)
+            throws org.simantics.db.exception.BindingException, ManyObjectsForFunctionalRelationException, ServiceException {
+
+        try {
+            Binding binding = Bindings.getBinding(value.getClass());
+            claimLiteral(resource, predicate, inverse, type, value, binding);
+        } catch(BindingConstructionException e) {
+            throw new IllegalArgumentException(e);
+        } 
+        
+    }
+    
+    @Override
+    public void claimLiteral(Resource resource, Resource predicate, Resource inverse, Resource type, Object value, Binding binding)
+            throws org.simantics.db.exception.BindingException, ManyObjectsForFunctionalRelationException, ServiceException {
+        
+        Layer0 b = getBuiltins();
+        
+        Statement literalStatement = getPossibleStatement(resource, predicate);
+
+        if(literalStatement != null && resource.equals(literalStatement.getSubject())) {
+
+            claimValue(literalStatement.getObject(), value, binding);
+
+        } else {
+
+            Resource literal = newResource();
+            claim(literal, b.InstanceOf, null, type);
+            claimValue(literal, value, binding); 
+            claim(resource, predicate, inverse, literal);
+
+        }
+        
+    }
+
+
+    @Override
+    public void denyValue(Resource resource) throws ServiceException {
+
+        denyValue0(resource, null);
+
+    }
+
+    @Override
+    public void denyValue(Resource resource, VirtualGraph valueProvider) throws ServiceException {
+
+        denyValue0(resource, valueProvider);
+
+    }
+    
+    /**
+     * @param resource resource to remove value from
+     * @param valueProvider <code>null</code> means the caller doesn't know and
+     *        this method must find out
+     * @throws ServiceException
+     */
+    private void denyValue0(Resource resource, VirtualGraph valueProvider) throws ServiceException {
+
+       if(Development.DEVELOPMENT) {
+               try {
+                               if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG, Bindings.BOOLEAN)) {
+                                       Object oldValue = getPossibleValue(resource);
+                                       String valueText = oldValue == null ? "<no value>" : oldValue.toString();
+                                       String text = "denyValue(" + resourceName(resource) + ", " + valueText + ")";
+                                       if (oldValue != null) {
+                                               if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG_STACK, Bindings.BOOLEAN)) {
+                                                       StringWriter writer = new StringWriter();
+                                                       PrintWriter out = new PrintWriter(writer);
+                                                       new Exception(text).printStackTrace(out);
+                                                       Development.dispatchEvent(new DenyValueEventImpl(this, provider, resource, oldValue, writer.toString()));
+                                               } else {
+                                                       Development.dispatchEvent(new DenyValueEventImpl(this, provider, resource, oldValue, text));
+                                               }
+                                       }
+                               }
+               } catch (DatabaseException e) {
+                               Logger.defaultLogError(e);
+               }
+               }
+       
+        if (provider != null) {
+
+            // Can only remove from the specified VirtualGraph
+            if (resource.isPersistent())
+                throw new ArgumentException("Tried to remove literal value from persistent resource " + resource
+                        + " using virtual graph '" + provider.getIdentifier() + "'");
+            writeSupport.denyValue(provider, resource);
+
+        } else {
+
+            // Can remove from any provider
+            if (resource.isPersistent()) {
+                // A persistent resource cannot have a virtual literal
+                // so this should be safe.
+                writeSupport.denyValue(provider, resource);
+            } else {
+                // A virtual resource cannot have a persistent literal.
+                // Must look for a virtual graph for resource.
+                Layer0 L0 = getBuiltins();
+                if (valueProvider == null) {
+                    if (hasStatement(resource, L0.InstanceOf)) {
+                        valueProvider = processor.getProvider(resource, L0.InstanceOf);
+                    } else if (hasStatement(resource, L0.Inherits)) {
+                        valueProvider = processor.getProvider(resource, L0.Inherits);
+                    } else if (hasStatement(resource, L0.SubrelationOf)) {
+                        valueProvider = processor.getProvider(resource, L0.SubrelationOf);
+                    }
+                }
+                if (valueProvider != null)
+                    writeSupport.denyValue(valueProvider, resource);
+            }
+
+        }
+
+    }
+
+    @Override
+    public void denyValue(Resource resource, Resource predicate) throws ManyObjectsForFunctionalRelationException, ServiceException {
+
+        Statement valueStatement = getPossibleStatement(resource, predicate);
+
+        if (valueStatement != null && !valueStatement.isAsserted(resource)) {
+
+            Resource value = valueStatement.getObject();
+            Resource inverse = getPossibleInverse(predicate);
+
+            if (provider != null) {
+
+                // Can only remove from the specified provider
+                deny(resource, predicate, inverse, value, provider);
+                writeSupport.denyValue(provider, value);
+
+            } else {
+
+                // Can remove from any provider
+                VirtualGraph statementProvider = processor.getProvider(resource, valueStatement.getPredicate(), value);
+                deny(resource, predicate, inverse, value, statementProvider);
+                denyValue0(resource, statementProvider);
+
+            }
+
+        }
+
+    }
+
+    @Override
+    public void flushCluster() {
+        
+        writeSupport.flushCluster();
+        
+    }
+
+    @Override
+    public void flushCluster(Resource r) {
+        writeSupport.flushCluster(r);
+    }
+    
+    Object serialize(Object value) {
+        
+        Class<?> clazz = value.getClass();
+        
+        if(clazz.isArray()) return value;
+        
+        if(Double.class == clazz) return new double[] { (Double)value };
+        else if(String.class == clazz) return new String[] { (String)value };
+        else if(Integer.class == clazz) return new int[] { (Integer)value };
+        else if(Boolean.class == clazz) return new boolean[] { (Boolean)value };
+        else if(Long.class == clazz) return new long[] { (Long)value };
+        else if(Byte.class == clazz) return new byte[] { (Byte)value };
+        else if(Float.class == clazz) return new float[] { (Float)value };
+        else return value;
+        
+    }
+    
+    @Override
+    public <T> void addMetadata(Metadata data) throws ServiceException {
+        writeSupport.addMetadata(data);
+    }
+
+    @Override
+    public <T extends Metadata> T getMetadata(Class<T> clazz) throws ServiceException {
+        return writeSupport.getMetadata(clazz);
+    }
+
+    @Override
+    public TreeMap<String, byte[]> getMetadata() {
+        return writeSupport.getMetadata();
+    }
+
+    @Override
+    public String toString() {
+        return "WriteGraphImpl[thread=" + Thread.currentThread() + "]";
+    }     
+
+    public void commitAccessorChanges(WriteTraits writeTraits) {
+        try {
+            RandomAccessValueSupport ravs = getSession().peekService(RandomAccessValueSupport.class);
+            if (ravs == null)
+                return;
+
+            for(Pair<Resource, ResourceData> entry : ravs.entries()) {
+                ResourceData rd = entry.second;
+               if(!rd.isChanged()) continue;
+                Resource resource = entry.first;
+                try {
+                    ExternalValueSupport evs = getService(ExternalValueSupport.class);
+                    long vsize = rd.oldExternalValue ? evs.getValueSize(this, resource) : -1;
+                    long bsize = rd.binaryFile.length();
+                    final int N = 1<<20;
+                    final int LIMIT = 10 * 1000 * 1000;
+                    long left = bsize;
+                    long offset = 0;
+                    byte[] bytes = new byte[N];
+                    rd.binaryFile.reset();
+                    rd.binaryFile.position(0);
+                    int count = 0;
+//                    if (LIMIT < left)
+//                        evs.moveValue(this, resource);
+                    while (left > 0) {
+                        int length = N < left ? N : (int)left;
+                        rd.binaryFile.readFully(bytes, 0, length);
+                        evs.writeValue(this, resource, offset, length, bytes);
+                        offset += length;
+                        left -= length;
+                        count += length;
+                        if (count > LIMIT) {
+                            count = 0;
+//                            evs.commitAndContinue(this, writeTraits, resource);
+//                            evs.moveValue(this, resource);
+                            // This is needed so we don't create too many requests and run out of heap.
+                            evs.wait4RequestsLess(1);
+                        }
+                    }
+                    if (bsize < vsize) // truncate
+                        evs.writeValue(this, resource, bsize, 0, new byte[0]);
+                } catch (DatabaseException e) {
+                    Logger.defaultLogError(e);
+                }
+            }
+        } catch(IOException e) {
+            Logger.defaultLogError(e);
+        } catch (RuntimeException e) {
+          Logger.defaultLogError(e);
+        }
+    }
+
+    @Override
+    public VirtualGraph getProvider() {
+        return provider;
+    }
+
+    @Override
+    public void clearUndoList(WriteTraits writeTraits) {
+        writeSupport.clearUndoList(writeTraits);
+    }
+    
+    public void markUndoPoint() {
+       writeSupport.startUndo();
+    }
+    
+}