All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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