X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=blobdiff_plain;f=bundles%2Forg.simantics.db.impl%2Fsrc%2Forg%2Fsimantics%2Fdb%2Fimpl%2Fgraph%2FWriteGraphImpl.java;h=56fb9ec72f977e967b6a3db6a9366d4d20433657;hp=f560008e3d009639652585626bd46a90879589a6;hb=9f0fd59be54719b1fe9322d8fd37e4950857308c;hpb=969bd23cab98a79ca9101af33334000879fb60c5 diff --git a/bundles/org.simantics.db.impl/src/org/simantics/db/impl/graph/WriteGraphImpl.java b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/graph/WriteGraphImpl.java index f560008e3..56fb9ec72 100644 --- a/bundles/org.simantics.db.impl/src/org/simantics/db/impl/graph/WriteGraphImpl.java +++ b/bundles/org.simantics.db.impl/src/org/simantics/db/impl/graph/WriteGraphImpl.java @@ -1,1152 +1,1132 @@ -/******************************************************************************* - * Copyright (c) 2007, 2010 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 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.Callback; -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 - final public ReadGraphImpl withAsyncParent(CacheEntry parent2) { - return new WriteGraphImpl(parent2, 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.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.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.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.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 null 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, new Callback() { - - @Override - public void run(DatabaseException parameter) { - if(parameter != null) - Logger.defaultLogError(parameter); - } - - }, null, Boolean.TRUE); - } - - @Override - public void asyncRequest(DelayedWrite request, Callback callback) { - assert (request != null); - getWriteRequestScheduler().scheduleRequest(request, callback, null, Boolean.TRUE); - } - - @Override - public void asyncRequest(DelayedWriteResult request, Procedure procedure) { - assert (request != null); - getWriteRequestScheduler().scheduleRequest(request, procedure, null, Boolean.TRUE); - } - - @Override - public void asyncRequest(final Write r) { - assert (r != null); - getWriteRequestScheduler().scheduleRequest(r, new Callback() { - - @Override - public void run(DatabaseException parameter) { - if(parameter != null) - Logger.defaultLogError(parameter); - } - - }, null, Boolean.TRUE); - } - - @Override - public void asyncRequest(Write request, Callback callback) { - assert (request != null); - getWriteRequestScheduler().scheduleRequest(request, callback, null, Boolean.TRUE); - } - - @Override - public void asyncRequest(WriteOnly request) { - assert (request != null); - getWriteRequestScheduler().scheduleRequest(request, new Callback() { - - @Override - public void run(DatabaseException parameter) { - if(parameter != null) - Logger.defaultLogError(parameter); - } - - }, null, Boolean.TRUE); - } - - @Override - public void asyncRequest(WriteOnly request, Callback callback) { - assert (request != null); - getWriteRequestScheduler().scheduleRequest(request, callback, null, Boolean.TRUE); - } - - @Override - public void asyncRequest(WriteOnlyResult request, Procedure procedure) { - assert (request != null); - getWriteRequestScheduler().scheduleRequest(request, procedure, null, Boolean.TRUE); - } - - @Override - public void asyncRequest(WriteResult request, Procedure 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 syncRequest(WriteResult 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.getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG, Bindings.BOOLEAN)) { - String text = "claim(" + resourceName(subject) + ":" + subject + ", " + resourceName(predicate) + ":" + predicate + ", " + resourceName(object) + ":" + object + ")"; - if(Development.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.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.getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG, Bindings.BOOLEAN)) { - String text = "deny(" + resourceName(subject) + ":" + subject + ", " + resourceName(predicate) + ":" + predicate + ", " + resourceName(object) + ":" + object + ")"; - if(Development.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.getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG, Bindings.BOOLEAN)) { - String valueText = Literals.shortString(value.toString()); - String text = "claimValue(" + resourceName(resource) + ", " + valueText + ")"; - if(Development.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.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, Resource> builtinValues = new THashMap, 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 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 null 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.getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG, Bindings.BOOLEAN)) { - Object oldValue = getPossibleValue(resource); - String valueText = oldValue == null ? "" : oldValue.toString(); - String text = "denyValue(" + resourceName(resource) + ", " + valueText + ")"; - if (oldValue != null) { - if(Development.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 void addMetadata(Metadata data) throws ServiceException { - writeSupport.addMetadata(data); - } - - @Override - public T getMetadata(Class clazz) throws ServiceException { - return writeSupport.getMetadata(clazz); - } - - @Override - public TreeMap 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 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(); - } - -} +/******************************************************************************* + * 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.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.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.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.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 null 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 callback) { + assert (request != null); + getWriteRequestScheduler().scheduleRequest(request, callback, null, Boolean.TRUE); + } + + @Override + public void asyncRequest(DelayedWriteResult request, Procedure 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 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 callback) { + assert (request != null); + getWriteRequestScheduler().scheduleRequest(request, callback, null, Boolean.TRUE); + } + + @Override + public void asyncRequest(WriteOnlyResult request, Procedure procedure) { + assert (request != null); + getWriteRequestScheduler().scheduleRequest(request, procedure, null, Boolean.TRUE); + } + + @Override + public void asyncRequest(WriteResult request, Procedure 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 syncRequest(WriteResult 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.getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG, Bindings.BOOLEAN)) { + String text = "claim(" + resourceName(subject) + ":" + subject + ", " + resourceName(predicate) + ":" + predicate + ", " + resourceName(object) + ":" + object + ")"; + if(Development.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.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.getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG, Bindings.BOOLEAN)) { + String text = "deny(" + resourceName(subject) + ":" + subject + ", " + resourceName(predicate) + ":" + predicate + ", " + resourceName(object) + ":" + object + ")"; + if(Development.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.getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG, Bindings.BOOLEAN)) { + String valueText = Literals.shortString(value.toString()); + String text = "claimValue(" + resourceName(resource) + ", " + valueText + ")"; + if(Development.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.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, Resource> builtinValues = new THashMap, 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 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 null 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.getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG, Bindings.BOOLEAN)) { + Object oldValue = getPossibleValue(resource); + String valueText = oldValue == null ? "" : oldValue.toString(); + String text = "denyValue(" + resourceName(resource) + ", " + valueText + ")"; + if (oldValue != null) { + if(Development.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 void addMetadata(Metadata data) throws ServiceException { + writeSupport.addMetadata(data); + } + + @Override + public T getMetadata(Class clazz) throws ServiceException { + return writeSupport.getMetadata(clazz); + } + + @Override + public TreeMap 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 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(); + } + +}