]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.db.common/src/org/simantics/db/common/utils/Transaction.java
New splash.bmp with version 1.36.0
[simantics/platform.git] / bundles / org.simantics.db.common / src / org / simantics / db / common / utils / Transaction.java
1 /*******************************************************************************
2  * Copyright (c) 2007 VTT Technical Research Centre of Finland and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *     VTT Technical Research Centre of Finland - initial API and implementation
10  *******************************************************************************/
11 package org.simantics.db.common.utils;
12
13 import java.util.Collection;
14 import java.util.Set;
15 import java.util.concurrent.Semaphore;
16 import java.util.function.Consumer;
17
18 import org.simantics.databoard.accessor.Accessor;
19 import org.simantics.databoard.binding.Binding;
20 import org.simantics.databoard.type.Datatype;
21 import org.simantics.db.ReadGraph;
22 import org.simantics.db.RequestProcessor;
23 import org.simantics.db.Resource;
24 import org.simantics.db.Statement;
25 import org.simantics.db.WriteGraph;
26 import org.simantics.db.WriteOnlyGraph;
27 import org.simantics.db.common.request.DelayedWriteRequest;
28 import org.simantics.db.exception.AdaptionException;
29 import org.simantics.db.exception.BindingException;
30 import org.simantics.db.exception.CancelTransactionException;
31 import org.simantics.db.exception.DatabaseException;
32 import org.simantics.db.exception.DoesNotContainValueException;
33 import org.simantics.db.exception.ManyObjectsForFunctionalRelationException;
34 import org.simantics.db.exception.NoInverseException;
35 import org.simantics.db.exception.NoSingleResultException;
36 import org.simantics.db.exception.ResourceNotFoundException;
37 import org.simantics.db.exception.RuntimeDatabaseException;
38 import org.simantics.db.exception.ServiceException;
39 import org.simantics.db.exception.ValidationException;
40 import org.simantics.db.procedure.Procedure;
41 import org.simantics.db.request.DelayedWrite;
42 import org.simantics.db.request.Read;
43 import org.simantics.db.request.Write;
44
45 /**
46  * Synchronous Transaction. <p>
47  * 
48  * Hint: Import all methods as static.
49  * import static org.simantics.db.Transaction.*; 
50  * 
51  * Remember also to change Eclipse Preferences:
52  *   Organize Imports: number of static imports needed for .* on vakiona 99, siihen vaikka 3
53  *
54  * 
55  * Usage A:
56  * 
57  * startTransaction(session, true);
58  * try {
59  *  ...
60  *    commit();
61  * } finally {
62  *    endTransaction();
63  * }
64  * 
65  * Usage B:
66  * setGraph(g);
67  * ...
68  * setGraph(null);
69  *
70  * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
71  */
72 public class Transaction {
73
74         public static enum Type {
75                 READ,
76                 WRITE,
77                 DELAYED_WRITE
78         }
79         
80         /** Thread local transactions */
81         private static ThreadLocal<TransactionInfo> transactions = new ThreadLocal<TransactionInfo>();
82         
83         static private class TransactionInfo {
84                 WriteGraph wg;
85                 ReadGraph rg;
86                 
87                 /** Semaphore that is released when the transaction is complete */
88                 Semaphore ts;
89                 
90                 /** Semaphore that is release after transaction ended */
91                 Semaphore es;
92                 
93                 /** Error */
94                 DatabaseException error;
95                 
96                 /** */
97                 boolean commit = false;
98                 
99                 TransactionInfo() {
100                 }
101                 
102                 TransactionInfo(Object graph) {
103                         if (graph instanceof WriteGraph) {
104                                 wg = (WriteGraph) graph;
105                                 rg = (ReadGraph) graph;
106                         } else if (graph instanceof ReadGraph) {
107                                 wg = null;
108                                 rg = (ReadGraph) graph;
109                         } else {
110                                 throw new RuntimeDatabaseException("Not a sync graph");
111                         }                       
112                 }
113         }
114         
115         public static ReadGraph readGraph() {
116                 TransactionInfo t = transactions.get();
117                 return t == null ? null : t.rg;
118         }
119         
120         public static WriteGraph writeGraph() {
121                 TransactionInfo t = transactions.get();
122                 return t == null ? null : t.wg;
123         }
124                 
125         public static Object setGraph(Object graph) {
126                 if (graph==null) {
127                         transactions.set(null);
128                         return null;
129                 }
130                 
131                 {
132                         Object oldGraph = null;
133                         TransactionInfo t = transactions.get();
134                         if (t!=null) {                          
135                                 oldGraph = t.rg;
136                                 if (graph instanceof WriteGraph) {
137                                         t.wg = (WriteGraph) graph;
138                                         t.rg = (ReadGraph) graph;
139                                 } else if (graph instanceof ReadGraph) {
140                                         t.wg = null;
141                                         t.rg = (ReadGraph) graph;
142                                 } else {
143                                         throw new RuntimeDatabaseException("Not a sync graph");
144                                 }                                                       
145                         } else {
146                                 t = new TransactionInfo(graph);
147                         }
148                         transactions.set(t);
149                         return oldGraph;
150                 }
151         }
152         
153         public static void startTransaction(RequestProcessor processor, boolean write) throws DatabaseException {
154                 startTransaction(processor, write ? Type.WRITE : Type.READ);
155         }
156         
157         public static void startTransaction(RequestProcessor processor, Type type) throws DatabaseException {
158                 switch (type) {
159                         case READ:
160                         {
161                                 if (transactions.get()!=null) throw new RuntimeDatabaseException("There is already a transaction.");
162                                 final Semaphore started = new Semaphore(0);
163                                 final TransactionInfo t = new TransactionInfo();
164                                 t.es = new Semaphore(0);
165                                 t.ts = new Semaphore(0);
166                                 transactions.set(t);
167
168                                 Read<Object> request = new Read<Object>() {
169                                         @Override
170                                         public Object perform(ReadGraph g) throws DatabaseException {
171                                                 t.wg = null;
172                                                 t.rg = g;
173                                                 started.release();
174                                                 try {
175                                                         t.ts.acquire();
176                                                 } catch (InterruptedException e) {
177                                                 }
178                                                 return null;
179                                         }
180                                 };
181                                 Procedure<Object> procedure = new Procedure<Object>() {
182                                     @Override
183                                     public void execute(Object result) {
184                                         t.es.release(9999);
185                                     }
186                                     @Override
187                                     public void exception(Throwable ex) {
188                                         if (ex instanceof DatabaseException)
189                                             t.error = (DatabaseException) ex;
190                                         else {
191                                             t.error = new DatabaseException(ex);
192                                         }
193                                         t.es.release(9999);
194                                     }
195                                 };
196
197                                 processor.asyncRequest(request, procedure);
198
199                                 // Wait until transaction has started
200                                 try {
201                                         // Sleep this thread until transaction has started
202                                         started.acquire(1);
203                                 } catch (InterruptedException e) {
204                                         throw new DatabaseException("Thread was interrupted.");
205                                 } finally {
206                                 }
207                         }
208                         break;
209
210                         case WRITE:
211                         {
212                                 if (transactions.get()!=null) throw new RuntimeDatabaseException("There is already a transaction.");
213                                 final Semaphore started = new Semaphore(0);
214                                 final TransactionInfo t = new TransactionInfo();
215                                 t.es = new Semaphore(0);
216                                 t.ts = new Semaphore(0);
217                                 transactions.set(t);
218
219                                 Consumer<DatabaseException> callback = parameter -> {
220                                         t.error = parameter;
221                                         t.es.release(9999);
222                                 };
223
224                                 Write request =  new Write() { 
225                                         @Override
226                                         public void perform(WriteGraph g) throws DatabaseException {
227                                                 t.wg = g;
228                                                 t.rg = g;
229                                                 started.release();
230                                                 try {
231                                                         t.ts.acquire();
232                                                 } catch (InterruptedException e) {
233                                                 }
234                                                 if (!t.commit) throw new CancelTransactionException();
235                                         }
236                                 };
237
238                                 processor.asyncRequest( request, callback );
239
240                                 // Wait until transaction has started
241                                 try {
242                                         // Sleep this thread until transaction has started
243                                         started.acquire(1);
244                                 } catch (InterruptedException e) {
245                                         throw new DatabaseException("Thread was interrupted.");
246                                 } finally {
247                                 }
248                         }
249                         break;
250
251                         case DELAYED_WRITE:
252                         {
253                                 if (transactions.get()!=null) throw new RuntimeDatabaseException("There is already a transaction.");
254                                 final Semaphore started = new Semaphore(0);
255                                 final TransactionInfo t = new TransactionInfo();
256                                 t.es = new Semaphore(0);
257                                 t.ts = new Semaphore(0);
258                                 transactions.set(t);
259
260                                 Consumer<DatabaseException> callback = parameter -> {
261                                         t.error = parameter;
262                                         t.es.release(9999);
263                                 };
264
265                                 DelayedWrite request =  new DelayedWriteRequest() { 
266                                         @Override
267                                         public void perform(WriteGraph g) throws DatabaseException {
268                                                 t.wg = g;
269                                                 t.rg = g;
270                                                 started.release();
271                                                 try {
272                                                         t.ts.acquire();
273                                                 } catch (InterruptedException e) {
274                                                 }
275                                                 if (!t.commit) throw new CancelTransactionException();
276                                         }
277                                 };
278
279                                 processor.asyncRequest( request, callback );
280
281                                 // Wait until transaction has started
282                                 try {
283                                         // Sleep this thread until transaction has started
284                                         started.acquire(1);
285                                 } catch (InterruptedException e) {
286                                         throw new DatabaseException("Thread was interrupted.");
287                                 } finally {
288                                 }
289                         }
290                         break;
291                 }
292         }
293         
294         /**
295          * Commits transaction if no error occurred 
296          * 
297          * @throws DatabaseException
298          */
299         public static void endTransaction() throws DatabaseException {
300                 TransactionInfo t = transactions.get();
301                 if (t == null) return; //throw new RuntimeDatabaseException("There is no transaction to commit.");
302                 
303                 t.ts.release(9999);
304                 
305                 try {
306                         t.es.acquire();
307                 } catch (InterruptedException e) {
308                         throw new DatabaseException(e);
309                 }                               
310                 
311                 if (t.error!=null) {
312                         if (t.error instanceof CancelTransactionException==false) throw t.error;                
313                 }
314                 transactions.set(null);
315         }
316
317         /**
318          * Commits transaction  
319          * 
320          * @throws DatabaseException
321          */
322         public static void commit() throws DatabaseException {
323                 TransactionInfo t = transactions.get();
324                 if (t == null) throw new RuntimeDatabaseException("There is not transaction to commit.");
325                 t.commit = true;
326                 endTransaction();
327         }
328         
329     public static String getURI(Resource resource) throws ResourceNotFoundException, ValidationException, ServiceException {
330         return readGraph().getPossibleURI(resource);
331     }
332     
333     public static String getPossibleURI(Resource resource) throws ResourceNotFoundException, ValidationException, ServiceException {
334         return readGraph().getPossibleURI(resource);
335     }
336         
337     public static Resource getResource(String uri) throws ResourceNotFoundException, ValidationException, ServiceException 
338     {
339         return readGraph().getResource(uri);
340     }
341
342     public static Resource getPossibleResource(String uri) throws ResourceNotFoundException, ValidationException, ServiceException
343     {
344         return readGraph().getPossibleResource(uri);
345     }
346     
347     public static Resource getBuiltin(String id) throws ResourceNotFoundException, ServiceException
348     {
349         return readGraph().getBuiltin(id);
350     }
351
352     public static Collection<Statement> getStatements(Resource subject, Resource relation) throws ManyObjectsForFunctionalRelationException, ServiceException
353     {
354         return readGraph().getStatements(subject, relation);
355     }
356
357     public static Collection<Statement> getAssertedStatements(Resource subject, Resource relation) throws ManyObjectsForFunctionalRelationException, ServiceException
358     {
359         return readGraph().getAssertedStatements(subject, relation);            
360     }    
361     
362     public static Collection<Resource> getPredicates(Resource subject) throws ServiceException
363     {
364         return readGraph().getPredicates(subject);      
365     }
366
367     public static Collection<Resource> getPrincipalTypes(Resource subject) throws ServiceException
368     {
369         return readGraph().getPrincipalTypes(subject);
370     }
371
372     public static Set<Resource> getTypes(Resource subject) throws ServiceException
373     {
374         return readGraph().getTypes(subject);
375     }
376
377     public static Set<Resource> getSupertypes(Resource subject) throws ServiceException
378     {
379         return readGraph().getSupertypes(subject);
380     }
381
382     public static Set<Resource> getSuperrelations(Resource subject) throws ServiceException
383     {
384         return readGraph().getSuperrelations(subject);
385     }
386
387     public static Collection<Resource> getObjects(Resource subject, Resource relation) throws ManyObjectsForFunctionalRelationException, ServiceException
388     {
389         return readGraph().getObjects(subject, relation);
390     }
391
392     public static Collection<Resource> getAssertedObjects(Resource subject, Resource relation) throws ManyObjectsForFunctionalRelationException, ServiceException
393     {
394         return readGraph().getAssertedObjects(subject, relation);
395     }
396     
397     public static Resource getInverse(Resource relation) throws NoInverseException, ManyObjectsForFunctionalRelationException, ServiceException
398     {
399         return readGraph().getInverse(relation);
400     }
401     
402     public static Resource getSingleObject(Resource subject, Resource relation) throws NoSingleResultException, ManyObjectsForFunctionalRelationException, ServiceException
403     {
404         return readGraph().getSingleObject(subject, relation);
405     }
406     
407     public static Statement getSingleStatement(Resource subject, Resource relation) throws NoSingleResultException, ManyObjectsForFunctionalRelationException, ServiceException
408     {
409         return readGraph().getSingleStatement(subject, relation);
410     }
411
412     public static Resource getSingleType(Resource subject) throws NoSingleResultException, ServiceException
413     {
414         return readGraph().getSingleType(subject);
415     }
416     
417     public static Resource getSingleType(Resource subject, Resource baseType) throws NoSingleResultException, ServiceException
418     {
419         return readGraph().getSingleType(subject, baseType);
420     }
421
422     public static <T> T getValue(Resource subject) throws DoesNotContainValueException, ServiceException
423     {
424         return readGraph().<T>getValue(subject);
425     }
426
427     public static <T> T getValue(Resource subject, Binding binding) throws DoesNotContainValueException, BindingException, ServiceException
428     {
429         return readGraph().<T>getValue(subject, binding);
430     }
431
432     public static <T> T getRelatedValue(Resource subject, Resource relation) throws NoSingleResultException, DoesNotContainValueException, ServiceException
433     {
434         return readGraph().<T>getRelatedValue(subject, relation);
435     }
436
437     public static <T> T getRelatedValue(Resource subject, Resource relation, Binding binding) throws NoSingleResultException, DoesNotContainValueException, BindingException, ServiceException
438     {
439         return readGraph().<T>getRelatedValue(subject, relation, binding);
440     }
441
442     public static <T> T adapt(Resource resource, Class<T> clazz) throws AdaptionException, ValidationException, ServiceException
443     {
444         return readGraph().adapt(resource, clazz);
445     }
446
447     public static <T> T adaptUnique(Resource resource, Class<T> clazz) throws AdaptionException, ValidationException, ServiceException
448     {
449         return readGraph().adaptUnique(resource, clazz);
450     }
451     
452     public static Resource getPossibleInverse(Resource relation) throws ManyObjectsForFunctionalRelationException, ServiceException
453     {
454         return readGraph().getPossibleInverse(relation);
455     }
456     
457     public static Resource getPossibleObject(Resource subject, Resource relation) throws ManyObjectsForFunctionalRelationException, ServiceException
458     {
459         return readGraph().getPossibleObject(subject, relation);
460     }
461     
462     public static Statement getPossibleStatement(Resource subject, Resource relation) throws ManyObjectsForFunctionalRelationException, ServiceException
463     {
464         return readGraph().getPossibleStatement(subject, relation);
465     }
466     
467     public static Resource getPossibleType(Resource subject, Resource baseType) throws ServiceException
468     {
469         return readGraph().getPossibleType(subject, baseType);
470     }
471     
472     public static <T> T getPossibleValue(Resource subject) throws ServiceException
473     {
474         return readGraph().<T>getPossibleValue(subject);
475     }
476     
477     public static <T> T getPossibleValue(Resource subject, Binding binding) throws BindingException, ServiceException
478     {
479         return readGraph().<T>getPossibleValue(subject, binding);
480     }
481
482     public static <T> T getPossibleRelatedValue(Resource subject, Resource relation) throws ManyObjectsForFunctionalRelationException, ServiceException
483     {
484         return readGraph().<T>getPossibleRelatedValue(subject, relation);
485     }
486     
487     public static <T> T getPossibleRelatedValue(Resource subject, Resource relation, Binding binding) throws ManyObjectsForFunctionalRelationException, BindingException, ServiceException
488     {
489         return readGraph().<T>getPossibleRelatedValue(subject, relation, binding);
490     }
491     
492     public static <T> T getPossibleAdapter(Resource resource, Class<T> clazz) throws ValidationException, ServiceException
493     {
494         return readGraph().<T>getPossibleAdapter(resource, clazz);
495     }
496     
497     public static <T> T getPossibleUniqueAdapter(Resource resource, Class<T> clazz) throws ValidationException, ServiceException
498     {
499         return readGraph().<T>getPossibleUniqueAdapter(resource, clazz);
500     }
501     
502     public static boolean isInstanceOf(Resource resource, Resource type) throws ServiceException
503     {
504         return readGraph().isInstanceOf(resource, type);
505     }
506     
507     public static boolean isInheritedFrom(Resource resource, Resource type) throws ServiceException
508     {
509         return readGraph().isInheritedFrom(resource, type);
510     }
511     
512     public static boolean isSubrelationOf(Resource resource, Resource relation) throws ServiceException
513     {
514         return readGraph().isSubrelationOf(resource, relation);
515     }
516     
517     public static boolean hasStatement(Resource subject) throws ServiceException
518     {
519         return readGraph().hasStatement(subject);
520     }
521     
522     public static boolean hasStatement(Resource subject, Resource relation) throws ServiceException
523     {
524         return readGraph().hasStatement(subject, relation);
525     }
526     
527     public static boolean hasStatement(Resource subject, Resource relation, Resource object) throws ServiceException
528     {
529         return readGraph().hasStatement(subject, relation, object);
530     }
531     
532     public static boolean hasValue(Resource subject) throws ServiceException
533     {
534         return readGraph().hasValue(subject);
535     }
536     
537     public static Datatype getDataType(Resource subject) throws DatabaseException
538     {
539         return readGraph().getDataType(subject);
540     }
541     
542     public static <T extends Accessor> T getAccessor(Resource subject) throws DatabaseException
543     {
544         return readGraph().<T>getAccessor(subject);
545     }
546     
547     
548     /**
549      * Makes sure that the statements (s,p,o) and (o,p',s) are found in the
550      * graph, where p' is the inverse predicate of p. Contrary to
551      * {@link WriteOnlyGraph#claim(Resource, Resource, Resource, Resource)} this
552      * method assures that the the statement and its inverse are semantically
553      * valid after the invocation of this method.
554      * 
555      * @param subject subject, i.e. source resource of the statement to be
556      *        claimed
557      * @param predicate predicate resource of the statement to be claimed
558      * @param object object, i.e. target resource of the statement to be claimed
559      * @throws ServiceException
560      */
561     public static void claim(Resource subject, Resource predicate, Resource object) throws ServiceException {
562         writeGraph().claim(subject, predicate, object);
563     }
564
565     /**
566      * Sets literal value related to the specified resource with the specified
567      * predicate. If such value exists (s,p), the value is overridden with the
568      * new specified value.
569      * 
570      * @param resource
571      * @param predicate
572      * @param value Value of the literal (boxed primitive/String or
573      *        primitive/String array)
574      * @throws ManyObjectsForFunctionalRelationException
575      */
576     public static void claimValue(Resource resource, Resource predicate, Object value)
577     throws ManyObjectsForFunctionalRelationException, ServiceException, DatabaseException {
578         writeGraph().claimLiteral(resource, predicate, value);
579     }
580     public static void claimValue(Resource resource, Resource predicate, Object value, Binding binding)
581     throws BindingException, ManyObjectsForFunctionalRelationException, ServiceException, DatabaseException {
582         writeGraph().claimValue(resource, value, binding);
583     }
584     public static void claimValue(Resource resource, Resource predicate, Resource inverse, Resource type, Object value, Binding binding)
585     throws BindingException, ManyObjectsForFunctionalRelationException, ServiceException, DatabaseException {
586         writeGraph().claimLiteral(resource, predicate, inverse, type, value, binding);
587     }
588
589     /**
590      * Makes sure that no statements matching the patterns (s,?p,?o) and
591      * (?o,?p',s), where ?p' is the inverse predicate of ?p, exist in the graph.
592      * In other words, removes all statements outgoing from the specified
593      * resource along with the inverses of those statements.
594      * 
595      * @param subject
596      * @throws ServiceException
597      */
598     public static void deny(Resource subject) throws ServiceException {
599         writeGraph().deny(subject);
600     }
601
602     /**
603      * Makes sure that no statements matching the patterns (s,p,?o) and
604      * (?o,p',s), where p' is the inverse predicate of p, exist in the graph.
605      * Also statements where <code>isSubrelationOf(p, predicate)</code> returns
606      * <code>true</code> shall be removed. In other words, removes all
607      * statements outgoing from the specified resource with the specified
608      * predicate or any of its subrelations, along with the inverses of those
609      * statements.
610      * 
611      * @param subject
612      * @throws ServiceException
613      */
614     public static void deny(Resource subject, Resource predicate) throws ServiceException {
615         writeGraph().deny(subject, predicate);
616     }
617
618     /**
619      * Makes sure that no statements matching the patterns (s,p,o) and (o,p',s),
620      * where p' is the inverse predicate of p, exist in the graph. Contrary to
621      * {@link #denyStatement(Resource, Resource, Resource)}, all statements
622      * where <code>isSubrelationOf(p, predicate)</code> returns
623      * <code>true</code> shall be removed. In other words, removes all
624      * statements between the specified subject and object with the specified
625      * predicate or any of its subrelations, along with the inverses of those
626      * statements.
627      * 
628      * @param subject
629      * @param predicate
630      * @param object
631      * @throws ServiceException
632      */
633     public static void deny(Resource subject, Resource predicate, Resource object) throws ServiceException {
634         writeGraph().deny(subject, predicate, object);
635     }
636
637     /**
638      * Makes sure that no statements matching the patterns (s,p,o) and (o,p',s),
639      * where p' is the inverse predicate of p, exist in the graph. In other
640      * words, removes the specified statement and its possible inverse.
641      * 
642      * <p>
643      * This method behaves exactly like {@link #deny(Statement)}, it just takes
644      * the arguments as resources instead of a statement.
645      * 
646      * @param subject
647      * @throws ServiceException
648      * 
649      * @see {@link #deny(Statement)}
650      */
651     public static void denyStatement(Resource subject, Resource predicate, Resource object) throws ServiceException {
652         writeGraph().denyStatement(subject, predicate, object);
653     }
654
655     /**
656      * Makes sure that the specified statements (s,p,o) and its inverse
657      * statements (o,p',s), where p' is the inverse predicate of p, do not exist
658      * in the graph.
659      * 
660      * <p>
661      * This method behaves exactly like
662      * {@link #denyStatement(Resource, Resource, Resource)}, it just takes the
663      * arguments as a statement instead of 3 resources.
664      * 
665      * @param statement
666      * 
667      * @see #denyStatement(Resource, Resource, Resource)
668      */
669     public static void deny(Statement statement) throws ServiceException {
670         writeGraph().deny(statement);
671     }
672
673     /**
674      * Removes all statements (resource,predicate,?o) and literal contained by
675      * ?o.
676      * 
677      * @param resource
678      * @param predicate
679      * @throws ManyObjectsForFunctionalRelationException
680      */
681     public static void denyValue(Resource resource, Resource predicate) throws ManyObjectsForFunctionalRelationException, ServiceException {
682         writeGraph().denyValue(resource, predicate);
683     }
684     
685 }
686