Merge commit '3efd7d44a55ae1fff284a1826e7c9afe27e0815e'
[simantics/platform.git] / bundles / org.simantics.db.procore / src / fi / vtt / simantics / procore / internal / WriteSupportImpl.java
1 package fi.vtt.simantics.procore.internal;\r
2 \r
3 import java.util.TreeMap;\r
4 \r
5 import org.simantics.db.Metadata;\r
6 import org.simantics.db.Operation;\r
7 import org.simantics.db.Resource;\r
8 import org.simantics.db.VirtualGraph;\r
9 import org.simantics.db.WriteGraph;\r
10 import org.simantics.db.common.MetadataUtils;\r
11 import org.simantics.db.common.exception.DebugException;\r
12 import org.simantics.db.common.utils.Logger;\r
13 import org.simantics.db.exception.DatabaseException;\r
14 import org.simantics.db.exception.ImmutableException;\r
15 import org.simantics.db.exception.ServiceException;\r
16 import org.simantics.db.impl.ClusterI;\r
17 import org.simantics.db.impl.MemWatch;\r
18 import org.simantics.db.impl.ResourceImpl;\r
19 import org.simantics.db.impl.VirtualGraphImpl;\r
20 import org.simantics.db.impl.graph.ReadGraphImpl;\r
21 import org.simantics.db.impl.graph.WriteGraphImpl;\r
22 import org.simantics.db.impl.graph.WriteSupport;\r
23 import org.simantics.db.impl.query.QueryProcessor;\r
24 import org.simantics.db.impl.query.QuerySupport;\r
25 import org.simantics.db.procore.cluster.ClusterImpl;\r
26 import org.simantics.db.procore.protocol.Constants;\r
27 import org.simantics.db.request.Write;\r
28 import org.simantics.db.request.WriteOnly;\r
29 import org.simantics.db.request.WriteResult;\r
30 import org.simantics.db.request.WriteTraits;\r
31 import org.simantics.db.service.ByteReader;\r
32 \r
33 public class WriteSupportImpl implements WriteSupport {\r
34 \r
35     final private SessionImplSocket session;\r
36     final private QueryProcessor queryProcessor;\r
37     final private State state;\r
38     final private QuerySupport querySupport;\r
39     final private TreeMap<String, byte[]> metadata;\r
40     \r
41     WriteSupportImpl(SessionImplSocket session) {\r
42         this.session = session;\r
43         this.queryProcessor = session.getQueryProvider2();\r
44         this.state = session.state;\r
45         this.querySupport = session.querySupport;\r
46         this.metadata = new TreeMap<String, byte[]>();\r
47         assert(this.session != null);\r
48         assert(this.queryProcessor != null);\r
49         assert(this.state != null);\r
50         assert(this.querySupport != null);\r
51     }\r
52     \r
53     @Override\r
54     public void flushCluster() {\r
55         session.clusterTable.flushCluster(session.graphSession);\r
56         if(session.defaultClusterSet != null) {\r
57             long resourceId = session.defaultClusterSet.getResourceId();\r
58             session.clusterSetsSupport.put(resourceId, Constants.NewClusterId);\r
59         }\r
60     }\r
61 \r
62     @Override\r
63     public void flushCluster(Resource r) {\r
64         session.clusterStream.reallyFlush();\r
65     }\r
66 \r
67     @Override\r
68     public boolean writeOnly() {\r
69         return session.writeOnly;\r
70     }\r
71 \r
72     @Override\r
73     public void flush(boolean intermediate) {\r
74 \r
75         if (!session.state.isWriteTransaction())\r
76             throw new IllegalStateException("Can only flush during transaction.");\r
77 \r
78         gc();\r
79         \r
80     }\r
81     \r
82     final public void claim(VirtualGraph provider, Resource subject, Resource predicate, Resource object) throws ServiceException {\r
83         claim(provider, querySupport.getId(subject), querySupport.getId(predicate), querySupport.getId(object));\r
84     }\r
85 \r
86     @Override\r
87     final public void claim(VirtualGraph provider, int subject, int predicate, int object)\r
88     throws ServiceException {\r
89 \r
90         provider = session.getProvider(provider); \r
91         if (writeOnly()) {\r
92             if (provider != null) {\r
93                 ((VirtualGraphImpl)provider).claim(subject, predicate, object);\r
94                 queryProcessor.updateStatements(subject, predicate);\r
95                 session.clientChanges.claim(subject, predicate, object);\r
96             } else {\r
97                 claimImpl2(subject, predicate, object);\r
98             }\r
99         } else {\r
100 //            queryProcessor.acquireWrite(writeState.getGraph());\r
101             if (provider != null) {\r
102                 ((VirtualGraphImpl)provider).claim(subject, predicate, object);\r
103                 queryProcessor.updateStatements(subject, predicate);\r
104                 session.clientChanges.claim(subject, predicate, object);\r
105             } else {\r
106                 claimImpl(subject, predicate, object);\r
107             }\r
108             queryProcessor.releaseWrite(session.writeState.getGraph());\r
109         }\r
110         \r
111     }\r
112     \r
113     @Override\r
114     public void setValue(VirtualGraph provider, Resource resource, byte[] value) throws ServiceException {\r
115 \r
116         provider = session.getProvider(provider);\r
117         if (writeOnly()) {\r
118             if (provider != null) {\r
119                 ((VirtualGraphImpl)provider).claimValue(((ResourceImpl) resource).id, value, value.length);\r
120                 queryProcessor.updateValue(querySupport.getId(resource));\r
121                 session.clientChanges.claimValue(resource);\r
122             } else {\r
123                 try {\r
124                     addSetValue(((ResourceImpl) resource).id, value, value.length);\r
125                 } catch (DatabaseException e) {\r
126                     Logger.defaultLogError(e);\r
127                 }\r
128             }\r
129         } else {\r
130             if (provider != null) {\r
131                 ((VirtualGraphImpl)provider).claimValue(((ResourceImpl) resource).id, value, value.length);\r
132                 queryProcessor.updateValue(querySupport.getId(resource));\r
133                 session.clientChanges.claimValue(resource);\r
134             } else {\r
135                 try {\r
136                     addSetValue(((ResourceImpl) resource).id, value, value.length);\r
137                 } catch (DatabaseException e) {\r
138                     Logger.defaultLogError(e);\r
139                 }\r
140             }\r
141             queryProcessor.releaseWrite(session.writeState.getGraph());\r
142         }\r
143 \r
144     }\r
145 \r
146     @Override\r
147     public Resource createResource(VirtualGraph provider) throws DatabaseException {\r
148         if (provider != null) {\r
149             int newId = ((VirtualGraphImpl)provider).newResource(false);\r
150             return new ResourceImpl(session.resourceSupport, newId);\r
151         } else {\r
152             return session.getNewResource();\r
153         }\r
154     }\r
155 \r
156     @Override\r
157     public Resource createResource(VirtualGraph provider, long clusterId)\r
158     throws DatabaseException {\r
159         assert (provider == null);\r
160         return session.getNewResource(clusterId);\r
161     }\r
162 \r
163     @Override\r
164     public Resource createResource(VirtualGraph provider, Resource clusterSet)\r
165     throws DatabaseException {\r
166         assert(provider == null);\r
167         assert(clusterSet != null);\r
168         return session.getNewResource(clusterSet);\r
169     }\r
170 \r
171     @Override\r
172     public void createClusterSet(VirtualGraph provider, Resource clusterSet)\r
173     throws DatabaseException {\r
174         assert(provider == null);\r
175         assert(clusterSet != null);\r
176         session.getNewClusterSet(clusterSet);\r
177     }\r
178 \r
179     @Override\r
180     public boolean hasClusterSet(VirtualGraph dummy, Resource clusterSet)\r
181     throws ServiceException {\r
182         return session.containsClusterSet(clusterSet);\r
183     }\r
184 \r
185     @Override\r
186     public Resource setDefaultClusterSet(Resource clusterSet)\r
187     throws ServiceException {\r
188         return session.setDefaultClusterSet4NewResource(clusterSet);\r
189     }\r
190     @Override\r
191     public void denyValue(VirtualGraph provider, Resource resource) throws ServiceException {\r
192         provider = session.getProvider(provider);\r
193         if (null == provider) {\r
194             int key = ((ResourceImpl)resource).id;\r
195             ClusterI cluster = session.clusterTable.getClusterByResourceKey(key);\r
196             if (cluster.getImmutable() && (session.serviceMode & SessionImplSocket.SERVICE_MODE_ALLOW) == 0)\r
197                 if(key != queryProcessor.getRootLibrary())\r
198                         throw new ImmutableException("Trying to modify immutable resource key=" + key);\r
199 \r
200             try { \r
201                 cluster.removeValue(key, session.clusterTranslator);\r
202             } catch (DatabaseException e) {\r
203                 Logger.defaultLogError(e);\r
204                 return;\r
205             }\r
206             queryProcessor.updateValue(key);\r
207             session.clientChanges.claimValue(resource);\r
208         } else {\r
209             ((VirtualGraphImpl)provider).denyValue(((ResourceImpl) resource).id);\r
210             queryProcessor.updateValue(querySupport.getId(resource));\r
211             session.clientChanges.claimValue(resource);\r
212         }\r
213         if (!writeOnly())\r
214             queryProcessor.releaseWrite(session.writeState.getGraph());\r
215     }\r
216 \r
217 \r
218     @Override\r
219     public boolean removeStatement(VirtualGraph provider, Resource subject, Resource predicate,\r
220             Resource object) throws ServiceException {\r
221         boolean ret = true;\r
222         if (writeOnly()) {\r
223             if (provider != null) {\r
224                 ((VirtualGraphImpl)provider).deny(querySupport.getId(subject), querySupport.getId(predicate), querySupport.getId(object));\r
225                 queryProcessor.updateStatements(querySupport.getId(subject), querySupport.getId(predicate));\r
226             } else {\r
227                 ret = removeStatement(querySupport.getId(subject), querySupport.getId(predicate), querySupport.getId(object));\r
228             }\r
229         } else {\r
230             if (provider != null) {\r
231 //                queryProcessor.acquireWrite(writeState.getGraph());\r
232                 ((VirtualGraphImpl)provider).deny(querySupport.getId(subject), querySupport.getId(predicate), querySupport.getId(object));\r
233                 queryProcessor.updateStatements(querySupport.getId(subject), querySupport.getId(predicate));\r
234                 queryProcessor.releaseWrite(session.writeState.getGraph());\r
235             } else {\r
236                 int sid = querySupport.getId(subject);\r
237                 int pid = querySupport.getId(predicate);\r
238                 int oid = querySupport.getId(object);\r
239                 if (sid < 0 || pid < 0 || oid < 0) {\r
240                     // One of the resources is virtual, cannot remove such\r
241                     // statement from persistent storage.\r
242                     return false;\r
243                 }\r
244 //                queryProcessor.acquireWrite(writeState.getGraph());\r
245                 ret = removeStatement(sid, pid, oid);\r
246                 queryProcessor.releaseWrite(session.writeState.getGraph());\r
247             }\r
248         }\r
249         session.clientChanges.deny(subject, predicate, object);\r
250         return ret;\r
251     }\r
252     \r
253     @Override\r
254     public synchronized void performWriteRequest(WriteGraph graph_, Write request) throws DatabaseException {\r
255         WriteGraphImpl graph = (WriteGraphImpl)graph_;\r
256 //        graph.state.barrier.inc();\r
257         try {\r
258             request.perform(graph);\r
259         } catch (Throwable t) {\r
260             t.printStackTrace();\r
261         }\r
262 //        graph.state.barrier.dec();\r
263 //        graph.waitAsync(request);\r
264         \r
265         queryProcessor.performDirtyUpdates(graph);\r
266         \r
267         // Do not fire metadata listeners for virtual requests\r
268         if(graph.getProvider() == null) {\r
269             //session.fireMetadataListeners(graph, session.clientChanges);\r
270             state.commitAndContinue(graph, session.clusterStream, request);\r
271             //session.clientChanges = new ClientChangesImpl(session);\r
272         }\r
273 \r
274 //        graph.state.barrier.assertReady();\r
275         \r
276     }\r
277     \r
278     @Override\r
279     public synchronized <T> T performWriteRequest(WriteGraph graph_, WriteResult<T> request) throws DatabaseException {\r
280 \r
281         WriteGraphImpl graph = (WriteGraphImpl)graph_;\r
282 //        graph.state.barrier.inc();\r
283         T result = null;\r
284         Throwable t = null;\r
285         try {\r
286             result = request.perform(graph);\r
287         } catch (Throwable t2) {\r
288             if(DebugException.DEBUG) new DebugException(t2).printStackTrace();\r
289             t = t2;\r
290         }\r
291         \r
292 //        graph.state.barrier.dec();\r
293 //        graph.waitAsync(request);\r
294         \r
295         queryProcessor.performDirtyUpdates(graph);\r
296 \r
297         // Do not fire metadata listeners for virtual requests\r
298         if(graph.getProvider() == null) {\r
299             //session.fireMetadataListeners((WriteGraphImpl)graph, session.clientChanges);\r
300             state.commitAndContinue(graph, session.clusterStream, request);\r
301             //session.clientChanges = new ClientChangesImpl(session);\r
302         }\r
303         \r
304         if(t != null) {\r
305             if(t instanceof DatabaseException) throw (DatabaseException)t;\r
306             else throw new DatabaseException(t);\r
307         }\r
308         \r
309         return result;\r
310         \r
311     }\r
312 \r
313     @Override\r
314     public synchronized void performWriteRequest(WriteGraph graph, WriteOnly request) throws DatabaseException {\r
315 \r
316         session.acquireWriteOnly();\r
317         \r
318         request.perform(graph);\r
319         \r
320         ReadGraphImpl impl = (ReadGraphImpl)graph;\r
321         \r
322         queryProcessor.performDirtyUpdates(impl);\r
323 \r
324         // Do not fire metadata listeners for virtual requests\r
325         if(graph.getProvider() == null) {\r
326             //session.fireMetadataListeners(impl, session.clientChanges);\r
327             state.commitAndContinue(session.writeState.getGraph(), session.clusterStream, request);\r
328             //session.clientChanges = new ClientChangesImpl(session);\r
329         }        \r
330         session.releaseWriteOnly(impl);\r
331         \r
332     }\r
333 \r
334     \r
335     @Override\r
336     public void gc() {\r
337         if (MemWatch.isLowOnMemory()) {\r
338             session.clusterTable.gc();\r
339             queryProcessor.gc(0, Integer.MAX_VALUE);\r
340             System.gc();\r
341         }\r
342     }\r
343     \r
344     @Override\r
345     public void claimValue(VirtualGraph provider, Resource resource, byte[] value) throws DatabaseException {\r
346         claimValue(provider, ((ResourceImpl)resource).id, value, value.length);\r
347     }\r
348 \r
349     @Override\r
350     public void claimValue(VirtualGraph provider, int resource, byte[] value, int length) throws DatabaseException {\r
351 \r
352         provider = session.getProvider(provider);\r
353         if (writeOnly()) {\r
354             if (provider != null) {\r
355                 ((VirtualGraphImpl)provider).claimValue(resource, value, length);\r
356                 queryProcessor.updateValue(resource);\r
357                 session.clientChanges.claimValue(resource);\r
358             } else {\r
359                 addSetValue(resource, value, length);\r
360             }\r
361         } else {\r
362             if (provider != null) {\r
363                 ((VirtualGraphImpl)provider).claimValue(resource, value, length);\r
364                 queryProcessor.updateValue(resource);\r
365                 session.clientChanges.claimValue(resource);\r
366                 queryProcessor.releaseWrite(session.writeState.getGraph());\r
367             } else {\r
368                 try {\r
369                     addSetValue(resource, value, length);\r
370                 } catch (DatabaseException e) {\r
371                     throw e;\r
372                 } catch (Throwable t) {\r
373                     throw new DatabaseException(t);\r
374                 } finally {\r
375                     queryProcessor.releaseWrite(session.writeState.getGraph());\r
376                 }\r
377             }\r
378         }\r
379     }\r
380     \r
381     @Override\r
382     public void claimValue(VirtualGraph provider, Resource resource, ByteReader reader, int amount) throws DatabaseException {\r
383         claimValue(provider, resource, reader.readBytes(null, amount));\r
384     }\r
385 \r
386     @Override\r
387     public <T> void addMetadata(Metadata data) throws ServiceException {\r
388         MetadataUtils.addMetadata(session, metadata, data);\r
389     }\r
390 \r
391     @SuppressWarnings("unchecked")\r
392     @Override\r
393     public <T extends Metadata> T getMetadata(Class<T> clazz) throws ServiceException {\r
394         return MetadataUtils.getMetadata(session, metadata, clazz);\r
395     }\r
396 \r
397     @Override\r
398     public TreeMap<String, byte[]> getMetadata() {\r
399         return metadata;\r
400     }\r
401 \r
402     @Override\r
403     public void commitDone(WriteTraits writeTraits, long csid) {\r
404         metadata.clear();\r
405         if (this.writeTraits == writeTraits) {\r
406             session.graphSession.undoContext.clear();\r
407             this.writeTraits = null;\r
408         }\r
409 //        if (null != operation)\r
410 //            operation = null;\r
411     }\r
412     @Override\r
413     public int clearMetadata() {\r
414         int ret = metadata.size();\r
415         metadata.clear();\r
416         return ret;\r
417     }\r
418     @Override\r
419     public void clearUndoList(WriteTraits writeTraits) {\r
420         this.writeTraits = writeTraits;\r
421     }\r
422     @Override\r
423     public void startUndo() {\r
424         session.state.setCombine(false);\r
425     }\r
426     private WriteTraits writeTraits = null;\r
427     private void addSetValue(int subject, byte[] value, int length)\r
428     throws DatabaseException {\r
429 \r
430         ClusterI cluster = session.clusterTable.getClusterByResourceKey(subject);\r
431         if (cluster.getImmutable() && (session.serviceMode & SessionImplSocket.SERVICE_MODE_ALLOW) == 0)\r
432                 if(subject != queryProcessor.getRootLibrary())\r
433                         throw new ImmutableException("Trying to modify immutable resource key=" + subject);\r
434         \r
435         ClusterI cluster2 = cluster.setValue(subject, value, length, session.clusterTranslator);\r
436         if (cluster2 != cluster)\r
437             session.clusterTable.replaceCluster(cluster2);\r
438         \r
439         session.clientChanges.claimValue(subject);\r
440         \r
441         if (cluster2.isWriteOnly())\r
442             return;\r
443 \r
444         queryProcessor.updateValue(subject);\r
445 \r
446     }\r
447 \r
448     final private void claimImpl(int subject, int predicate, int object)\r
449     throws ServiceException {\r
450 \r
451         assert (subject != 0);\r
452         assert (predicate != 0);\r
453         assert (object != 0);\r
454 \r
455         ClusterImpl cluster = session.clusterTable.getClusterByResourceKey(subject);\r
456         assert (null != cluster);\r
457         if (cluster.getImmutable() && (session.serviceMode & SessionImplSocket.SERVICE_MODE_ALLOW) == 0)\r
458                 if(subject != queryProcessor.getRootLibrary())\r
459                         throw new ImmutableException("Trying to modify immutable resource key=" + subject);\r
460         try {\r
461             ClusterI c = cluster.addRelation(subject, predicate, object, session.clusterTranslator);\r
462             if (null != c && c != cluster)\r
463                 session.clusterTable.replaceCluster(c);\r
464         } catch (DatabaseException e) {\r
465             Logger.defaultLogError(e);\r
466             throw new RuntimeException(e);\r
467         }\r
468         queryProcessor.updateStatements(subject, predicate);\r
469         session.clientChanges.claim(subject, predicate, object);\r
470 \r
471     }\r
472 \r
473     final private void claimImpl2(int subject, int predicate, int object) {\r
474         \r
475         assert (subject != 0);\r
476         assert (predicate != 0);\r
477         assert (object != 0);\r
478 \r
479         ClusterI cluster = session.clusterTable.getClusterByResourceKey(subject);\r
480         try {\r
481             ClusterI c = cluster.addRelation(subject, predicate, object, session.clusterTranslator);\r
482             if (null != c && c != cluster)\r
483                 session.clusterTable.replaceCluster(c);\r
484         } catch (DatabaseException e) {\r
485             Logger.defaultLogError(e);\r
486         }\r
487         if (cluster.isWriteOnly())\r
488             return;\r
489         queryProcessor.updateStatements(subject, predicate);\r
490 \r
491     }\r
492     \r
493     private boolean removeStatement(int subject, int predicate, int object) throws ImmutableException {\r
494 \r
495         assert (subject != 0);\r
496         assert (predicate != 0);\r
497         assert (object != 0);\r
498 \r
499         ClusterI cluster = session.clusterTable.getClusterByResourceKey(subject);\r
500         assert (null != cluster);\r
501 \r
502         if (cluster.getImmutable() && (session.serviceMode & SessionImplSocket.SERVICE_MODE_ALLOW) == 0)\r
503                 if(subject != queryProcessor.getRootLibrary())\r
504                         throw new ImmutableException("Trying to modify immutable resource key=" + subject);\r
505 \r
506         try {\r
507             cluster.denyRelation(subject, predicate, object, session.clusterTranslator);\r
508         } catch (DatabaseException e) {\r
509             Logger.defaultLogError(e);\r
510             return false;\r
511         }\r
512         queryProcessor.updateStatements(subject, predicate);\r
513         return true;\r
514 \r
515     }\r
516     \r
517 }\r