/*******************************************************************************
* 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 java.util.function.Consumer;
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;
/**
* 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);
Consumer callback = 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);
Consumer callback = 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);
}
}