/******************************************************************************* * Copyright (c) 2007 VTT Technical Research Centre of Finland and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.db.common.utils; import java.util.Collection; import java.util.Set; import java.util.concurrent.Semaphore; import org.simantics.databoard.accessor.Accessor; import org.simantics.databoard.binding.Binding; import org.simantics.databoard.type.Datatype; import org.simantics.db.ReadGraph; import org.simantics.db.RequestProcessor; import org.simantics.db.Resource; import org.simantics.db.Statement; import org.simantics.db.WriteGraph; import org.simantics.db.WriteOnlyGraph; import org.simantics.db.common.request.DelayedWriteRequest; import org.simantics.db.exception.AdaptionException; import org.simantics.db.exception.BindingException; import org.simantics.db.exception.CancelTransactionException; import org.simantics.db.exception.DatabaseException; import org.simantics.db.exception.DoesNotContainValueException; import org.simantics.db.exception.ManyObjectsForFunctionalRelationException; import org.simantics.db.exception.NoInverseException; import org.simantics.db.exception.NoSingleResultException; import org.simantics.db.exception.ResourceNotFoundException; import org.simantics.db.exception.RuntimeDatabaseException; import org.simantics.db.exception.ServiceException; import org.simantics.db.exception.ValidationException; import org.simantics.db.procedure.Procedure; import org.simantics.db.request.DelayedWrite; import org.simantics.db.request.Read; import org.simantics.db.request.Write; import org.simantics.utils.datastructures.Callback; /** * Synchronous Transaction.

* * Hint: Import all methods as static. * import static org.simantics.db.Transaction.*; * * Remember also to change Eclipse Preferences: * Organize Imports: number of static imports needed for .* on vakiona 99, siihen vaikka 3 * * * Usage A: * * startTransaction(session, true); * try { * ... * commit(); * } finally { * endTransaction(); * } * * Usage B: * setGraph(g); * ... * setGraph(null); * * @author Toni Kalajainen */ public class Transaction { public static enum Type { READ, WRITE, DELAYED_WRITE } /** Thread local transactions */ private static ThreadLocal transactions = new ThreadLocal(); static private class TransactionInfo { WriteGraph wg; ReadGraph rg; /** Semaphore that is released when the transaction is complete */ Semaphore ts; /** Semaphore that is release after transaction ended */ Semaphore es; /** Error */ DatabaseException error; /** */ boolean commit = false; TransactionInfo() { } TransactionInfo(Object graph) { if (graph instanceof WriteGraph) { wg = (WriteGraph) graph; rg = (ReadGraph) graph; } else if (graph instanceof ReadGraph) { wg = null; rg = (ReadGraph) graph; } else { throw new RuntimeDatabaseException("Not a sync graph"); } } } public static ReadGraph readGraph() { TransactionInfo t = transactions.get(); return t == null ? null : t.rg; } public static WriteGraph writeGraph() { TransactionInfo t = transactions.get(); return t == null ? null : t.wg; } public static Object setGraph(Object graph) { if (graph==null) { transactions.set(null); return null; } { Object oldGraph = null; TransactionInfo t = transactions.get(); if (t!=null) { oldGraph = t.rg; if (graph instanceof WriteGraph) { t.wg = (WriteGraph) graph; t.rg = (ReadGraph) graph; } else if (graph instanceof ReadGraph) { t.wg = null; t.rg = (ReadGraph) graph; } else { throw new RuntimeDatabaseException("Not a sync graph"); } } else { t = new TransactionInfo(graph); } transactions.set(t); return oldGraph; } } public static void startTransaction(RequestProcessor processor, boolean write) throws DatabaseException { startTransaction(processor, write ? Type.WRITE : Type.READ); } public static void startTransaction(RequestProcessor processor, Type type) throws DatabaseException { switch (type) { case READ: { if (transactions.get()!=null) throw new RuntimeDatabaseException("There is already a transaction."); final Semaphore started = new Semaphore(0); final TransactionInfo t = new TransactionInfo(); t.es = new Semaphore(0); t.ts = new Semaphore(0); transactions.set(t); Read request = new Read() { @Override public Object perform(ReadGraph g) throws DatabaseException { t.wg = null; t.rg = g; started.release(); try { t.ts.acquire(); } catch (InterruptedException e) { } return null; } }; Procedure procedure = new Procedure() { @Override public void execute(Object result) { t.es.release(9999); } @Override public void exception(Throwable ex) { if (ex instanceof DatabaseException) t.error = (DatabaseException) ex; else { t.error = new DatabaseException(ex); } t.es.release(9999); } }; processor.asyncRequest(request, procedure); // Wait until transaction has started try { // Sleep this thread until transaction has started started.acquire(1); } catch (InterruptedException e) { throw new DatabaseException("Thread was interrupted."); } finally { } } break; case WRITE: { if (transactions.get()!=null) throw new RuntimeDatabaseException("There is already a transaction."); final Semaphore started = new Semaphore(0); final TransactionInfo t = new TransactionInfo(); t.es = new Semaphore(0); t.ts = new Semaphore(0); transactions.set(t); Callback callback = new Callback() { @Override public void run(DatabaseException parameter) { t.error = parameter; t.es.release(9999); } }; Write request = new Write() { @Override public void perform(WriteGraph g) throws DatabaseException { t.wg = g; t.rg = g; started.release(); try { t.ts.acquire(); } catch (InterruptedException e) { } if (!t.commit) throw new CancelTransactionException(); } }; processor.asyncRequest( request, callback ); // Wait until transaction has started try { // Sleep this thread until transaction has started started.acquire(1); } catch (InterruptedException e) { throw new DatabaseException("Thread was interrupted."); } finally { } } break; case DELAYED_WRITE: { if (transactions.get()!=null) throw new RuntimeDatabaseException("There is already a transaction."); final Semaphore started = new Semaphore(0); final TransactionInfo t = new TransactionInfo(); t.es = new Semaphore(0); t.ts = new Semaphore(0); transactions.set(t); Callback callback = new Callback() { @Override public void run(DatabaseException parameter) { t.error = parameter; t.es.release(9999); } }; DelayedWrite request = new DelayedWriteRequest() { @Override public void perform(WriteGraph g) throws DatabaseException { t.wg = g; t.rg = g; started.release(); try { t.ts.acquire(); } catch (InterruptedException e) { } if (!t.commit) throw new CancelTransactionException(); } }; processor.asyncRequest( request, callback ); // Wait until transaction has started try { // Sleep this thread until transaction has started started.acquire(1); } catch (InterruptedException e) { throw new DatabaseException("Thread was interrupted."); } finally { } } break; } } /** * Commits transaction if no error occurred * * @throws DatabaseException */ public static void endTransaction() throws DatabaseException { TransactionInfo t = transactions.get(); if (t == null) return; //throw new RuntimeDatabaseException("There is no transaction to commit."); t.ts.release(9999); try { t.es.acquire(); } catch (InterruptedException e) { throw new DatabaseException(e); } if (t.error!=null) { if (t.error instanceof CancelTransactionException==false) throw t.error; } transactions.set(null); } /** * Commits transaction * * @throws DatabaseException */ public static void commit() throws DatabaseException { TransactionInfo t = transactions.get(); if (t == null) throw new RuntimeDatabaseException("There is not transaction to commit."); t.commit = true; endTransaction(); } public static String getURI(Resource resource) throws ResourceNotFoundException, ValidationException, ServiceException { return readGraph().getPossibleURI(resource); } public static String getPossibleURI(Resource resource) throws ResourceNotFoundException, ValidationException, ServiceException { return readGraph().getPossibleURI(resource); } public static Resource getResource(String uri) throws ResourceNotFoundException, ValidationException, ServiceException { return readGraph().getResource(uri); } public static Resource getPossibleResource(String uri) throws ResourceNotFoundException, ValidationException, ServiceException { return readGraph().getPossibleResource(uri); } public static Resource getBuiltin(String id) throws ResourceNotFoundException, ServiceException { return readGraph().getBuiltin(id); } public static Collection getStatements(Resource subject, Resource relation) throws ManyObjectsForFunctionalRelationException, ServiceException { return readGraph().getStatements(subject, relation); } public static Collection getAssertedStatements(Resource subject, Resource relation) throws ManyObjectsForFunctionalRelationException, ServiceException { return readGraph().getAssertedStatements(subject, relation); } public static Collection getPredicates(Resource subject) throws ServiceException { return readGraph().getPredicates(subject); } public static Collection getPrincipalTypes(Resource subject) throws ServiceException { return readGraph().getPrincipalTypes(subject); } public static Set getTypes(Resource subject) throws ServiceException { return readGraph().getTypes(subject); } public static Set getSupertypes(Resource subject) throws ServiceException { return readGraph().getSupertypes(subject); } public static Set getSuperrelations(Resource subject) throws ServiceException { return readGraph().getSuperrelations(subject); } public static Collection getObjects(Resource subject, Resource relation) throws ManyObjectsForFunctionalRelationException, ServiceException { return readGraph().getObjects(subject, relation); } public static Collection getAssertedObjects(Resource subject, Resource relation) throws ManyObjectsForFunctionalRelationException, ServiceException { return readGraph().getAssertedObjects(subject, relation); } public static Resource getInverse(Resource relation) throws NoInverseException, ManyObjectsForFunctionalRelationException, ServiceException { return readGraph().getInverse(relation); } public static Resource getSingleObject(Resource subject, Resource relation) throws NoSingleResultException, ManyObjectsForFunctionalRelationException, ServiceException { return readGraph().getSingleObject(subject, relation); } public static Statement getSingleStatement(Resource subject, Resource relation) throws NoSingleResultException, ManyObjectsForFunctionalRelationException, ServiceException { return readGraph().getSingleStatement(subject, relation); } public static Resource getSingleType(Resource subject) throws NoSingleResultException, ServiceException { return readGraph().getSingleType(subject); } public static Resource getSingleType(Resource subject, Resource baseType) throws NoSingleResultException, ServiceException { return readGraph().getSingleType(subject, baseType); } public static T getValue(Resource subject) throws DoesNotContainValueException, ServiceException { return readGraph().getValue(subject); } public static T getValue(Resource subject, Binding binding) throws DoesNotContainValueException, BindingException, ServiceException { return readGraph().getValue(subject, binding); } public static T getRelatedValue(Resource subject, Resource relation) throws NoSingleResultException, DoesNotContainValueException, ServiceException { return readGraph().getRelatedValue(subject, relation); } public static T getRelatedValue(Resource subject, Resource relation, Binding binding) throws NoSingleResultException, DoesNotContainValueException, BindingException, ServiceException { return readGraph().getRelatedValue(subject, relation, binding); } public static T adapt(Resource resource, Class clazz) throws AdaptionException, ValidationException, ServiceException { return readGraph().adapt(resource, clazz); } public static T adaptUnique(Resource resource, Class clazz) throws AdaptionException, ValidationException, ServiceException { return readGraph().adaptUnique(resource, clazz); } public static Resource getPossibleInverse(Resource relation) throws ManyObjectsForFunctionalRelationException, ServiceException { return readGraph().getPossibleInverse(relation); } public static Resource getPossibleObject(Resource subject, Resource relation) throws ManyObjectsForFunctionalRelationException, ServiceException { return readGraph().getPossibleObject(subject, relation); } public static Statement getPossibleStatement(Resource subject, Resource relation) throws ManyObjectsForFunctionalRelationException, ServiceException { return readGraph().getPossibleStatement(subject, relation); } public static Resource getPossibleType(Resource subject, Resource baseType) throws ServiceException { return readGraph().getPossibleType(subject, baseType); } public static T getPossibleValue(Resource subject) throws ServiceException { return readGraph().getPossibleValue(subject); } public static T getPossibleValue(Resource subject, Binding binding) throws BindingException, ServiceException { return readGraph().getPossibleValue(subject, binding); } public static T getPossibleRelatedValue(Resource subject, Resource relation) throws ManyObjectsForFunctionalRelationException, ServiceException { return readGraph().getPossibleRelatedValue(subject, relation); } public static T getPossibleRelatedValue(Resource subject, Resource relation, Binding binding) throws ManyObjectsForFunctionalRelationException, BindingException, ServiceException { return readGraph().getPossibleRelatedValue(subject, relation, binding); } public static T getPossibleAdapter(Resource resource, Class clazz) throws ValidationException, ServiceException { return readGraph().getPossibleAdapter(resource, clazz); } public static T getPossibleUniqueAdapter(Resource resource, Class clazz) throws ValidationException, ServiceException { return readGraph().getPossibleUniqueAdapter(resource, clazz); } public static boolean isInstanceOf(Resource resource, Resource type) throws ServiceException { return readGraph().isInstanceOf(resource, type); } public static boolean isInheritedFrom(Resource resource, Resource type) throws ServiceException { return readGraph().isInheritedFrom(resource, type); } public static boolean isSubrelationOf(Resource resource, Resource relation) throws ServiceException { return readGraph().isSubrelationOf(resource, relation); } public static boolean hasStatement(Resource subject) throws ServiceException { return readGraph().hasStatement(subject); } public static boolean hasStatement(Resource subject, Resource relation) throws ServiceException { return readGraph().hasStatement(subject, relation); } public static boolean hasStatement(Resource subject, Resource relation, Resource object) throws ServiceException { return readGraph().hasStatement(subject, relation, object); } public static boolean hasValue(Resource subject) throws ServiceException { return readGraph().hasValue(subject); } public static Datatype getDataType(Resource subject) throws DatabaseException { return readGraph().getDataType(subject); } public static T getAccessor(Resource subject) throws DatabaseException { return readGraph().getAccessor(subject); } /** * Makes sure that the statements (s,p,o) and (o,p',s) are found in the * graph, where p' is the inverse predicate of p. Contrary to * {@link WriteOnlyGraph#claim(Resource, Resource, Resource, Resource)} this * method assures that the the statement and its inverse are semantically * valid after the invocation of this method. * * @param subject subject, i.e. source resource of the statement to be * claimed * @param predicate predicate resource of the statement to be claimed * @param object object, i.e. target resource of the statement to be claimed * @throws ServiceException */ public static void claim(Resource subject, Resource predicate, Resource object) throws ServiceException { writeGraph().claim(subject, predicate, object); } /** * Sets literal value related to the specified resource with the specified * predicate. If such value exists (s,p), the value is overridden with the * new specified value. * * @param resource * @param predicate * @param value Value of the literal (boxed primitive/String or * primitive/String array) * @throws ManyObjectsForFunctionalRelationException */ public static void claimValue(Resource resource, Resource predicate, Object value) throws ManyObjectsForFunctionalRelationException, ServiceException, DatabaseException { writeGraph().claimLiteral(resource, predicate, value); } public static void claimValue(Resource resource, Resource predicate, Object value, Binding binding) throws BindingException, ManyObjectsForFunctionalRelationException, ServiceException, DatabaseException { writeGraph().claimValue(resource, value, binding); } public static void claimValue(Resource resource, Resource predicate, Resource inverse, Resource type, Object value, Binding binding) throws BindingException, ManyObjectsForFunctionalRelationException, ServiceException, DatabaseException { writeGraph().claimLiteral(resource, predicate, inverse, type, value, binding); } /** * Makes sure that no statements matching the patterns (s,?p,?o) and * (?o,?p',s), where ?p' is the inverse predicate of ?p, exist in the graph. * In other words, removes all statements outgoing from the specified * resource along with the inverses of those statements. * * @param subject * @throws ServiceException */ public static void deny(Resource subject) throws ServiceException { writeGraph().deny(subject); } /** * Makes sure that no statements matching the patterns (s,p,?o) and * (?o,p',s), where p' is the inverse predicate of p, exist in the graph. * Also statements where isSubrelationOf(p, predicate) returns * true shall be removed. In other words, removes all * statements outgoing from the specified resource with the specified * predicate or any of its subrelations, along with the inverses of those * statements. * * @param subject * @throws ServiceException */ public static void deny(Resource subject, Resource predicate) throws ServiceException { writeGraph().deny(subject, predicate); } /** * Makes sure that no statements matching the patterns (s,p,o) and (o,p',s), * where p' is the inverse predicate of p, exist in the graph. Contrary to * {@link #denyStatement(Resource, Resource, Resource)}, all statements * where isSubrelationOf(p, predicate) returns * true shall be removed. In other words, removes all * statements between the specified subject and object with the specified * predicate or any of its subrelations, along with the inverses of those * statements. * * @param subject * @param predicate * @param object * @throws ServiceException */ public static void deny(Resource subject, Resource predicate, Resource object) throws ServiceException { writeGraph().deny(subject, predicate, object); } /** * Makes sure that no statements matching the patterns (s,p,o) and (o,p',s), * where p' is the inverse predicate of p, exist in the graph. In other * words, removes the specified statement and its possible inverse. * *

* This method behaves exactly like {@link #deny(Statement)}, it just takes * the arguments as resources instead of a statement. * * @param subject * @throws ServiceException * * @see {@link #deny(Statement)} */ public static void denyStatement(Resource subject, Resource predicate, Resource object) throws ServiceException { writeGraph().denyStatement(subject, predicate, object); } /** * Makes sure that the specified statements (s,p,o) and its inverse * statements (o,p',s), where p' is the inverse predicate of p, do not exist * in the graph. * *

* This method behaves exactly like * {@link #denyStatement(Resource, Resource, Resource)}, it just takes the * arguments as a statement instead of 3 resources. * * @param statement * * @see #denyStatement(Resource, Resource, Resource) */ public static void deny(Statement statement) throws ServiceException { writeGraph().deny(statement); } /** * Removes all statements (resource,predicate,?o) and literal contained by * ?o. * * @param resource * @param predicate * @throws ManyObjectsForFunctionalRelationException */ public static void denyValue(Resource resource, Resource predicate) throws ManyObjectsForFunctionalRelationException, ServiceException { writeGraph().denyValue(resource, predicate); } }