
org.dizitart.no2.transaction.NitriteTransaction Maven / Gradle / Ivy
package org.dizitart.no2.transaction;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.dizitart.no2.Nitrite;
import org.dizitart.no2.NitriteConfig;
import org.dizitart.no2.collection.Document;
import org.dizitart.no2.collection.NitriteCollection;
import org.dizitart.no2.collection.NitriteId;
import org.dizitart.no2.common.concurrent.LockService;
import org.dizitart.no2.common.module.NitriteModule;
import org.dizitart.no2.exceptions.TransactionException;
import org.dizitart.no2.repository.ObjectRepository;
import org.dizitart.no2.repository.EntityDecorator;
import org.dizitart.no2.store.NitriteMap;
import org.dizitart.no2.store.NitriteStore;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import static org.dizitart.no2.common.util.ObjectUtils.findRepositoryName;
import static org.dizitart.no2.common.util.ObjectUtils.findRepositoryNameByDecorator;
/**
* @author Anindya Chatterjee
* @since 4.0
*/
@Slf4j(topic = "nitrite")
class NitriteTransaction implements Transaction {
private final Nitrite nitrite;
private final LockService lockService;
private TransactionStore> transactionStore;
private TransactionConfig transactionConfig;
private Map contextMap;
private Map collectionRegistry;
private Map> repositoryRegistry;
private Map> undoRegistry;
@Getter
private String id;
private TransactionState state;
public NitriteTransaction(Nitrite nitrite, LockService lockService) {
this.nitrite = nitrite;
this.lockService = lockService;
prepare();
}
@Override
public synchronized NitriteCollection getCollection(String name) {
checkState();
if (collectionRegistry.containsKey(name)) {
return collectionRegistry.get(name);
}
NitriteCollection primary;
if (nitrite.hasCollection(name)) {
primary = nitrite.getCollection(name);
} else {
throw new TransactionException("Collection " + name + " does not exists");
}
NitriteMap txMap = transactionStore.openMap(name,
NitriteId.class, Document.class);
TransactionContext context = new TransactionContext();
context.setCollectionName(name);
context.setNitriteMap(txMap);
context.setJournal(new LinkedList<>());
context.setConfig(transactionConfig);
NitriteCollection txCollection = new DefaultTransactionalCollection(primary, context);
collectionRegistry.put(name, txCollection);
contextMap.put(name, context);
return txCollection;
}
@Override
@SuppressWarnings("unchecked")
public synchronized ObjectRepository getRepository(Class type) {
checkState();
String name = findRepositoryName(type, null);
if (repositoryRegistry.containsKey(name)) {
return (ObjectRepository) repositoryRegistry.get(name);
}
ObjectRepository primary;
if (nitrite.hasRepository(type)) {
primary = nitrite.getRepository(type);
} else {
throw new TransactionException("Repository of type " + type.getName() + " does not exists");
}
NitriteMap txMap = transactionStore.openMap(name,
NitriteId.class, Document.class);
TransactionContext context = new TransactionContext();
context.setCollectionName(name);
context.setNitriteMap(txMap);
context.setJournal(new LinkedList<>());
context.setConfig(transactionConfig);
NitriteCollection primaryCollection = primary.getDocumentCollection();
NitriteCollection backingCollection = new DefaultTransactionalCollection(primaryCollection, context);
ObjectRepository txRepository = new DefaultTransactionalRepository<>(type,
primary, backingCollection, transactionConfig);
repositoryRegistry.put(name, txRepository);
contextMap.put(name, context);
return txRepository;
}
@Override
@SuppressWarnings("unchecked")
public synchronized ObjectRepository getRepository(Class type, String key) {
checkState();
String name = findRepositoryName(type, key);
if (repositoryRegistry.containsKey(name)) {
return (ObjectRepository) repositoryRegistry.get(name);
}
ObjectRepository primary;
if (nitrite.hasRepository(type, key)) {
primary = nitrite.getRepository(type, key);
} else {
throw new TransactionException("Repository of type " + type.getName()
+ " and key " + key + " does not exists");
}
NitriteMap txMap = transactionStore.openMap(name,
NitriteId.class, Document.class);
TransactionContext context = new TransactionContext();
context.setCollectionName(name);
context.setNitriteMap(txMap);
context.setJournal(new LinkedList<>());
context.setConfig(transactionConfig);
NitriteCollection primaryCollection = primary.getDocumentCollection();
NitriteCollection backingCollection = new DefaultTransactionalCollection(primaryCollection, context);
ObjectRepository txRepository = new DefaultTransactionalRepository<>(type,
primary, backingCollection, transactionConfig);
repositoryRegistry.put(name, txRepository);
contextMap.put(name, context);
return txRepository;
}
@Override
@SuppressWarnings("unchecked")
public synchronized ObjectRepository getRepository(EntityDecorator entityDecorator) {
checkState();
String name = findRepositoryNameByDecorator(entityDecorator, null);
if (repositoryRegistry.containsKey(name)) {
return (ObjectRepository) repositoryRegistry.get(name);
}
ObjectRepository primary;
if (nitrite.hasRepository(entityDecorator)) {
primary = nitrite.getRepository(entityDecorator);
} else {
throw new TransactionException("Repository of type " + entityDecorator.getEntityName() + " does not exists");
}
NitriteMap txMap = transactionStore.openMap(name,
NitriteId.class, Document.class);
TransactionContext context = new TransactionContext();
context.setCollectionName(name);
context.setNitriteMap(txMap);
context.setJournal(new LinkedList<>());
context.setConfig(transactionConfig);
NitriteCollection primaryCollection = primary.getDocumentCollection();
NitriteCollection backingCollection = new DefaultTransactionalCollection(primaryCollection, context);
ObjectRepository txRepository = new DefaultTransactionalRepository<>(entityDecorator,
primary, backingCollection, transactionConfig);
repositoryRegistry.put(name, txRepository);
contextMap.put(name, context);
return txRepository;
}
@Override
@SuppressWarnings("unchecked")
public synchronized ObjectRepository getRepository(EntityDecorator entityDecorator, String key) {
checkState();
String name = findRepositoryNameByDecorator(entityDecorator, key);
if (repositoryRegistry.containsKey(name)) {
return (ObjectRepository) repositoryRegistry.get(name);
}
ObjectRepository primary;
if (nitrite.hasRepository(entityDecorator, key)) {
primary = nitrite.getRepository(entityDecorator, key);
} else {
throw new TransactionException("Repository of type " + entityDecorator.getEntityName()
+ " and key " + key + " does not exists");
}
NitriteMap txMap = transactionStore.openMap(name,
NitriteId.class, Document.class);
TransactionContext context = new TransactionContext();
context.setCollectionName(name);
context.setNitriteMap(txMap);
context.setJournal(new LinkedList<>());
context.setConfig(transactionConfig);
NitriteCollection primaryCollection = primary.getDocumentCollection();
NitriteCollection backingCollection = new DefaultTransactionalCollection(primaryCollection, context);
ObjectRepository txRepository = new DefaultTransactionalRepository<>(entityDecorator,
primary, backingCollection, transactionConfig);
repositoryRegistry.put(name, txRepository);
contextMap.put(name, context);
return txRepository;
}
@Override
public synchronized void commit() {
checkState();
this.state = TransactionState.PartiallyCommitted;
for (Map.Entry contextEntry : contextMap.entrySet()) {
String collectionName = contextEntry.getKey();
TransactionContext transactionContext = contextEntry.getValue();
Stack undoLog = undoRegistry.containsKey(collectionName)
? undoRegistry.get(collectionName) : new Stack<>();
// put collection level lock
Lock lock = lockService.getWriteLock(collectionName);
try {
lock.lock();
Queue commitLog = transactionContext.getJournal();
int length = commitLog.size();
for (int i = 0; i < length; i++) {
JournalEntry entry = commitLog.poll();
if (entry != null) {
Command commitCommand = entry.getCommit();
if (commitCommand != null) {
try {
commitCommand.execute();
} finally {
UndoEntry undoEntry = new UndoEntry();
undoEntry.setCollectionName(collectionName);
undoEntry.setRollback(entry.getRollback());
undoLog.push(undoEntry);
}
}
}
}
} catch (TransactionException te) {
state = TransactionState.Failed;
log.error("Error while committing transaction", te);
throw te;
} catch (Exception e) {
state = TransactionState.Failed;
log.error("Error while committing transaction", e);
throw new TransactionException("Error committing transaction", e);
} finally {
undoRegistry.put(collectionName, undoLog);
transactionContext.getActive().set(false);
lock.unlock();
}
}
state = TransactionState.Committed;
close();
}
@Override
public synchronized void rollback() {
this.state = TransactionState.Aborted;
for (Map.Entry> entry : undoRegistry.entrySet()) {
String collectionName = entry.getKey();
Stack undoLog = entry.getValue();
// put collection level lock
Lock writeLock = lockService.getWriteLock(collectionName);
try {
writeLock.lock();
int size = undoLog.size();
for (int i = 0; i < size; i++) {
UndoEntry undoEntry = undoLog.pop();
if (undoEntry != null) {
Command rollbackCommand = undoEntry.getRollback();
rollbackCommand.execute();
}
}
} finally {
writeLock.unlock();
}
}
close();
}
@Override
public synchronized void close() {
try {
state = TransactionState.Closed;
for (TransactionContext context : contextMap.values()) {
context.getActive().set(false);
}
this.contextMap.clear();
this.collectionRegistry.clear();
this.repositoryRegistry.clear();
this.undoRegistry.clear();
this.transactionStore.close();
this.transactionConfig.close();
} catch (Exception e) {
throw new TransactionException("Error closing transaction", e);
}
}
public synchronized TransactionState getState() {
return state;
}
private void prepare() {
this.contextMap = new ConcurrentHashMap<>();
this.collectionRegistry = new ConcurrentHashMap<>();
this.repositoryRegistry = new ConcurrentHashMap<>();
this.undoRegistry = new ConcurrentHashMap<>();
this.id = UUID.randomUUID().toString();
NitriteStore> nitriteStore = nitrite.getStore();
NitriteConfig nitriteConfig = nitrite.getConfig();
this.transactionConfig = new TransactionConfig(nitriteConfig);
this.transactionConfig.loadModule(NitriteModule.module(new TransactionStore<>(nitriteStore)));
this.transactionConfig.autoConfigure();
this.transactionConfig.initialize();
this.transactionStore = (TransactionStore>) this.transactionConfig.getNitriteStore();
this.state = TransactionState.Active;
}
private void checkState() {
if (state != TransactionState.Active) {
throw new TransactionException("Transaction is not active");
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy