]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.db.impl/src/org/simantics/db/impl/graph/WriteGraphImpl.java
ca485f9893957ff784075f686b87221e843fce50
[simantics/platform.git] / bundles / org.simantics.db.impl / src / org / simantics / db / impl / graph / WriteGraphImpl.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.db.impl.graph;
13
14 import java.io.IOException;
15 import java.io.PrintWriter;
16 import java.io.StringWriter;
17 import java.util.TreeMap;
18 import java.util.function.Consumer;
19
20 import org.simantics.databoard.Bindings;
21 import org.simantics.databoard.accessor.Accessor;
22 import org.simantics.databoard.binding.Binding;
23 import org.simantics.databoard.binding.error.BindingConstructionException;
24 import org.simantics.databoard.binding.mutable.Variant;
25 import org.simantics.databoard.primitives.MutableBoolean;
26 import org.simantics.databoard.primitives.MutableByte;
27 import org.simantics.databoard.primitives.MutableDouble;
28 import org.simantics.databoard.primitives.MutableFloat;
29 import org.simantics.databoard.primitives.MutableInteger;
30 import org.simantics.databoard.primitives.MutableLong;
31 import org.simantics.databoard.primitives.MutableString;
32 import org.simantics.databoard.serialization.SerializationException;
33 import org.simantics.databoard.serialization.Serializer;
34 import org.simantics.databoard.type.Datatype;
35 import org.simantics.databoard.util.binary.RandomAccessBinary;
36 import org.simantics.db.DevelopmentKeys;
37 import org.simantics.db.ExternalValueSupport;
38 import org.simantics.db.Metadata;
39 import org.simantics.db.Resource;
40 import org.simantics.db.Statement;
41 import org.simantics.db.VirtualGraph;
42 import org.simantics.db.WriteGraph;
43 import org.simantics.db.WriteOnlyGraph;
44 import org.simantics.db.common.request.WriteOnlyRequest;
45 import org.simantics.db.common.utils.Literals;
46 import org.simantics.db.common.utils.Logger;
47 import org.simantics.db.exception.ArgumentException;
48 import org.simantics.db.exception.DatabaseException;
49 import org.simantics.db.exception.InternalException;
50 import org.simantics.db.exception.ManyObjectsForFunctionalRelationException;
51 import org.simantics.db.exception.ServiceException;
52 import org.simantics.db.impl.DatabaseUtils;
53 import org.simantics.db.impl.MemWatch;
54 import org.simantics.db.impl.ResourceImpl;
55 import org.simantics.db.impl.internal.RandomAccessValueSupport;
56 import org.simantics.db.impl.internal.ResourceData;
57 import org.simantics.db.impl.query.CacheEntry;
58 import org.simantics.db.impl.query.QueryProcessor;
59 import org.simantics.db.impl.support.WriteRequestScheduleSupport;
60 import org.simantics.db.procedure.Procedure;
61 import org.simantics.db.request.DelayedWrite;
62 import org.simantics.db.request.DelayedWriteResult;
63 import org.simantics.db.request.Write;
64 import org.simantics.db.request.WriteOnly;
65 import org.simantics.db.request.WriteOnlyResult;
66 import org.simantics.db.request.WriteResult;
67 import org.simantics.db.request.WriteTraits;
68 import org.simantics.layer0.Layer0;
69 import org.simantics.utils.Development;
70 import org.simantics.utils.datastructures.Pair;
71
72 import gnu.trove.map.hash.THashMap;
73
74
75 final public class WriteGraphImpl extends ReadGraphImpl implements WriteGraph {
76
77     final public static Binding DATA_TYPE_BINDING = Bindings.getBindingUnchecked(Datatype.class);
78     
79     final public WriteSupport writeSupport;
80     final public VirtualGraph provider;
81     
82     private String resourceName(Resource resource) throws DatabaseException {
83         if(Development.DEVELOPMENT) {
84                 Boolean names = Development.getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG_NAMES, Bindings.BOOLEAN); 
85                 if(names && !writeSupport.writeOnly()) return DatabaseUtils.getReadableName(this, resource);
86                 else return resource.toString();
87         }
88         throw new IllegalStateException();
89     }
90     
91     private Layer0 getBuiltins() {
92         return getService(Layer0.class);
93     }
94
95     private WriteRequestScheduleSupport getWriteRequestScheduler() {
96         return (WriteRequestScheduleSupport) getSession();
97     }
98
99     private WriteGraphImpl(CacheEntry parent2, QueryProcessor readSupport,
100             WriteSupport writeSupport, VirtualGraph provider) {
101         super(parent2, readSupport);
102         this.writeSupport = writeSupport;
103         this.provider = provider;
104     }
105
106     public final static WriteGraphImpl create(QueryProcessor support, WriteSupport writeSupport, VirtualGraph provider) {
107
108         WriteGraphImpl impl = new WriteGraphImpl(null, support, writeSupport, provider); 
109         
110         try {
111                         writeSupport.setDefaultClusterSet(null);
112                 } catch (ServiceException e) {
113                         Logger.defaultLogError(e);
114                 }
115         
116         return impl;
117         
118     }
119
120     final public WriteGraphImpl newAsync() {
121         return this;
122     }
123     
124     public WriteGraphImpl newSync(final VirtualGraph provider) {
125         return new WriteGraphImpl(parent, processor, writeSupport, provider);
126     }
127     final public WriteGraphImpl newSync(final CacheEntry parent) {
128         return new WriteGraphImpl(parent, processor, writeSupport, provider);
129     }
130
131     @Override
132     final public ReadGraphImpl withAsyncParent(CacheEntry parent2) {
133         return new WriteGraphImpl(parent2, processor, writeSupport, provider);
134     }
135
136     @Override
137     public ReadGraphImpl newRestart(ReadGraphImpl impl) {
138
139         WriteGraphImpl write = processor.getSession().getService(WriteGraphImpl.class);
140
141         return write;
142         
143     }
144
145     @Override
146     final public Resource newResource() throws ServiceException {
147         
148         try {
149                 
150                 Resource result = writeSupport.createResource(provider); 
151
152                 if(Development.DEVELOPMENT) {
153                         if(Development.<Boolean>getProperty(DevelopmentKeys.WRITELOGGER_LOG, Bindings.BOOLEAN))
154                         WriteLogger.logNewResource(this, result);
155                 }
156                 
157             return result;
158             
159         } catch (DatabaseException e) {
160             throw new ServiceException(e);
161         }
162         
163     }
164
165     @Override
166     final public Resource newResource(long clusterId) throws ServiceException {
167         
168         try {
169             
170                 Resource result = writeSupport.createResource(provider, clusterId); 
171
172                 if(Development.DEVELOPMENT) {
173                         if(Development.<Boolean>getProperty(DevelopmentKeys.WRITELOGGER_LOG, Bindings.BOOLEAN))
174                         WriteLogger.logNewResource(this, result);
175                 }
176                 
177             return result;
178             
179         } catch (DatabaseException e) {
180             throw new ServiceException(e);
181         }
182         
183     }
184
185     @Override
186     public Resource newResource(Resource clusterSet) throws ServiceException {
187         try {
188                 Resource result;
189                 if (provider != null)
190                         result = writeSupport.createResource(provider);
191                 else
192                         result = writeSupport.createResource(provider, clusterSet); 
193             if(Development.DEVELOPMENT) {
194                 if(Development.<Boolean>getProperty(DevelopmentKeys.WRITELOGGER_LOG, Bindings.BOOLEAN))
195                     WriteLogger.logNewResource(this, result);
196             }
197             return result;
198         } catch (ServiceException e) {
199             throw e;
200         } catch (DatabaseException e) {
201             throw new ServiceException(e);
202         }
203     }
204
205     @Override
206     public void newClusterSet(Resource clusterSet) throws ServiceException {
207         try {
208                 if (provider == null)
209                         writeSupport.createClusterSet(provider, clusterSet); 
210             if(Development.DEVELOPMENT) {
211                 if(Development.<Boolean>getProperty(DevelopmentKeys.WRITELOGGER_LOG, Bindings.BOOLEAN))
212                     WriteLogger.logNewResource(this, clusterSet);
213             }
214         } catch (ServiceException e) {
215             throw e;
216         } catch (DatabaseException e) {
217             throw new ServiceException(e);
218         }
219     }
220
221     @Override
222     public Resource setClusterSet4NewResource(Resource clusterSet)
223     throws ServiceException {
224         return writeSupport.setDefaultClusterSet(clusterSet);
225     }
226     /**
227      * Compares two object for equality, allowing null objects also.
228      * 
229      * @param o1 obj1
230      * @param o2 obj2
231      * @return true if both arguments are <code>null</code> or equal
232      */
233     static boolean safeEquals(Object o1, Object o2) {
234         if (o1 == o2)
235             return true;
236         if (o1 == null && o2 == null)
237             return true;
238         if (o1 == null || o2 == null)
239             return false;
240         return o1.equals(o2);
241     }
242
243     @Override
244     public void asyncRequest(DelayedWrite request) {
245         assert (request != null);
246         getWriteRequestScheduler().scheduleRequest(request, e -> {
247             if(e != null)
248                 Logger.defaultLogError(e);
249         }, null, Boolean.TRUE);
250     }
251
252     @Override
253     public void asyncRequest(DelayedWrite request, Consumer<DatabaseException> callback) {
254         assert (request != null);
255         getWriteRequestScheduler().scheduleRequest(request, callback, null, Boolean.TRUE);
256     }
257
258     @Override
259     public <T> void asyncRequest(DelayedWriteResult<T> request, Procedure<T> procedure) {
260         assert (request != null);
261         getWriteRequestScheduler().scheduleRequest(request, procedure, null, Boolean.TRUE);
262     }
263
264     @Override
265     public void asyncRequest(final Write r) {
266         assert (r != null);
267         getWriteRequestScheduler().scheduleRequest(r, e -> {
268             if(e != null)
269                 Logger.defaultLogError(e);
270         }, null, Boolean.TRUE);
271     }
272
273     @Override
274     public void asyncRequest(Write request, Consumer<DatabaseException> callback) {
275         assert (request != null);
276         getWriteRequestScheduler().scheduleRequest(request, callback, null, Boolean.TRUE);
277     }
278
279     @Override
280     public void asyncRequest(WriteOnly request) {
281         assert (request != null);
282         getWriteRequestScheduler().scheduleRequest(request, e -> {
283             if(e != null)
284                 Logger.defaultLogError(e);
285         }, null, Boolean.TRUE);
286     }
287     
288     @Override
289     public void asyncRequest(WriteOnly request, Consumer<DatabaseException> callback) {
290         assert (request != null);
291         getWriteRequestScheduler().scheduleRequest(request, callback, null, Boolean.TRUE);
292     }
293
294     @Override
295     public <T> void asyncRequest(WriteOnlyResult<T> request, Procedure<T> procedure) {
296         assert (request != null);
297         getWriteRequestScheduler().scheduleRequest(request, procedure, null, Boolean.TRUE);
298     }
299
300     @Override
301     public <T> void asyncRequest(WriteResult<T> request, Procedure<T> procedure) {
302         assert (request != null);
303         getWriteRequestScheduler().scheduleRequest(request, procedure, null, Boolean.TRUE);
304     }
305
306     @Override
307     public void syncRequest(Write request) throws DatabaseException {
308         
309         Resource defaultClusterSet = setClusterSet4NewResource(null);
310         
311         WriteGraphImpl graph = (WriteGraphImpl)newSync(request.getProvider()); 
312         try {
313             writeSupport.performWriteRequest(graph, request);
314         } catch (DatabaseException e) {
315             throw e;
316         } catch (Throwable t) {
317             throw new DatabaseException(t);
318         } finally {
319             setClusterSet4NewResource(defaultClusterSet);
320         }
321         
322         
323     }
324     
325     @Override
326     public <T> T syncRequest(WriteResult<T> request) throws DatabaseException {
327
328         Resource defaultClusterSet = setClusterSet4NewResource(null);
329
330         WriteGraphImpl graph = (WriteGraphImpl)newSync(request.getProvider()); 
331         try {
332             return writeSupport.performWriteRequest(graph, request);
333         } catch (DatabaseException e) {
334             throw e;
335         } catch (Throwable t) {
336             throw new DatabaseException(t);
337         } finally {
338             setClusterSet4NewResource(defaultClusterSet);
339         }
340     }
341
342     @Override
343     public void syncRequest(final DelayedWrite request) throws DatabaseException {
344         
345         try {
346
347                 final DelayedWriteGraph dwg = new DelayedWriteGraph(this);
348                 request.perform(dwg);
349
350                 syncRequest(new WriteOnlyRequest() {
351
352                         @Override
353                         public void perform(WriteOnlyGraph graph) throws DatabaseException {
354                                 dwg.commit(graph, request);
355                         }
356
357                 });
358
359         } catch (DatabaseException e) {
360                 
361                 throw e;
362                 
363         } catch (Throwable e) {
364                 
365                 throw new DatabaseException(e);
366                 
367         } finally {
368                 
369         }
370         
371     }
372
373     @Override
374     public void syncRequest(WriteOnly request) throws DatabaseException {
375         
376         Resource defaultClusterSet = setClusterSet4NewResource(null);
377         
378         try {
379             writeSupport.performWriteRequest(this, request);
380         } catch (DatabaseException e) {
381             throw e;
382         } catch (Throwable t) {
383             throw new DatabaseException(t);
384         }  finally {
385             setClusterSet4NewResource(defaultClusterSet);
386         }
387         
388     }
389     
390     @Override
391     public void claim(Resource subject, Resource predicate, Resource object) throws ServiceException {
392
393         if(subject == null || predicate == null || object == null) {
394                 throw new ServiceException("Claim does not accept null arguments (subject=" + subject + ",predicate=" + predicate + ",object=" + object + ").");
395         }
396
397         if(processor.isImmutable(object)) {
398                 claim(subject, predicate, null, object);                
399         } else {
400                 claim(subject, predicate, getPossibleInverse(predicate), object);               
401         }
402
403     }
404
405     @Override
406     public void claim(Resource subject, Resource predicate, Resource inverse, Resource object) throws ServiceException {
407         
408         if (MemWatch.isLowOnMemory())
409             writeSupport.gc();
410
411         if(subject == null) throw new ServiceException("Claim does not accept null arguments (subject).");
412         if(predicate == null) throw new ServiceException("Claim does not accept null arguments (predicate).");
413         if(object == null) throw new ServiceException("Claim does not accept null arguments (object).");
414         if(provider == null && subject.isPersistent() && !object.isPersistent())
415             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.");
416
417                 try {
418                         if(Development.DEVELOPMENT) {
419                                 try {
420                                         if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG, Bindings.BOOLEAN)) {
421                                                 String text = "claim(" + resourceName(subject) + ":" + subject + ", " + resourceName(predicate) + ":" + predicate + ", " + resourceName(object) + ":" + object + ")";
422                                                 if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG_STACK, Bindings.BOOLEAN)) {
423                                                         StringWriter writer = new StringWriter();
424                                                         PrintWriter out = new PrintWriter(writer);
425                                                         new Exception(text).printStackTrace(out);
426                                                         Development.dispatchEvent(new ClaimEventImpl(this, provider, subject, predicate, object, writer.toString()));
427                                                 } else {
428                                                         Development.dispatchEvent(new ClaimEventImpl(this, provider, subject, predicate, object, text));
429                                                 }
430                                         }
431                                         if(Development.<Boolean>getProperty(DevelopmentKeys.WRITELOGGER_LOG, Bindings.BOOLEAN))
432                                                 WriteLogger.logClaim(this, subject, predicate, inverse, object);
433                                 } catch (DatabaseException e) {
434                                         Logger.defaultLogError(e);
435                                 }
436                         }
437                         writeSupport.claim(provider, subject, predicate, object);
438                 } catch(RuntimeException e) {
439                         throw new ServiceException(e);
440                 }
441     
442         if(inverse == null) return;
443         if(subject.equals(object) && predicate.equals(inverse)) {
444             return;
445         }
446
447         // Do as claim(s,p,o) already works -
448         // don't write inverse relations if object is immutable.
449         if(processor.isImmutable(object))
450             return;
451
452         try {
453             writeSupport.claim(provider, object, inverse, subject);
454         } catch(RuntimeException e) {
455             throw new ServiceException(e);
456         }
457         
458     }
459
460     @Override
461     public void deny(Resource subject) throws ServiceException {
462         
463         assert(subject != null);
464
465         try {
466         
467             for(Statement stm : getStatements(subject, getBuiltins().IsWeaklyRelatedTo)) {
468                 if(subject.equals(stm.getSubject())) {
469                     Resource inverse = getPossibleInverse(stm.getPredicate());
470 //                    if(inverse == null) {
471 //                        try {
472 //                            String name = adapt(stm.getPredicate(), String.class);
473 //                            throw new ResourceDoesNotSatisfyAssumptionException("Resource '" + name + "' does not have an inverse.");
474 //                        } catch (AdaptionException e) {
475 //                            throw new ResourceDoesNotSatisfyAssumptionException("Resource '" + stm.getPredicate() + "' does not have an inverse.");
476 //                        }
477 //                    }
478                     deny(stm.getSubject(), stm.getPredicate(), inverse, stm.getObject());
479                 }
480             }
481             
482         } catch(DatabaseException e) {
483             
484             throw new ServiceException(INTERNAL_ERROR_STRING, e);
485             
486         }
487         
488     }
489
490     @Override
491     public void deny(Resource subject, Resource predicate) throws ServiceException {
492         
493         assert(subject != null);
494         assert(predicate != null);
495
496         try {
497
498             for (Statement stm : getStatements(subject, predicate)) {
499                 if (subject.equals(stm.getSubject())) {
500                     Resource inverse = getPossibleInverse(stm.getPredicate());
501 //                    if(inverse == null) {
502 //                        try {
503 //                            String name = adapt(stm.getPredicate(), String.class);
504 //                            throw new ResourceDoesNotSatisfyAssumptionException("Resource '" + name + "' does not have an inverse.");
505 //                        } catch (AdaptionException e) {
506 //                            throw new ResourceDoesNotSatisfyAssumptionException("Resource '" + stm.getPredicate() + "' does not have an inverse.");
507 //                        }
508 //                    }
509                     deny(stm.getSubject(), stm.getPredicate(), inverse, stm.getObject());
510             }
511         }
512
513         } catch(DatabaseException e) {
514             
515             throw new ServiceException(INTERNAL_ERROR_STRING, e);
516             
517         }
518
519     }
520
521     @Override
522     public void deny(Resource subject, Resource predicate, Resource object) throws ServiceException {
523
524         assert(subject != null);
525         assert(predicate != null);
526         assert(object != null);
527
528         try { 
529             for (Statement stm : getStatements(subject, predicate)) {
530                 if (subject.equals(stm.getSubject()) && object.equals(stm.getObject())) {
531                     Resource inverse = getPossibleInverse(stm.getPredicate());
532                     deny(stm.getSubject(), stm.getPredicate(), inverse, stm.getObject());
533                 }
534             }
535         } catch (DatabaseException e) {
536             throw new ServiceException(INTERNAL_ERROR_STRING, e);
537         }
538
539     }
540
541     @Override
542     public void denyStatement(Resource subject, Resource predicate, Resource object) throws ServiceException {
543         
544         assert(subject != null);
545         assert(predicate != null);
546         assert(object != null);
547         
548         Resource inverse = getPossibleInverse(predicate);
549 //        if(inverse == null) {
550 //            try {
551 //                String name = adapt(predicate, String.class);
552 //                throw new ResourceDoesNotSatisfyAssumptionException("Resource '" + name + "' does not have an inverse.");
553 //            } catch (AdaptionException e) {
554 //                throw new ResourceDoesNotSatisfyAssumptionException("Resource '" + predicate + "' does not have an inverse.");
555 //            }
556 //        }
557         deny(subject, predicate, inverse, object);
558         
559     }
560
561     @Override
562     public void deny(Statement statement) throws ServiceException {
563         assert(statement != null);
564         denyStatement(statement.getSubject(), statement.getPredicate(), statement.getObject());
565     }
566
567     @Override
568     public void deny(Resource subject, Resource predicate, Resource inverse, Resource object) throws ServiceException {
569
570         assert(subject != null);
571         assert(predicate != null);
572         assert(object != null);
573         
574         VirtualGraph provider = processor.getProvider(subject, predicate, object);
575         
576         deny(subject, predicate, inverse, object, provider);
577         
578     }
579
580     /*
581      * Note: We assume here that all inverse pairs of statements are stored in the same virtual graph.
582      */
583     @Override
584     public void deny(Resource subject, Resource predicate, Resource inverse, Resource object, VirtualGraph provider) throws ServiceException {
585         
586         assert(subject != null);
587         assert(predicate != null);
588         assert(object != null);
589
590                 if(Development.DEVELOPMENT) {
591                         try {
592                                 if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG, Bindings.BOOLEAN)) {
593                                         String text = "deny(" + resourceName(subject) + ":" + subject + ", " + resourceName(predicate) + ":" + predicate + ", " + resourceName(object) + ":" + object + ")";
594                                         if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG_STACK, Bindings.BOOLEAN)) {
595                                                 StringWriter writer = new StringWriter();
596                                                 PrintWriter out = new PrintWriter(writer);
597                                                 new Exception(text).printStackTrace(out);
598                                                 Development.dispatchEvent(new DenyEventImpl(this, provider, subject, predicate, object, writer.toString()));
599                                         } else {
600                                                 Development.dispatchEvent(new DenyEventImpl(this, provider, subject, predicate, object, text));
601                                         }
602                                 }
603                         } catch (DatabaseException e) {
604                                 Logger.defaultLogError(e);
605                         }
606                 }
607         
608         try {
609             writeSupport.removeStatement(provider, subject, predicate, object);
610         } catch(Throwable e) {
611             throw new InternalException("bug in deny(s,p,i,o)", e);
612         }
613     
614         if(inverse == null || (subject.equals(object) && predicate.equals(inverse))) return;
615
616         // don't deny inverse relations if object is immutable.
617         if(processor.isImmutable(object))
618             return;
619         
620         try {
621             writeSupport.removeStatement(provider, object, inverse, subject);
622         } catch(Throwable e) {
623             throw new InternalException("bug in deny(s,p,i,o)", e);
624         }
625         
626     }
627     
628     @Override
629     public void claimValue(Resource resource, Object value) throws ServiceException {
630         
631         try {
632             Binding b = Bindings.getBinding(value.getClass());
633             claimValue(resource, value, b);
634         } catch(BindingConstructionException e) {
635             throw new IllegalArgumentException(e);
636         }
637         
638     }
639
640     @Override
641     public void claimValue(Resource resource, Object value, Binding binding) throws ServiceException {
642         
643         if(value == null) throw new ServiceException("claimValue does not accept null value");
644         if(binding == null) throw new ServiceException("claimValue does not accept null binding");
645         
646                 if(Development.DEVELOPMENT) {
647                 try {
648                                 if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG, Bindings.BOOLEAN)) {
649                                         String valueText = Literals.shortString(value.toString());
650                                         String text = "claimValue(" + resourceName(resource) + ", " + valueText + ")";
651                                         if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG_STACK, Bindings.BOOLEAN)) {
652                                                 StringWriter writer = new StringWriter();
653                                                 PrintWriter out = new PrintWriter(writer);
654                                                 new Exception(text).printStackTrace(out);
655                                                 Development.dispatchEvent(new ClaimValueEventImpl(this, provider, resource, value, binding, writer.toString()));
656                                         } else {
657                                                 Development.dispatchEvent(new ClaimValueEventImpl(this, provider, resource, value, binding, text));
658                                         }
659                                 }
660                 } catch (DatabaseException e) {
661                                 Logger.defaultLogError(e);
662                 }
663                 }
664         
665         try {
666                 Serializer serializer = getSerializer(binding);
667             //Serializer serializer = binding.serializer();
668             byte[] data = serializer.serialize(value);
669
670                 if(Development.DEVELOPMENT) {
671                 try {
672                     if(Development.<Boolean>getProperty(DevelopmentKeys.WRITELOGGER_LOG, Bindings.BOOLEAN))
673                         WriteLogger.logValue(this, resource, data);
674                 } catch (DatabaseException e) {
675                                 Logger.defaultLogError(e);
676                 }
677                 }
678             
679             writeSupport.claimValue(provider, resource, data);
680             
681         } catch (DatabaseException e) {
682             throw new ServiceException(e);
683         } catch (SerializationException e) {
684             throw new ServiceException(e);
685         } catch (IOException e) {
686             throw new ServiceException(e);
687         } 
688         
689     }
690
691     THashMap<Class<?>, Resource> builtinValues = new THashMap<Class<?>, Resource>(32);
692
693     private void initBuiltinValues(Layer0 b) {
694
695         if(!builtinValues.isEmpty()) return;
696
697         builtinValues.put(String.class, b.String);
698         builtinValues.put(Double.class, b.Double);
699         builtinValues.put(Float.class, b.Float);
700         builtinValues.put(Long.class, b.Long);
701         builtinValues.put(Integer.class, b.Integer);
702         builtinValues.put(Byte.class, b.Byte);
703         builtinValues.put(Boolean.class, b.Boolean);
704         builtinValues.put(Variant.class, b.Variant);
705
706         builtinValues.put(String[].class, b.StringArray);
707         builtinValues.put(double[].class, b.DoubleArray);
708         builtinValues.put(float[].class, b.FloatArray);
709         builtinValues.put(long[].class, b.LongArray);
710         builtinValues.put(int[].class, b.IntegerArray);
711         builtinValues.put(byte[].class, b.ByteArray);
712         builtinValues.put(boolean[].class, b.BooleanArray);
713
714         builtinValues.put(MutableString.class, b.String);
715         builtinValues.put(MutableDouble.class, b.Double);
716         builtinValues.put(MutableFloat.class, b.Float);
717         builtinValues.put(MutableLong.class, b.Long);
718         builtinValues.put(MutableInteger.class, b.Integer);
719         builtinValues.put(MutableByte.class, b.Byte);
720         builtinValues.put(MutableBoolean.class, b.Boolean);
721
722     }
723
724     @Override
725     public void addLiteral(Resource resource, Resource predicate, Resource inverse, Resource type, Object value, Binding binding) throws ManyObjectsForFunctionalRelationException, ServiceException {
726
727         Layer0 b = getBuiltins();
728         Resource valueResource = newResource();
729         claim(valueResource, b.InstanceOf, null, type);
730         claim(resource, predicate, inverse, valueResource);
731         claimValue(valueResource, value, binding);
732         
733     }
734
735     @Override
736     public void addLiteral(Resource resource, Resource predicate, Resource inverse, Object value, Binding binding) throws ManyObjectsForFunctionalRelationException, ServiceException {
737
738         Layer0 b = getBuiltins();
739         initBuiltinValues(b);
740         Resource literal = newResource();
741         
742         Class<?> clazz = value.getClass();
743         Resource type = builtinValues.get(clazz);
744         if (type == null) {
745             type = b.Literal;
746             Resource dataType = newResource();
747             claim(dataType, b.InstanceOf, null, b.DataType);
748             claimValue(dataType, binding.type(), DATA_TYPE_BINDING);
749             claim(literal, b.HasDataType, b.HasDataType_Inverse, dataType);
750         }
751         
752         claim(literal, b.InstanceOf, null, type);
753         claim(resource, predicate, inverse, literal);
754         claimValue(literal, value, binding);
755         
756     }
757
758     @Override
759     public <T extends Accessor> T newLiteral(Resource resource, Resource predicate, Datatype datatype, Object initialValue)
760     throws DatabaseException {
761         Layer0 b = getBuiltins();
762         initBuiltinValues(b);       
763         Resource literal = newResource();
764         claim(literal, b.InstanceOf, null, b.Literal);
765         Resource dataType = newResource();
766         claim(dataType, b.InstanceOf, null, b.DataType);
767         claimValue(dataType, datatype, DATA_TYPE_BINDING);
768         claim(literal, b.HasDataType, dataType);
769         claim(resource, predicate, literal);
770         return createAccessor(literal, datatype, initialValue);
771     }
772     @Override
773     public RandomAccessBinary createRandomAccessBinary
774     (Resource resource, Resource predicate, Datatype datatype, Object initiaValue)
775     throws DatabaseException {
776         Layer0 b = getBuiltins();
777         initBuiltinValues(b);       
778         Resource literal = newResource();
779         claim(literal, b.InstanceOf, null, b.Literal);
780         Resource dataType = newResource();
781         claim(dataType, b.InstanceOf, null, b.DataType);
782         claimValue(dataType, datatype, DATA_TYPE_BINDING);
783         claim(literal, b.HasDataType, dataType);
784         claim(resource, predicate, literal);
785         return createRandomAccessBinary(literal, datatype, initiaValue);
786     }
787     @Override
788     public void claimLiteral(Resource resource, Resource predicate, Object value) throws ManyObjectsForFunctionalRelationException, ServiceException {
789
790         try {
791             Binding binding = Bindings.getBinding(value.getClass());
792             claimLiteral(resource, predicate, value, binding);
793         } catch(BindingConstructionException e) {
794             throw new IllegalArgumentException(e);
795         } 
796
797     }
798
799     @Override
800     public void claimLiteral(Resource resource, Resource predicate, Object value, Binding binding) throws ManyObjectsForFunctionalRelationException, ServiceException {
801         
802         Layer0 b = getBuiltins();
803         initBuiltinValues(b);       
804         
805         Statement literalStatement = getPossibleStatement(resource, predicate);
806
807         if(literalStatement != null && resource.equals(literalStatement.getSubject())) {
808
809             claimValue(literalStatement.getObject(), value, binding);
810
811         } else {
812
813             Class<?> clazz = value.getClass();
814             Resource type = builtinValues.get(clazz);
815             Resource literal = newResource();
816             if (type == null) {
817                 type = b.Literal;
818                 Resource dataType = newResource();
819                 claim(dataType, b.InstanceOf, null, b.DataType);
820                 claimValue(dataType, binding.type(), DATA_TYPE_BINDING);
821                 claim(literal, b.HasDataType, null, dataType);
822             }
823             claim(literal, b.InstanceOf, null, type);
824             claimValue(literal, value, binding); 
825             claim(resource, predicate, literal);
826
827         }
828
829         if(Development.DEVELOPMENT) {
830                 try {
831                         getPossibleStatement(resource, predicate);
832                 } catch (ManyObjectsForFunctionalRelationException e) {
833                         System.err.println("err " + ((ResourceImpl)resource).id + " " + ((ResourceImpl)predicate).id);
834                         getPossibleStatement(resource, predicate);
835                 }
836         }
837         
838     }
839     
840     @Override
841     public void claimLiteral(Resource resource, Resource predicate, Resource type, Object value)
842             throws org.simantics.db.exception.BindingException, ManyObjectsForFunctionalRelationException, ServiceException {
843         
844         try {
845             Binding binding = Bindings.getBinding(value.getClass());
846             claimLiteral(resource, predicate, type, value, binding);
847         } catch(BindingConstructionException e) {
848             throw new IllegalArgumentException(e);
849         } 
850         
851     }
852     
853     @Override
854     public void claimLiteral(Resource resource, Resource predicate, Resource type, Object value, Binding binding)
855             throws org.simantics.db.exception.BindingException, ManyObjectsForFunctionalRelationException, ServiceException {
856         
857         Layer0 b = getBuiltins();
858         
859         Statement literalStatement = getPossibleStatement(resource, predicate);
860
861         if(literalStatement != null && resource.equals(literalStatement.getSubject())) {
862
863             claimValue(literalStatement.getObject(), value, binding);
864
865         } else {
866
867             Resource literal = newResource();
868             claim(literal, b.InstanceOf, null, type);
869             claimValue(literal, value, binding); 
870             claim(resource, predicate, literal);
871
872         }
873         
874     }
875     
876     @Override
877     public void claimLiteral(Resource resource, Resource predicate, Resource inverse, Resource type, Object value)
878             throws org.simantics.db.exception.BindingException, ManyObjectsForFunctionalRelationException, ServiceException {
879
880         try {
881             Binding binding = Bindings.getBinding(value.getClass());
882             claimLiteral(resource, predicate, inverse, type, value, binding);
883         } catch(BindingConstructionException e) {
884             throw new IllegalArgumentException(e);
885         } 
886         
887     }
888     
889     @Override
890     public void claimLiteral(Resource resource, Resource predicate, Resource inverse, Resource type, Object value, Binding binding)
891             throws org.simantics.db.exception.BindingException, ManyObjectsForFunctionalRelationException, ServiceException {
892         
893         Layer0 b = getBuiltins();
894         
895         Statement literalStatement = getPossibleStatement(resource, predicate);
896
897         if(literalStatement != null && resource.equals(literalStatement.getSubject())) {
898
899             claimValue(literalStatement.getObject(), value, binding);
900
901         } else {
902
903             Resource literal = newResource();
904             claim(literal, b.InstanceOf, null, type);
905             claimValue(literal, value, binding); 
906             claim(resource, predicate, inverse, literal);
907
908         }
909         
910     }
911
912
913     @Override
914     public void denyValue(Resource resource) throws ServiceException {
915
916         denyValue0(resource, null);
917
918     }
919
920     @Override
921     public void denyValue(Resource resource, VirtualGraph valueProvider) throws ServiceException {
922
923         denyValue0(resource, valueProvider);
924
925     }
926     
927     /**
928      * @param resource resource to remove value from
929      * @param valueProvider <code>null</code> means the caller doesn't know and
930      *        this method must find out
931      * @throws ServiceException
932      */
933     private void denyValue0(Resource resource, VirtualGraph valueProvider) throws ServiceException {
934
935         if(Development.DEVELOPMENT) {
936                 try {
937                                 if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG, Bindings.BOOLEAN)) {
938                                         Object oldValue = getPossibleValue(resource);
939                                         String valueText = oldValue == null ? "<no value>" : oldValue.toString();
940                                         String text = "denyValue(" + resourceName(resource) + ", " + valueText + ")";
941                                         if (oldValue != null) {
942                                                 if(Development.<Boolean>getProperty(DevelopmentKeys.WRITEGRAPH_DEBUG_STACK, Bindings.BOOLEAN)) {
943                                                         StringWriter writer = new StringWriter();
944                                                         PrintWriter out = new PrintWriter(writer);
945                                                         new Exception(text).printStackTrace(out);
946                                                         Development.dispatchEvent(new DenyValueEventImpl(this, provider, resource, oldValue, writer.toString()));
947                                                 } else {
948                                                         Development.dispatchEvent(new DenyValueEventImpl(this, provider, resource, oldValue, text));
949                                                 }
950                                         }
951                                 }
952                 } catch (DatabaseException e) {
953                                 Logger.defaultLogError(e);
954                 }
955                 }
956         
957         if (provider != null) {
958
959             // Can only remove from the specified VirtualGraph
960             if (resource.isPersistent())
961                 throw new ArgumentException("Tried to remove literal value from persistent resource " + resource
962                         + " using virtual graph '" + provider.getIdentifier() + "'");
963             writeSupport.denyValue(provider, resource);
964
965         } else {
966
967             // Can remove from any provider
968             if (resource.isPersistent()) {
969                 // A persistent resource cannot have a virtual literal
970                 // so this should be safe.
971                 writeSupport.denyValue(provider, resource);
972             } else {
973                 // A virtual resource cannot have a persistent literal.
974                 // Must look for a virtual graph for resource.
975                 Layer0 L0 = getBuiltins();
976                 if (valueProvider == null) {
977                     if (hasStatement(resource, L0.InstanceOf)) {
978                         valueProvider = processor.getProvider(resource, L0.InstanceOf);
979                     } else if (hasStatement(resource, L0.Inherits)) {
980                         valueProvider = processor.getProvider(resource, L0.Inherits);
981                     } else if (hasStatement(resource, L0.SubrelationOf)) {
982                         valueProvider = processor.getProvider(resource, L0.SubrelationOf);
983                     }
984                 }
985                 if (valueProvider != null)
986                     writeSupport.denyValue(valueProvider, resource);
987             }
988
989         }
990
991     }
992
993     @Override
994     public void denyValue(Resource resource, Resource predicate) throws ManyObjectsForFunctionalRelationException, ServiceException {
995
996         Statement valueStatement = getPossibleStatement(resource, predicate);
997
998         if (valueStatement != null && !valueStatement.isAsserted(resource)) {
999
1000             Resource value = valueStatement.getObject();
1001             Resource inverse = getPossibleInverse(predicate);
1002
1003             if (provider != null) {
1004
1005                 // Can only remove from the specified provider
1006                 deny(resource, predicate, inverse, value, provider);
1007                 writeSupport.denyValue(provider, value);
1008
1009             } else {
1010
1011                 // Can remove from any provider
1012                 VirtualGraph statementProvider = processor.getProvider(resource, valueStatement.getPredicate(), value);
1013                 deny(resource, predicate, inverse, value, statementProvider);
1014                 denyValue0(resource, statementProvider);
1015
1016             }
1017
1018         }
1019
1020     }
1021
1022     @Override
1023     public void flushCluster() {
1024         
1025         writeSupport.flushCluster();
1026         
1027     }
1028
1029     @Override
1030     public void flushCluster(Resource r) {
1031         writeSupport.flushCluster(r);
1032     }
1033     
1034     Object serialize(Object value) {
1035         
1036         Class<?> clazz = value.getClass();
1037         
1038         if(clazz.isArray()) return value;
1039         
1040         if(Double.class == clazz) return new double[] { (Double)value };
1041         else if(String.class == clazz) return new String[] { (String)value };
1042         else if(Integer.class == clazz) return new int[] { (Integer)value };
1043         else if(Boolean.class == clazz) return new boolean[] { (Boolean)value };
1044         else if(Long.class == clazz) return new long[] { (Long)value };
1045         else if(Byte.class == clazz) return new byte[] { (Byte)value };
1046         else if(Float.class == clazz) return new float[] { (Float)value };
1047         else return value;
1048         
1049     }
1050     
1051     @Override
1052     public <T> void addMetadata(Metadata data) throws ServiceException {
1053         writeSupport.addMetadata(data);
1054     }
1055
1056     @Override
1057     public <T extends Metadata> T getMetadata(Class<T> clazz) throws ServiceException {
1058         return writeSupport.getMetadata(clazz);
1059     }
1060
1061     @Override
1062     public TreeMap<String, byte[]> getMetadata() {
1063         return writeSupport.getMetadata();
1064     }
1065
1066     @Override
1067     public String toString() {
1068         return "WriteGraphImpl[thread=" + Thread.currentThread() + "]";
1069     }     
1070
1071     public void commitAccessorChanges(WriteTraits writeTraits) {
1072         try {
1073             RandomAccessValueSupport ravs = getSession().peekService(RandomAccessValueSupport.class);
1074             if (ravs == null)
1075                 return;
1076
1077             for(Pair<Resource, ResourceData> entry : ravs.entries()) {
1078                 ResourceData rd = entry.second;
1079                 if(!rd.isChanged()) continue;
1080                 Resource resource = entry.first;
1081                 try {
1082                     ExternalValueSupport evs = getService(ExternalValueSupport.class);
1083                     long vsize = rd.oldExternalValue ? evs.getValueSize(this, resource) : -1;
1084                     long bsize = rd.binaryFile.length();
1085                     final int N = 1<<20;
1086                     final int LIMIT = 10 * 1000 * 1000;
1087                     long left = bsize;
1088                     long offset = 0;
1089                     byte[] bytes = new byte[N];
1090                     rd.binaryFile.reset();
1091                     rd.binaryFile.position(0);
1092                     int count = 0;
1093 //                    if (LIMIT < left)
1094 //                        evs.moveValue(this, resource);
1095                     while (left > 0) {
1096                         int length = N < left ? N : (int)left;
1097                         rd.binaryFile.readFully(bytes, 0, length);
1098                         evs.writeValue(this, resource, offset, length, bytes);
1099                         offset += length;
1100                         left -= length;
1101                         count += length;
1102                         if (count > LIMIT) {
1103                             count = 0;
1104 //                            evs.commitAndContinue(this, writeTraits, resource);
1105 //                            evs.moveValue(this, resource);
1106                             // This is needed so we don't create too many requests and run out of heap.
1107                             evs.wait4RequestsLess(1);
1108                         }
1109                     }
1110                     if (bsize < vsize) // truncate
1111                         evs.writeValue(this, resource, bsize, 0, new byte[0]);
1112                 } catch (DatabaseException e) {
1113                     Logger.defaultLogError(e);
1114                 }
1115             }
1116         } catch(IOException e) {
1117             Logger.defaultLogError(e);
1118         } catch (RuntimeException e) {
1119           Logger.defaultLogError(e);
1120         }
1121     }
1122
1123     @Override
1124     public VirtualGraph getProvider() {
1125         return provider;
1126     }
1127
1128     @Override
1129     public void clearUndoList(WriteTraits writeTraits) {
1130         writeSupport.clearUndoList(writeTraits);
1131     }
1132     
1133     public void markUndoPoint() {
1134         writeSupport.startUndo();
1135     }
1136     
1137 }