dec1f5396582ce29522d464a14202c18161ba20b
[simantics/platform.git] / bundles / org.simantics.db.impl / src / org / simantics / db / impl / graph / DelayedWriteGraph.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
15 import java.io.DataInput;
16 import java.io.DataOutput;
17 import java.io.File;
18 import java.io.FileInputStream;
19 import java.io.FileOutputStream;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.io.OutputStream;
23 import java.nio.ByteBuffer;
24 import java.nio.channels.FileChannel;
25 import java.util.ArrayList;
26 import java.util.HashSet;
27 import java.util.IdentityHashMap;
28 import java.util.List;
29 import java.util.Set;
30 import java.util.TreeMap;
31 import java.util.UUID;
32
33 import org.eclipse.core.runtime.Platform;
34 import org.simantics.databoard.Bindings;
35 import org.simantics.databoard.accessor.Accessor;
36 import org.simantics.databoard.accessor.reference.ChildReference;
37 import org.simantics.databoard.binding.Binding;
38 import org.simantics.databoard.binding.error.BindingConstructionException;
39 import org.simantics.databoard.binding.impl.BindingPrintContext;
40 import org.simantics.databoard.serialization.SerializationException;
41 import org.simantics.databoard.serialization.Serializer;
42 import org.simantics.databoard.type.Datatype;
43 import org.simantics.databoard.util.IdentityPair;
44 import org.simantics.databoard.util.binary.RandomAccessBinary;
45 import org.simantics.db.Metadata;
46 import org.simantics.db.ReadGraph;
47 import org.simantics.db.Resource;
48 import org.simantics.db.Session;
49 import org.simantics.db.Statement;
50 import org.simantics.db.VirtualGraph;
51 import org.simantics.db.WriteGraph;
52 import org.simantics.db.WriteOnlyGraph;
53 import org.simantics.db.common.MetadataUtils;
54 import org.simantics.db.common.request.WriteOnlyRequest;
55 import org.simantics.db.common.utils.Logger;
56 import org.simantics.db.exception.BindingException;
57 import org.simantics.db.exception.ClusterSetExistException;
58 import org.simantics.db.exception.DatabaseException;
59 import org.simantics.db.exception.ManyObjectsForFunctionalRelationException;
60 import org.simantics.db.exception.NoSingleResultException;
61 import org.simantics.db.exception.ResourceNotFoundException;
62 import org.simantics.db.exception.ServiceException;
63 import org.simantics.db.impl.DebugPolicy;
64 import org.simantics.db.impl.ResourceImpl;
65 import org.simantics.db.request.DelayedWrite;
66 import org.simantics.db.request.WriteOnly;
67 import org.simantics.db.request.WriteResult;
68 import org.simantics.db.request.WriteTraits;
69 import org.simantics.db.service.ByteReader;
70 import org.simantics.db.service.ClusteringSupport;
71 import org.simantics.db.service.TransferableGraphSupport;
72 import org.simantics.db.service.XSupport;
73 import org.simantics.layer0.Layer0;
74 import org.simantics.utils.datastructures.MapList;
75
76 import gnu.trove.list.array.TIntArrayList;
77 import gnu.trove.map.hash.TIntIntHashMap;
78 import gnu.trove.map.hash.TObjectIntHashMap;
79
80 /**
81  * Write graph implementation that does not modify the database
82  * immediately but with an explicit commit method. All read operations
83  * return results based on the old graph. 
84  */
85 public class DelayedWriteGraph extends ReadGraphImpl implements WriteGraph, ByteReader {
86         
87         private static final boolean DEBUG = false;
88         public static int BUFFER = 65536;
89
90         static class Haxx extends Binding {
91
92                 static final Serializer serializer = new Serializer() {
93                         
94                         public byte[] serialize(Object obj) throws SerializationException {
95                                 return (byte[])obj;
96                         }
97
98                         @Override
99                         public void serialize(DataOutput out,
100                                         TObjectIntHashMap<Object> identities, Object obj)
101                                         throws IOException {
102                                 throw new Error("Not supported.");
103                         }
104
105                         @Override
106                         public void serialize(DataOutput out, Object obj)
107                                         throws IOException {
108                                 throw new Error("Not supported.");
109                         }
110
111                         @Override
112                         public Object deserialize(DataInput in, List<Object> identities)
113                                         throws IOException {
114                                 throw new Error("Not supported.");
115                         }
116
117                         @Override
118                         public Object deserialize(DataInput in) throws IOException {
119                                 throw new Error("Not supported.");
120                         }
121
122                         @Override
123                         public void deserializeTo(DataInput in, List<Object> identities,
124                                         Object obj) throws IOException {
125                                 throw new Error("Not supported.");
126                         }
127
128                         @Override
129                         public void deserializeTo(DataInput in, Object obj)
130                                         throws IOException {
131                                 throw new Error("Not supported.");
132                         }
133
134                         @Override
135                         public void skip(DataInput in, List<Object> identities)
136                                         throws IOException {
137                                 throw new Error("Not supported.");
138                         }
139
140                         @Override
141                         public void skip(DataInput in) throws IOException {
142                                 throw new Error("Not supported.");
143                         }
144
145                         @Override
146                         public Integer getConstantSize() {
147                                 throw new Error("Not supported.");
148                         }
149
150                         @Override
151                         public int getSize(Object obj, TObjectIntHashMap<Object> identities)
152                                         throws IOException {
153                                 throw new Error("Not supported.");
154                         }
155
156                         @Override
157                         public int getSize(Object obj) throws IOException {
158                                 throw new Error("Not supported.");
159                         }
160
161                         @Override
162                         public int getMinSize() {
163                                 throw new Error("Not supported.");
164                         }
165
166                 };
167                 
168                 @Override
169                 public Serializer serializer() {
170                         return serializer;
171                 }
172                 
173                 @Override
174                 public void accept(Visitor1 v, Object obj) {
175                         throw new Error("Not supported.");
176                 }
177
178                 @Override
179                 public <T> T accept(Visitor<T> v) {
180                         throw new Error("Not supported.");
181                 }
182
183                 @Override
184                 public boolean isInstance(Object obj) {
185                         throw new Error("Not supported.");
186                 }
187
188                 @Override
189                 public void assertInstaceIsValid(Object obj, Set<Object> validInstances)
190                                 throws org.simantics.databoard.binding.error.BindingException {
191                         throw new Error("Not supported.");
192                 }
193
194                 @Override
195                 public int deepHashValue(Object value,
196                                 IdentityHashMap<Object, Object> hashedObjects)
197                                 throws org.simantics.databoard.binding.error.BindingException {
198                         throw new Error("Not supported.");
199                 }
200
201                 @Override
202                 public int deepCompare(Object o1, Object o2,
203                                 Set<IdentityPair<Object, Object>> compareHistory)
204                                 throws org.simantics.databoard.binding.error.BindingException {
205                         throw new Error("Not supported.");
206                 }
207                 
208                 @Override
209                 public void readFrom(Binding srcBinding, Object src, Object dst)
210                                 throws org.simantics.databoard.binding.error.BindingException {
211                         throw new Error("Not supported.");
212                 }
213                 
214                 @Override
215                 public Object readFromTry(Binding srcBinding, Object src, Object dst)
216                                 throws org.simantics.databoard.binding.error.BindingException {
217                         throw new Error("Not supported.");
218                 }
219                 
220                 @Override
221                 protected void toString(Object value, BindingPrintContext ctx) throws org.simantics.databoard.binding.error.BindingException {
222                         throw new Error("Not supported.");
223                 }
224
225                 @Override
226                 public int getComponentCount() {
227                         throw new Error("Not supported.");
228                 }
229
230                 @Override
231                 public Binding getComponentBinding(int index) {
232                         throw new Error("Not supported.");
233                 }
234
235                 @Override
236                 public Binding getComponentBinding(ChildReference path) {
237                         throw new Error("Not supported.");
238                 }
239                 
240         }
241         
242         private static final Haxx haxx = new Haxx();
243
244         private final int TERM = 0;
245         private final int CLAIM = 1;
246     private final int CLAIM_NOINVERSE = 2;
247         private final int CLAIM_VALUE_B = 4;
248     private final int DENY = 5;
249     private final int DENY_VALUE = 6;
250     private final int COMMIT_AND_CONTINUE = 7;
251         
252     static class ClusterSet {
253         public Resource resource;
254         ClusterSet() {
255             ids = new TIntArrayList();
256             old = false;
257         }
258         ClusterSet(boolean old, Resource r) {
259             ids = new TIntArrayList();
260             this.old = old;
261             this.resource = r;
262         }
263         void add(int id) {
264             ids.add(id);
265         }
266         private TIntArrayList ids;
267         private final boolean old; // true if
268         boolean isNew() {
269             return !old;
270         }
271     }
272     public class State {
273         public File tempFile;
274         public FileOutputStream out;
275         public ArrayList<Resource> idToResource = new ArrayList<Resource>();
276         public TIntIntHashMap externalToId = new TIntIntHashMap(); 
277         public ArrayList<Binding> idToBinding = new ArrayList<Binding>();
278         public TObjectIntHashMap<Binding> bindingToId = new TObjectIntHashMap<Binding>();
279         public Set<Resource> clusterSets = new HashSet<Resource>();
280         public Set<Resource> clusterSetsForExistingResources = new HashSet<Resource>();
281         public int clusterCount = 0;
282         public long defaultCluster;
283         public Resource defaultClusterSet;
284         public int statementCount = 0;
285         public int valueCount = 0;
286         public int fileCount = 0;
287     }
288     
289     public State writeState;
290     public TreeMap<String,byte[]> metadata = new TreeMap<String,byte[]>();
291         
292     Layer0 b;
293     Session session;
294     public static Resource convertDelayedResource(Resource r) {
295         if (r instanceof InternalResource) {
296             InternalResource ri = (InternalResource)r;
297             return ri.resource;
298         }
299         return r;
300     }
301     private static class InternalResource implements Resource {
302
303         int id;
304         long clusterId = 0;
305         Resource resource = null;
306         Resource clusterSet = null;
307         
308         public InternalResource(int id, long clusterId) {
309             this.id = id;
310             this.clusterId = clusterId;
311         }
312
313         public InternalResource(int id, Resource clusterSet) {
314             this.id = id;
315             this.clusterSet = clusterSet;
316         }
317         
318         @Override
319         public long getResourceId() {
320             throw new UnsupportedOperationException();
321         }
322
323         @Override
324         public Resource get() {
325             return this;
326         }
327         
328         @Override
329         public boolean isPersistent() {
330             return false;
331         }
332
333         @Override
334         public int compareTo(Resource o) {
335                 if(o instanceof InternalResource) {
336                         return Integer.compare(id, ((InternalResource)o).id);
337                 } else {
338                         return -1;
339                 }
340         }
341
342         @Override
343         public int hashCode() {
344             final int prime = 31;
345             int result = 1;
346             result = prime * result + id;
347             return result;
348         }
349         
350         @Override
351         public int getThreadHash() {
352             return hashCode();
353         }
354
355         @Override
356         public boolean equals(Object obj) {
357             if (this == obj)
358                 return true;
359             if (obj == null)
360                 return false;
361             if (!(obj instanceof InternalResource))
362                 return false;
363             InternalResource other = (InternalResource) obj;
364             if (id != other.id)
365                 return false;
366             return true;
367         }
368         
369
370         @Override
371         public boolean equalsResource(Resource other) {
372             return equals(other);
373         }
374         @Override
375         public String toString() {
376             StringBuilder sb = new StringBuilder(32);
377             if(DebugPolicy.VERBOSE) {
378                 sb.append("[delayed id=");
379                 sb.append(id);
380                 sb.append("]");
381             } else {
382                 sb.append("[did=");
383                 sb.append(id);
384                 sb.append("]");
385             }
386             return sb.toString();
387         }
388     }
389
390         private int getId(Resource resource) {
391                 if(resource instanceof InternalResource)
392                         return ((InternalResource)resource).id;
393                 else {
394                         ResourceImpl r = (ResourceImpl)resource;
395                         int id = writeState.externalToId.get(r.id);
396                         if(id != 0) {
397                                 return id;
398                         } else {
399                                 id = writeState.idToResource.size();
400                                 writeState.idToResource.add(resource);
401                                 writeState.externalToId.put(r.id, id);
402                                 return id;
403                         }
404                 }
405         }
406         
407         private Resource getResource(int id) {
408                 return writeState.idToResource.get(id);
409         }
410         
411         private int getBindingId(Binding binding) {
412                 if(writeState.bindingToId.contains(binding))
413                         return writeState.bindingToId.get(binding);
414                 else {
415                         int id = writeState.idToBinding.size();
416                         writeState.idToBinding.add(binding);
417                         writeState.bindingToId.put(binding, id);
418                         return id;
419                 }
420         }
421
422         public DelayedWriteGraph(ReadGraph g) throws IOException {
423                 super((ReadGraphImpl)g);
424                 writeState = new State();
425                 session = g.getSession();
426                 b = Layer0.getInstance(g);
427                 writeState.defaultCluster = newCluster();
428         }       
429         
430     public DelayedWriteGraph(ReadGraph g, State state) {
431         super((ReadGraphImpl)g);
432         session = g.getSession();
433                 b = Layer0.getInstance(g);
434         this.writeState = state;
435     }   
436
437         public DelayedWriteGraph newSync() {
438         return new DelayedWriteGraph(this, writeState);
439     }
440         
441         @Override
442         public void claim(Resource subject, Resource predicate, Resource object)
443                         throws ServiceException {
444                 assert(subject != null);
445         assert(predicate != null);
446         assert(object != null);
447         
448         Resource inverse = getPossibleInverse(predicate);
449         claim(subject, predicate, inverse, object);
450         }
451
452     @Override
453     public void addLiteral(Resource resource, Resource predicate, Resource inverse, Resource type, Object value,
454             Binding binding) throws BindingException,
455             ManyObjectsForFunctionalRelationException, ServiceException {
456         Resource valueResource = newResource();
457         claimValue(valueResource, value, binding);
458         claim(valueResource, b.InstanceOf, null, type);
459         claim(resource, predicate, inverse, valueResource);  
460     }
461     
462     @Override
463     public void addLiteral(Resource resource, Resource predicate,
464                 Resource inverse, Object value, Binding binding)
465                 throws BindingException, ManyObjectsForFunctionalRelationException,
466                 ServiceException {
467         
468         Resource type = getType(value);
469         if(type == null) {
470             Resource literal = newResource();
471             type = b.Literal;
472             Resource dataType = newResource();
473             claim(dataType, b.InstanceOf, null, b.DataType);
474             claimValue(dataType, binding.type(), DATA_TYPE_BINDING_INTERNAL);
475             claim(literal, b.HasDataType, null, dataType);
476             claim(literal, b.InstanceOf, null, type);
477             claimValue(literal, value, binding); 
478             claim(resource, predicate, inverse, literal);
479         } else {
480                 addLiteral(resource, predicate, inverse, type, value, binding);
481         }
482         
483     }
484
485     @Override
486     public <T extends Accessor> T newLiteral(Resource resource, Resource predicate, Datatype datatype, Object initialValue)
487     throws DatabaseException {
488
489         throw new UnsupportedOperationException();
490
491     }
492
493     @Override
494     public RandomAccessBinary createRandomAccessBinary (Resource resource, Resource predicate, Datatype datatype, Object initialValue) throws DatabaseException {
495         throw new UnsupportedOperationException();
496     }
497     
498     @Override
499     public RandomAccessBinary createRandomAccessBinary(Resource resource, Datatype datatype, Object initialValue) throws DatabaseException {
500         throw new UnsupportedOperationException();
501     }
502
503     @Override
504     public void claimLiteral(Resource resource, Resource predicate, Object value) throws ManyObjectsForFunctionalRelationException, ServiceException {
505
506                 try {
507                         Binding b = Bindings.getBinding(value.getClass());
508                 claimLiteral(resource, predicate, value, b);
509                 } catch (BindingConstructionException e) {
510                         throw new IllegalArgumentException(e);
511                 } catch (BindingException e) {
512                         throw new IllegalArgumentException(e);
513                 }
514
515     }
516     
517     @Override
518     public void claimLiteral(Resource resource, Resource predicate, Object value, Binding binding) throws BindingException,
519                 ManyObjectsForFunctionalRelationException, ServiceException {
520
521         Statement valueStatement = null;
522         if(!(resource instanceof InternalResource)) valueStatement = getPossibleStatement(resource, predicate);
523
524         if(valueStatement != null && resource.equals(valueStatement.getSubject())) {
525
526             claimValue(valueStatement.getObject(), value, binding);
527
528         } else {
529
530             Resource type = getType(value);
531             Resource literal = newResource();
532             if (type == null) {
533                 type = b.Literal;
534                 Resource dataType = newResource();
535                 claim(dataType, b.InstanceOf, null, b.DataType);
536                 claimValue(dataType, binding.type(), DATA_TYPE_BINDING_INTERNAL);
537                 claim(literal, b.HasDataType, dataType);
538             }
539             claim(literal, b.InstanceOf, null, type);
540             claimValue(literal, value, binding); 
541             claim(resource, predicate, literal);
542             
543         }
544
545     }
546     
547     @Override
548     public void claimLiteral(Resource resource, Resource predicate,
549                 Resource inverse, Resource type, Object value)
550                 throws BindingException, ManyObjectsForFunctionalRelationException,
551                 ServiceException {
552
553                 try {
554                         Binding b = Bindings.getBinding(value.getClass());
555                 claimLiteral(resource, predicate, inverse, type, value, b);
556                 } catch (BindingConstructionException e) {
557                         throw new IllegalArgumentException(e);
558                 } catch (BindingException e) {
559                         throw new IllegalArgumentException(e);
560                 }
561         
562     }
563     
564     @Override
565     public void claimLiteral(Resource resource, Resource predicate,
566                 Resource inverse, Resource type, Object value, Binding binding)
567                 throws BindingException, ManyObjectsForFunctionalRelationException,
568                 ServiceException {
569
570         Statement valueStatement = (resource instanceof InternalResource) ? null : getPossibleStatement(resource, predicate);
571
572         if(valueStatement != null && resource.equals(valueStatement.getSubject())) {
573
574             claimValue(valueStatement.getObject(), value, binding);
575
576         } else {
577
578             Resource valueResource = newResource();
579             claim(valueResource, b.InstanceOf, null, type);
580             claim(resource, predicate, inverse, valueResource);
581             claimValue(valueResource, value, binding);
582
583         }
584
585     }
586
587     @Override
588     public void claimLiteral(Resource resource, Resource predicate,
589                 Resource type, Object value) throws BindingException,
590                 ManyObjectsForFunctionalRelationException, ServiceException {
591
592                 try {
593                         Binding b = Bindings.getBinding(value.getClass());
594                 claimLiteral(resource, predicate, type, value, b);
595                 } catch (BindingConstructionException e) {
596                         throw new IllegalArgumentException(e);
597                 } catch (BindingException e) {
598                         throw new IllegalArgumentException(e);
599                 }
600         
601     }
602     
603     @Override
604     public void claimLiteral(Resource resource, Resource predicate,
605                 Resource type, Object value, Binding binding)
606                 throws BindingException, ManyObjectsForFunctionalRelationException,
607                 ServiceException {
608
609                 try {
610                 Resource inverse = getSingleObject(predicate, b.InverseOf);
611                 claimLiteral(resource, predicate, inverse, type, value, binding);
612                 } catch (NoSingleResultException e) {
613                         throw new ServiceException(e);
614                 }
615         
616     }
617     
618         @Override
619         public void deny(Resource subject) throws ServiceException {
620                 assert(subject != null);
621                 if(!(subject instanceof InternalResource)) {
622                         try {
623                                 for (Statement statement : getStatements(subject, b.IsWeaklyRelatedTo)) {
624                                         deny(statement);
625                                 }
626                         } catch (ManyObjectsForFunctionalRelationException e) {
627                                 throw new ServiceException(e);
628                         }
629                 }
630         }
631
632         @Override
633         public void deny(Resource subject, Resource predicate)
634                         throws ServiceException {
635                 assert(subject != null);
636                 if(!(subject instanceof InternalResource)) {
637                         for (Resource object : getObjects(subject, predicate)) {
638                                 deny(subject, predicate, object);
639                         }
640                 }
641         }
642
643         @Override
644         public void deny(Resource subject, Resource predicate, Resource object) throws ServiceException {
645                 denyStatement(subject, predicate, object);
646         }
647
648         @Override
649         public void denyStatement(Resource subject, Resource predicate, Resource object) throws ServiceException {
650                 deny(subject, predicate, getPossibleInverse(predicate), object);
651         }
652
653         @Override
654         public void deny(Statement statement) throws ServiceException {
655                 Resource predicate = statement.getPredicate();
656                 deny(statement.getSubject(), predicate, getPossibleInverse(predicate), statement.getObject());
657         }
658
659         @Override
660         public void denyValue(Resource resource, Resource predicate)
661                         throws ManyObjectsForFunctionalRelationException, ServiceException {
662                 assert(resource != null);
663                 assert(predicate != null);
664
665                 if(!(resource instanceof InternalResource)) {
666                 Statement valueStatement = getPossibleStatement(resource, predicate);
667
668                 if (valueStatement != null && !valueStatement.isAsserted(resource)) {
669                     Resource value = valueStatement.getObject();
670                                 denyValue(value);
671                 }
672                 }
673         }
674
675         @Override
676         public Resource newResource() throws ServiceException {
677                 if(writeState.defaultClusterSet != null) return newResource(writeState.defaultClusterSet);
678                 else return newResource(writeState.defaultCluster);
679         }
680
681         @Override
682         public Resource newResource(long clusterId) throws ServiceException {
683                 int id = writeState.idToResource.size();
684                 InternalResource ret = new InternalResource(id, clusterId);
685                 writeState.idToResource.add(ret);
686                 return ret;
687         }
688
689     @Override
690     public Resource newResource(Resource clusterSet) throws ServiceException {
691         
692         if ((clusterSet instanceof InternalResource)) {
693                 if(!writeState.clusterSets.contains(clusterSet))
694                         throw new ClusterSetExistException("Cluster set does not exist. Resource=" + clusterSet);
695         } else {
696                 WriteSupport ws = session.getService(WriteSupport.class);
697                 if (!ws.hasClusterSet(null, clusterSet))
698                         if(!writeState.clusterSetsForExistingResources.contains(clusterSet))
699                         throw new ClusterSetExistException("Cluster set does not exist. Resource=" + clusterSet);
700         }
701         
702         int id = writeState.idToResource.size();
703         InternalResource ret = new InternalResource(id, clusterSet);
704         writeState.idToResource.add(ret);
705         return ret;
706     }
707
708     @Override
709     public void newClusterSet(Resource clusterSet) throws ServiceException {
710         if (DEBUG)
711             System.out.println("new cluster set=" + clusterSet);
712         boolean existingResource = !(clusterSet instanceof InternalResource);
713         if (existingResource) {
714                 WriteSupport ws = session.getService(WriteSupport.class);
715                 if (ws.hasClusterSet(null, clusterSet))
716                         throw new ClusterSetExistException("Cluster set exist already. Resource=" + clusterSet);
717                 writeState.clusterSetsForExistingResources.add(clusterSet);             
718         } else {        
719                 if(!writeState.clusterSets.add(clusterSet))
720                         throw new ClusterSetExistException("Cluster set exist already. Resource=" + clusterSet);
721         }
722     }
723     @Override
724     public Resource setClusterSet4NewResource(Resource clusterSet)
725     throws ServiceException {
726         Resource existing = writeState.defaultClusterSet; 
727         writeState.defaultClusterSet = clusterSet;
728         return existing;
729     }
730         
731         @Override
732         public void claim(Resource subject, Resource predicate, Resource inverse,
733                         Resource object) throws ServiceException {
734                 assert(subject != null);
735                 assert(predicate != null);
736                 assert(object != null);
737                 try {
738                     if(inverse != null && !(subject.equals(object) && inverse.equals(predicate))) {
739                         writeByte(CLAIM);
740                         writeInt(getId(subject));
741                         writeInt(getId(predicate));
742                         writeInt(getId(inverse));
743                         writeInt(getId(object));
744                     } else {
745                         writeByte(CLAIM_NOINVERSE);
746                         writeInt(getId(subject));
747                         writeInt(getId(predicate));
748                         writeInt(getId(object));
749                     }
750                 } catch(Exception e) {
751                         throw new ServiceException(e);
752                 }
753         }
754
755     @Override
756     public void deny(Resource subject, Resource predicate, Resource inverse,
757             Resource object) throws ServiceException {
758         assert(subject != null);
759         assert(predicate != null);
760         assert(object != null);
761         try {
762                 writeByte(DENY);
763                 writeInt(getId(subject));
764                 writeInt(getId(predicate));
765             if(inverse != null) writeInt(getId(inverse));
766             else writeInt(0);
767                 writeInt(getId(object));
768         } catch(Exception e) {
769             throw new ServiceException(e);
770         }
771     }
772
773     @Override
774     public void deny(Resource subject, Resource predicate, Resource inverse,
775             Resource object, VirtualGraph graph) throws ServiceException {
776         throw new UnsupportedOperationException();
777     }
778     
779         @Override
780         public void claimValue(Resource resource, Object value)
781                         throws ServiceException {
782                 try {
783                     Binding binding = Bindings.getBinding(value.getClass());
784                     claimValue(resource, value, binding);
785                 } catch (BindingConstructionException e) {
786                         throw new ServiceException(e);
787                 }
788         }
789
790         @Override
791         public void claimValue(Resource resource, Object value, Binding binding)
792                         throws ServiceException {
793                 try {
794                         
795                 writeByte(CLAIM_VALUE_B);
796                 writeInt(getId(resource));
797                 Serializer serializer = binding.serializer();
798                 int size = serializer.getSize(value);
799             writeInt(size);
800             serializer.serialize(new OutputStream() {
801                 
802                 @Override
803                 public void write(int b) throws IOException {
804                     writeByte(b);
805                 }
806                 
807             }, value);
808                         
809                 } catch(IOException e) {
810                 Logger.defaultLogError(e);
811                         throw new ServiceException(e);
812                 }
813         }
814
815         @Override
816         public void denyValue(Resource resource) throws ServiceException {
817                 writeByte(DENY_VALUE);
818                 writeInt(getId(resource));
819         }
820
821         @Override
822         public void denyValue(Resource resource, VirtualGraph graph) throws ServiceException {
823         throw new UnsupportedOperationException();
824         }
825
826         @Override
827         public void flushCluster() throws ServiceException {
828         writeState.defaultCluster = newCluster();
829         }
830
831     @Override
832     public void flushCluster(Resource r) throws ServiceException {
833         throw new ServiceException("Operation flushCluster(" + r + " not implemented.");
834     }
835         
836     private FileChannel channel;
837     byte[] bytes = new byte[BUFFER];
838     byte[] buffer = new byte[BUFFER];
839     ByteBuffer bb = ByteBuffer.wrap(bytes);
840     int byteIndex = 0;
841     
842     private void writeReset(int size) {
843
844         byteIndex = 0;
845         bb.position(0);
846         bb.limit(size);
847         try {
848
849             if(writeState.tempFile == null) {
850
851                 File workspace = Platform.getLocation().toFile();
852                 File temp = new File(workspace, "tempFiles");
853                 temp.mkdirs();
854
855                 File base = new File(temp, "delayed");
856                 base.mkdirs();
857                 writeState.tempFile = new File(base, UUID.randomUUID().toString());
858                 writeState.out = new FileOutputStream(writeState.tempFile);
859                 channel = writeState.out.getChannel();
860                 
861             }
862
863             for (int got=0;got < size;) {
864                 int n = channel.write(bb); 
865                 if (n <= 0) {
866                     new Exception().printStackTrace();
867                     return;
868                 }
869                 got += n;
870             }
871         } catch (IOException e) {
872             e.printStackTrace();
873         }
874     }
875
876     private void reset() {
877         byteIndex = 0;
878         try {
879             bb.clear();
880             for(int got=0; got < BUFFER;) {
881                 int n = channel.read(bb); 
882                 if (n <= 0)
883                     return;
884                 got += n;
885             }
886         } catch (IOException e) {
887             e.printStackTrace();
888         }
889     }
890
891     private void writeInt(int i) {
892         if(byteIndex < (BUFFER-4)) {
893                 bytes[byteIndex++] = (byte)(i&0xff);
894                 bytes[byteIndex++] = (byte)((i>>>8)&0xff);
895                 bytes[byteIndex++] = (byte)((i>>>16)&0xff);
896                 bytes[byteIndex++] = (byte)((i>>>24)&0xff);
897                 if (byteIndex == BUFFER)
898                     writeReset(BUFFER);
899         } else {
900                 int has = BUFFER-byteIndex;
901                 if(has == 0) writeReset(BUFFER);
902                 bytes[byteIndex++] = (byte)(i&0xff);
903                 if(has == 1) writeReset(BUFFER);
904                 bytes[byteIndex++] = (byte)((i>>>8)&0xff);
905                 if(has == 2) writeReset(BUFFER);
906                 bytes[byteIndex++] = (byte)((i>>>16)&0xff);
907                 if(has == 3) writeReset(BUFFER);
908                 bytes[byteIndex++] = (byte)((i>>>24)&0xff);
909                 if(has == 4) writeReset(BUFFER);
910         }
911     }
912     
913     private int readInt() {
914         if(byteIndex < (BUFFER-4)) {
915                 int result = (int) 
916                         ((bytes[byteIndex++] & 0xff) | 
917                         ((bytes[byteIndex++] & 0xff)<<8) | 
918                         ((bytes[byteIndex++] & 0xff)<<16) | 
919                         ((bytes[byteIndex++] & 0xff)<<24)); 
920                 return result;
921         } else {
922                 int has = BUFFER-byteIndex;
923                 int result = 0;
924                 if(has == 0) reset();
925                         result = (int)(bytes[byteIndex++] & 0xff);
926                 if(has == 1) reset();
927                 result |= (int)((bytes[byteIndex++] & 0xff) <<8);
928                 if(has == 2) reset();
929                 result |= (int)((bytes[byteIndex++] & 0xff) <<16);
930                 if(has == 3) reset();
931                 result |= (int)((bytes[byteIndex++] & 0xff) <<24);
932                 if(has == 4) reset();
933                 return result;
934         }
935     }
936     
937     private byte readByte() {
938         byte result = bytes[byteIndex++]; 
939         if(byteIndex == BUFFER) reset();
940         return result;
941     }
942
943     private void writeByte(int b) {
944         bytes[byteIndex++] = (byte)b;
945         if(byteIndex == BUFFER) writeReset(BUFFER);
946     }
947     
948     private void writeBytes(byte[] data) {
949                 int has = BUFFER-byteIndex;
950                 int amount = data.length;
951                 if(has > amount) {
952                         System.arraycopy(data, 0, bytes, byteIndex, amount);
953                         byteIndex += amount;
954                 } else {
955                         System.arraycopy(data, 0, bytes, byteIndex, has);
956                         writeReset(BUFFER);
957                         ByteBuffer bb2 = ByteBuffer.wrap(data);
958                         bb2.position(has);
959                         try {
960                                 channel.write(bb2);
961                         } catch (IOException e) {
962                                 e.printStackTrace();
963                         }
964                 }
965     }
966     
967     public byte[] readBytes(int amount) {
968         return readBytes(buffer, amount);
969     }
970     
971     public byte[] readBytes(byte[] result, int amount) {
972         if(result == null) result = new byte[amount];
973                 int has = BUFFER-byteIndex;
974                 if(has > amount) {
975                         System.arraycopy(bytes, byteIndex, result, 0, amount);
976                         byteIndex += amount;
977                 } else {
978                         System.arraycopy(bytes, byteIndex, result, 0, has);
979                         ByteBuffer bb2 = ByteBuffer.wrap(result);
980                         bb2.position(has);
981                         for(int got=has;got<amount;)
982                                 try {
983                                         got += channel.read(bb2);
984                                         if(got == -1) {
985                                                 new Exception().printStackTrace();
986                                                 return result;
987                                         }
988                                         
989                                 } catch (IOException e) {
990                                         e.printStackTrace();
991                                 }
992                         reset();
993                 }
994                 return result;
995     }
996     
997     public void commit(final WriteOnlyGraph w, final WriteTraits traits) throws ServiceException {
998             writeState.bindingToId = null;
999             writeState.externalToId = null;
1000                 try {
1001                         
1002                         writeByte(TERM);
1003                         if(byteIndex > 0) {
1004                                 writeReset(byteIndex);
1005                         }
1006                         channel.force(false);
1007                         writeState.out.close();
1008
1009                 FileInputStream fs = new FileInputStream(writeState.tempFile);
1010                 channel = fs.getChannel();
1011                         
1012                 } catch (IOException e) {
1013                         throw new ServiceException(e);
1014                 }
1015                 
1016                 w.getMetadata().putAll(metadata);
1017                 
1018                 TransferableGraphSupport tgs = w.getService(TransferableGraphSupport.class);
1019
1020                 // First create all resources defined by clusterId
1021                 MapList<Long,InternalResource> clusterAssignment = new MapList<Long,InternalResource>();
1022                 for(Resource r : writeState.idToResource) {
1023                         if(r instanceof InternalResource) {
1024                                 InternalResource ir = (InternalResource)r;
1025                                 if(ir.clusterId < 0) {
1026                                 if (DEBUG)
1027                                         System.out.println("ASSIGN CLUSTER " + ir + " => " + ir.clusterId);
1028                                         clusterAssignment.add(ir.clusterId, ir);
1029                                 } else if(ir.clusterId > 0) {
1030                                 if (DEBUG)
1031                                         System.out.println("-CREATED RESOURCE WITH EXISTING CLUSTER ID: " + ir);
1032                                         ir.resource = w.newResource(ir.clusterId);
1033                                         writeState.idToResource.set(ir.id, ir.resource);
1034                                         if (writeState.clusterSets.contains(ir)) {
1035                                                 w.newClusterSet(ir.resource);
1036                                         if (DEBUG)
1037                                                 System.out.println("--CREATED NEW INTERNAL RESOURCE CLUSTER SET: " + ir.resource);
1038                                         }
1039                                         
1040                                 }
1041                         }
1042                 }
1043                 
1044                 for(Long clusterKey : clusterAssignment.getKeys()) {
1045                 if (DEBUG)
1046                         System.out.println("CREATE LOGICAL CLUSTER: " + clusterKey);
1047                         w.flushCluster();
1048                         for(InternalResource ir : clusterAssignment.getValuesUnsafe(clusterKey)) {
1049                         if (DEBUG)
1050                                 System.out.println("-CREATED RESOURCE: " + ir);
1051                                 ir.resource = w.newResource();
1052                                 writeState.idToResource.set(ir.id, ir.resource);
1053                                 if (writeState.clusterSets.contains(ir)) {
1054                                         w.newClusterSet(ir.resource);
1055                                 if (DEBUG)
1056                                         System.out.println("--CREATED NEW INTERNAL RESOURCE CLUSTER SET: " + ir.resource);
1057                                 }
1058                         }
1059                 }
1060                 
1061                 // Create cluster sets for all existing resources (not InternalResource)
1062                 // before proceeding to create resources.
1063                 for(Resource existingResource : writeState.clusterSetsForExistingResources) {
1064                         w.newClusterSet(existingResource);
1065                 if (DEBUG)
1066                         System.out.println("CREATED NEW CLUSTER SET: " + existingResource);
1067                 }
1068                 
1069                 // Then create all resources defined by cluster set
1070                 for(Resource r : writeState.idToResource) {
1071                         if(r instanceof InternalResource) {
1072                                 InternalResource ir = (InternalResource)r;
1073                                 Resource clusterSet = ir.clusterSet;
1074                                 
1075                                 if (clusterSet != null) {
1076                                 if (DEBUG)
1077                                         System.out.println("NEW RESOURCE " + ir + " for cluster set " + clusterSet);
1078                                         if(clusterSet instanceof InternalResource) {
1079                                                 ir.resource = w.newResource(((InternalResource)clusterSet).resource);
1080                                         } else {
1081                                                 ir.resource = w.newResource(clusterSet);
1082                                         }
1083                                 if (DEBUG)
1084                                         System.out.println(" => " + ir.resource);
1085                                         writeState.idToResource.set(ir.id, ir.resource);
1086                                         if(writeState.clusterSets.contains(ir)) {
1087                                         if (DEBUG)
1088                                                 System.out.println(" ==> NEW CLUSTER SET");
1089                                                 w.newClusterSet(ir.resource);
1090                                         }
1091                                 }
1092                                 
1093                         }
1094                 }
1095                 
1096                 reset();
1097                 bb.limit(BUFFER);
1098                 
1099                 try {
1100                         while(true) {
1101                                 byte method = readByte();
1102                                 switch(method) {                
1103                                 case TERM: {
1104                                         return;
1105                                 }
1106                                 case CLAIM: {
1107                                         writeState.statementCount += 2;
1108                                         Resource subject = getResource(readInt());
1109                                         Resource predicate = getResource(readInt());
1110                     Resource inverse = getResource(readInt());
1111                                         Resource object = getResource(readInt());
1112                                         w.claim(subject, predicate, inverse, object);
1113                                 } break;
1114                 case CLAIM_NOINVERSE: {
1115                     ++writeState.statementCount;
1116                     Resource subject = getResource(readInt());
1117                     Resource predicate = getResource(readInt());
1118                     Resource object = getResource(readInt());
1119                     w.claim(subject, predicate, null, object);
1120                 } break;
1121                 case DENY: {
1122                     Resource subject = getResource(readInt());
1123                     Resource predicate = getResource(readInt());
1124                     int inv = readInt();
1125                     Resource inverse = null;
1126                     if(inv > 0) inverse = getResource(inv);
1127                     Resource object = getResource(readInt());
1128                     if(!subject.isPersistent() || !object.isPersistent()) {
1129                         VirtualGraph statementProvider1 = processor.getProvider(subject, predicate, object);
1130                         if(inv > 0) {
1131                                 VirtualGraph statementProvider2 = processor.getProvider(object, inverse, subject);
1132                                 if(statementProvider2 != null)
1133                                         w.deny(object, inverse, null, subject, statementProvider2);
1134                         }
1135                         if(statementProvider1 != null)
1136                                 w.deny(subject, predicate, null, object, statementProvider1);
1137                     } else {
1138                         w.deny(subject, predicate, inverse, object, null);
1139                     }
1140                 } break;
1141                 case DENY_VALUE: {
1142                     Resource subject = getResource(readInt());
1143                     if(!subject.isPersistent()) {
1144                         VirtualGraph provider = processor.getValueProvider(subject);
1145                         if(provider != null)
1146                                 w.denyValue(subject, provider);
1147                     } else {
1148                         w.denyValue(subject);
1149                     }
1150                 } break;
1151                                 case CLAIM_VALUE_B: {
1152                                         ++writeState.valueCount;
1153                                         Resource resource = getResource(readInt());
1154                                         int len = readInt();
1155                                         tgs.setValue(w, resource, null, this, len);
1156 //                                      byte[] bytes = readBytes(len);
1157 //                  tgs.setValue(resource, null, bytes);
1158                                 } break;
1159                                 case COMMIT_AND_CONTINUE: {
1160                                         XSupport xs = w.getService(XSupport.class);
1161                                         xs.commitAndContinue(w, traits);
1162                                 } break;
1163                                 }
1164                         }
1165                 } catch(Exception e) {
1166                         if(e instanceof ServiceException)
1167                                 throw (ServiceException)e;
1168                         else
1169                                 throw new ServiceException(e);
1170                 } finally {
1171                         try {
1172                                 channel.close();
1173                                 channel = null;
1174                         } catch (IOException e) {
1175                                 throw new ServiceException(e);
1176                         }
1177                 }
1178 //              System.out.println("Resources:  " + state.resourceCount);
1179 //              System.out.println("Statements: " + state.statementCount);
1180 //              System.out.println("Values:     " + state.valueCount);
1181         }
1182         
1183         private Resource getType(Object value) {
1184                 Class<?> clazz = value.getClass();
1185                 Resource dataType = 
1186                           clazz == Float.class ? b.Float
1187                         : clazz == Double.class ? b.Double
1188                         : clazz == Integer.class ? b.Integer
1189                         : clazz == String.class ? b.String
1190                         : clazz == Boolean.class ? b.Boolean
1191                         : clazz == Byte.class ? b.Byte
1192                         : clazz == Long.class ? b.Long
1193                         : clazz == float[].class ? b.FloatArray
1194                         : clazz == double[].class ? b.DoubleArray
1195                         : clazz == int[].class ? b.IntegerArray
1196                         : clazz == String[].class ? b.StringArray
1197                         : clazz == boolean[].class ? b.BooleanArray
1198                         : clazz == byte[].class ? b.ByteArray
1199                         : clazz == long[].class ? b.LongArray                                   
1200                         : null
1201                         ;
1202                 return dataType;
1203         }
1204         
1205         public long newCluster() {
1206                 return -1 - (++writeState.clusterCount);
1207         }
1208
1209         public long getDefaultCluster() {
1210                 return writeState.defaultCluster;
1211         }
1212         
1213         public void setDefaultCluster(long cluster) {
1214             writeState.defaultCluster = cluster;
1215         }
1216     
1217     @Override
1218     public void syncRequest(final DelayedWrite request) throws DatabaseException {
1219         
1220         try {
1221
1222                 final DelayedWriteGraph dwg = new DelayedWriteGraph(this);
1223                 request.perform(dwg);
1224
1225                 syncRequest(new WriteOnlyRequest() {
1226
1227                         @Override
1228                         public void perform(WriteOnlyGraph graph) throws DatabaseException {
1229                                 dwg.commit(graph, request);
1230                         }
1231
1232                 });
1233
1234         } catch (DatabaseException e) {
1235                 
1236                 throw e;
1237                 
1238         } catch (Throwable e) {
1239                 
1240                 throw new DatabaseException(e);
1241                 
1242         } finally {
1243                 
1244         }
1245         
1246     }
1247     
1248     @Override
1249     public void syncRequest(WriteOnly request) throws DatabaseException {
1250         
1251         Resource defaultClusterSet = setClusterSet4NewResource(null);
1252         
1253         try {
1254             WriteSupport ws = session.getService(WriteSupport.class);
1255             ws.performWriteRequest(this, request);
1256         } catch (DatabaseException e) {
1257             throw e;
1258         } catch (Throwable t) {
1259             throw new DatabaseException(t);
1260         }  finally {
1261             setClusterSet4NewResource(defaultClusterSet);
1262         }
1263         
1264     }
1265
1266     @SuppressWarnings("unchecked")
1267     @Override
1268     public <T> T getService(Class<T> api) {
1269         
1270         if(ClusteringSupport.class == api) {
1271
1272             final ClusteringSupport support = (ClusteringSupport)super.getService(api); 
1273
1274             return (T)new ClusteringSupport() {
1275                 
1276                 @Override
1277                 public Resource getResourceByIndexAndCluster(int resourceIndex, long clusterId)
1278                         throws DatabaseException, ResourceNotFoundException {
1279                     return support.getResourceByIndexAndCluster(resourceIndex, clusterId);
1280                 }
1281                 
1282                 @Override
1283                 public Resource getResourceByKey(int resourceKey) throws ResourceNotFoundException {
1284                     return support.getResourceByKey(resourceKey);
1285                 }
1286                 
1287                 @Override
1288                 public int getNumberOfResources(long clusterId)
1289                                 throws DatabaseException {
1290                         return support.getNumberOfResources(clusterId);
1291                 }
1292                 
1293                 @Override
1294                 public long getCluster(Resource r) {
1295                     return support.getCluster(r);
1296                 }
1297                 
1298                 @Override
1299                 public long createCluster() {
1300                     return newCluster();
1301                 }
1302                 
1303                 @Override
1304                 public boolean isClusterSet(Resource r) throws DatabaseException {
1305                         return support.isClusterSet(r);
1306                 }
1307
1308                 @Override
1309                 public Resource getClusterSetOfCluster(Resource r) throws DatabaseException {
1310                         return support.getClusterSetOfCluster(r);
1311                 }
1312                 
1313                 @Override
1314                 public Resource getClusterSetOfCluster(long cluster) throws DatabaseException {
1315                         return support.getClusterSetOfCluster(cluster);
1316                 }
1317
1318             };
1319             
1320         } else if (TransferableGraphSupport.class == api) {
1321                 
1322                 final TransferableGraphSupport parentSupport = session.getService(TransferableGraphSupport.class);
1323                 
1324                 return (T)new TransferableGraphSupport() {
1325
1326                         @Override
1327                         public void setValue(WriteOnlyGraph graph, Resource resource, VirtualGraph provider, byte[] raw) {
1328                                 writeByte(CLAIM_VALUE_B);
1329                                 writeInt(getId(resource));
1330                                 writeInt(raw.length);
1331                                 writeBytes(raw);
1332                                 writeInt(getBindingId(haxx));
1333                         }
1334                         
1335                         @Override
1336                         public void setValue(WriteOnlyGraph graph, Resource resource, VirtualGraph provider, ByteReader reader, int amount)
1337                                 throws DatabaseException {
1338                     writeByte(CLAIM_VALUE_B);
1339                     writeInt(getId(resource));
1340                     writeInt(amount);
1341                     writeBytes(reader.readBytes(null, amount));
1342                     writeInt(getBindingId(haxx));
1343                         }
1344                         
1345                         @Override
1346                         public byte[] getValue(ReadGraph graph, Resource resource) {
1347                                 return parentSupport.getValue(graph, resource);
1348                         }
1349                         
1350                         @Override
1351                         public InputStream getValueStream(ReadGraph graph, Resource resource) {
1352                                 return parentSupport.getValueStream(graph, resource);
1353                         }
1354                         
1355                 };
1356
1357         }
1358         
1359         return super.getService(api);
1360         
1361     }
1362
1363     @Override
1364     public <T> void addMetadata(Metadata data) throws ServiceException {
1365         MetadataUtils.addMetadata(session, metadata, data);
1366     }
1367     
1368     public void addCommitAndContinue() {
1369                 writeByte(COMMIT_AND_CONTINUE);
1370     }
1371
1372     @Override
1373     public <T extends Metadata> T getMetadata(Class<T> clazz) throws ServiceException {
1374         return MetadataUtils.getMetadata(session, metadata, clazz);
1375     }
1376
1377     @Override
1378     public TreeMap<String, byte[]> getMetadata() {
1379         return metadata;
1380     }
1381     @Override
1382     public <T> T syncRequest(WriteResult<T> request) throws DatabaseException {
1383         
1384         return request.perform(this);
1385         
1386     }
1387
1388     @Override
1389     public VirtualGraph getProvider() {
1390         return null;
1391     }
1392
1393     @Override
1394     public void clearUndoList(WriteTraits writeTraits) {
1395         WriteSupport ws = session.getService(WriteSupport.class);
1396         if (null != ws)
1397             ws.clearUndoList(writeTraits);
1398     }
1399
1400         @Override
1401         public void markUndoPoint() {
1402                 // TODO Auto-generated method stub
1403                 
1404         }
1405
1406         @Override
1407         public <T> T getPossibleRelatedValue(final Resource subject, final Resource relation, final Binding binding)
1408                         throws ManyObjectsForFunctionalRelationException, BindingException, ServiceException {
1409         if(!(subject instanceof InternalResource)) {
1410                 return super.getPossibleRelatedValue(subject, relation, binding);
1411         } else {
1412                 return null;
1413         }
1414         }
1415         
1416         @Override
1417         final public Resource getPossibleObject(final Resource subject, final Resource relation)
1418                         throws ManyObjectsForFunctionalRelationException, ServiceException {
1419         if(!(subject instanceof InternalResource)) {
1420                 return super.getPossibleObject(subject, relation);
1421         } else {
1422                 return null;
1423         }
1424         }
1425
1426 }