Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.yahoo.elide.datastores.multiplex.MultiplexTransaction Maven / Gradle / Ivy
/*
* Copyright 2016, Yahoo Inc.
* Licensed under the Apache License, Version 2.0
* See LICENSE file in project root for terms.
*/
package com.yahoo.elide.datastores.multiplex;
import com.yahoo.elide.core.DataStore;
import com.yahoo.elide.core.DataStoreTransaction;
import com.yahoo.elide.core.RelationshipType;
import com.yahoo.elide.core.RequestScope;
import com.yahoo.elide.core.EntityDictionary;
import com.yahoo.elide.core.exceptions.InvalidCollectionException;
import com.yahoo.elide.core.filter.FilterPredicate;
import com.yahoo.elide.core.filter.Operator;
import com.yahoo.elide.core.filter.expression.FilterExpression;
import com.yahoo.elide.core.filter.expression.PredicateExtractionVisitor;
import com.yahoo.elide.core.pagination.Pagination;
import com.yahoo.elide.core.sort.Sorting;
import com.yahoo.elide.security.User;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.io.Serializable;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Optional;
import java.util.Set;
/**
* Multiplex transaction handler. Process each sub-database transactions within a single transaction.
* If any commit fails in process, reverse any commits already completed.
*/
@Slf4j
public abstract class MultiplexTransaction implements DataStoreTransaction {
protected final LinkedHashMap transactions;
protected final MultiplexManager multiplexManager;
protected final DataStoreTransaction lastDataStoreTransaction;
/**
* Multiplex transaction handler.
* @param multiplexManager associated manager
*/
public MultiplexTransaction(MultiplexManager multiplexManager) {
this.multiplexManager = multiplexManager;
this.transactions = new LinkedHashMap<>(multiplexManager.dataStores.size());
// create each subordinate transaction
DataStoreTransaction transaction = null;
for (DataStore dataStore : multiplexManager.dataStores) {
transaction = beginTransaction(dataStore);
transactions.put(dataStore, transaction);
}
lastDataStoreTransaction = transaction;
}
protected abstract DataStoreTransaction beginTransaction(DataStore dataStore);
@Override
public User accessUser(Object opaqueUser) {
User user = new User(opaqueUser);
for (DataStore dataStore : multiplexManager.dataStores) {
DataStoreTransaction transaction = transactions.get(dataStore);
user = transaction.accessUser(user.getOpaqueUser());
}
return user;
}
@Override
public void createObject(Object entity, RequestScope scope) {
getTransaction(entity).createObject(entity, scope);
}
@Override
public Object loadObject(Class> entityClass,
Serializable id,
Optional filterExpression,
RequestScope scope) {
return getTransaction(entityClass).loadObject(entityClass, id, filterExpression, scope);
}
@Override
public Iterable loadObjects(
Class> entityClass,
Optional filterExpression,
Optional sorting,
Optional pagination,
RequestScope scope) {
return getTransaction(entityClass).loadObjects(entityClass,
filterExpression,
sorting,
pagination,
scope);
}
@Override
public void flush(RequestScope requestScope) {
transactions.values().forEach(dataStoreTransaction -> dataStoreTransaction.flush(requestScope));
}
@Override
public void preCommit() {
transactions.values().forEach(DataStoreTransaction::preCommit);
}
@Override
public void commit(RequestScope scope) {
// flush all before commit
flush(scope);
transactions.values().forEach(dataStoreTransaction -> dataStoreTransaction.commit(scope));
}
@Override
public void close() throws IOException {
IOException cause = null;
for (DataStoreTransaction transaction : transactions.values()) {
try {
transaction.close();
} catch (IOException | Error | RuntimeException e) {
if (cause != null) {
cause.addSuppressed(e);
} else if (e instanceof IOException) {
cause = (IOException) e;
} else {
cause = new IOException(e);
}
}
}
transactions.clear();
if (cause != null) {
throw cause;
}
}
protected DataStoreTransaction getTransaction(Object object) {
return getTransaction(object.getClass());
}
protected DataStoreTransaction getTransaction(Class> cls) {
DataStoreTransaction transaction = transactions.get(this.multiplexManager.getSubManager(cls));
if (transaction == null) {
Class entityClass = multiplexManager.getDictionary().lookupEntityClass(cls);
throw new InvalidCollectionException(entityClass == null ? cls.getName() : entityClass.getName());
}
return transaction;
}
protected DataStoreTransaction getRelationTransaction(Object object, String relationName) {
EntityDictionary dictionary = multiplexManager.getDictionary();
Class> relationClass = dictionary.getParameterizedType(object, relationName);
return getTransaction(relationClass);
}
@Override
public Object getRelation(DataStoreTransaction relationTx,
Object entity,
String relationName,
Optional filterExpression,
Optional sorting,
Optional pagination,
RequestScope scope) {
relationTx = getRelationTransaction(entity, relationName);
DataStoreTransaction entityTransaction = getTransaction(entity.getClass());
EntityDictionary dictionary = scope.getDictionary();
Class> relationClass = dictionary.getParameterizedType(entity, relationName);
String idFieldName = dictionary.getIdFieldName(relationClass);
// If different transactions, check if bridgeable and try to bridge
if (entityTransaction != relationTx && relationTx instanceof BridgeableTransaction) {
RelationshipType relationType = dictionary.getRelationshipType(entity.getClass(), relationName);
BridgeableTransaction bridgeableTransaction = (BridgeableTransaction) relationTx;
if (relationType.isToMany()) {
return filterExpression
.map(fe -> {
Serializable id = extractId(fe, idFieldName, entity, relationClass, dictionary);
if (id == null) {
return bridgeableTransaction.bridgeableLoadObjects(this,
entity, relationName, filterExpression, sorting, pagination, scope);
} else {
return bridgeableTransaction.bridgeableLoadObject(this,
entity, relationName, id, filterExpression, scope);
}
})
.orElseGet(() -> bridgeableTransaction.bridgeableLoadObjects(this,
entity, relationName, filterExpression, sorting, pagination, scope));
} else {
return filterExpression
.map(fe -> {
Serializable id = extractId(fe, idFieldName, entity, relationClass, dictionary);
return bridgeableTransaction.bridgeableLoadObject(this,
entity, relationName, id, filterExpression, scope);
})
.orElseGet(() -> bridgeableTransaction.bridgeableLoadObject(this,
entity, relationName, null, filterExpression, scope));
}
}
// Otherwise, rely on existing underlying transaction to call correctly into relationTx
return entityTransaction.getRelation(relationTx, entity,
relationName, filterExpression, sorting, pagination, scope);
}
@Override
public void updateToManyRelation(DataStoreTransaction relationTx,
Object entity, String relationName,
Set newRelationships,
Set deletedRelationships,
RequestScope scope) {
relationTx = getRelationTransaction(entity, relationName);
DataStoreTransaction entityTransaction = getTransaction(entity.getClass());
entityTransaction.updateToManyRelation(relationTx, entity, relationName,
newRelationships, deletedRelationships, scope);
}
@Override
public void updateToOneRelation(DataStoreTransaction relationTx, Object entity,
String relationName, Object relationshipValue, RequestScope scope) {
relationTx = getRelationTransaction(entity, relationName);
DataStoreTransaction entityTransaction = getTransaction(entity.getClass());
entityTransaction.updateToOneRelation(relationTx, entity, relationName,
relationshipValue, scope);
}
@Override
public Object getAttribute(Object entity,
String attributeName, RequestScope scope) {
DataStoreTransaction transaction = getTransaction(entity.getClass());
return transaction.getAttribute(entity, attributeName, scope);
}
@Override
public void setAttribute(Object entity, String attributeName, Object attributeValue, RequestScope scope) {
DataStoreTransaction transaction = getTransaction(entity.getClass());
transaction.setAttribute(entity, attributeName, attributeValue, scope);
}
private Serializable extractId(FilterExpression filterExpression,
String idFieldName,
Object parent,
Class> relationClass,
EntityDictionary dictionary) {
Collection predicates = filterExpression.accept(new PredicateExtractionVisitor());
for (FilterPredicate predicate : predicates) {
List values = predicate.getValues();
Class> entityClass = dictionary.getParameterizedType(parent, predicate.getLeafEntityType());
if (relationClass == entityClass
&& predicate.getOperator() == Operator.IN
&& idFieldName.equals(predicate.getField())
&& values.size() == 1) {
return (Serializable) values.get(0);
}
}
return null;
}
}