de.thorstenberger.examServer.dao.AbstractTransactionalFileIO.java Source code

Java tutorial

Introduction

Here is the source code for de.thorstenberger.examServer.dao.AbstractTransactionalFileIO.java

Source

/*
    
Copyright (C) 2009 Steffen Dienst
    
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
    
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
    
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package de.thorstenberger.examServer.dao;

import java.util.HashMap;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.transaction.file.FileResourceManager;
import org.apache.commons.transaction.file.ResourceManagerException;
import org.apache.commons.transaction.file.ResourceManagerSystemException;
import org.apache.commons.transaction.util.CommonsLoggingLogger;

import de.thorstenberger.taskmodel.TaskModelPersistenceException;

/**
 * Provide transactional file io functionality. Every access to any file within the directory specified in the
 * constructor has to happen via calls to the {@link FileResourceManager} returned by {@link #getFRM()}. No other ways
 * of accessing file contents of that directory are allowed, else the ACID properties won't be guranteed!
 *
 * <p>
 * Usage: Writing access
 * <ol>
 * <li>String txId = {@link #startTransaction()}</li>
 * <li>call (write|copy|move)Resource(txId,"filename") on {@link #getFRM()}</li>
 * <li>{@link #commitTransaction(String)} or {@link #rollback(String, Throwable)} for every exception</li>
 * </ol>
 * <p>
 * Reading only:
 * <ol>
 * <li>call readResource("filename") on {@link #getFRM()} without explicit start/commitTransaction calls</li>
 * <li>make sure to close the stream returned by readResource(String)!</li>
 * </ol>
 *
 * @author Steffen Dienst
 *
 */
public class AbstractTransactionalFileIO {
    final Log log = LogFactory.getLog(AbstractTransactionalFileIO.class);
    /**
     * Map of directories to {@link FileResourceManager} instances. This way we have exactly one manager
     * per directory.
     */
    private static final Map<String, FileResourceManager> resourceManagers = new HashMap();
    protected final String workingPath;

    /**
     * Provide file transactions for the given directory.
     *
     * @param workingDirectory
     */
    public AbstractTransactionalFileIO(final String workingDirectory) {
        this.workingPath = workingDirectory;
        initFileTransactionsForDirectory(workingDirectory);
    }

    /**
     * Commit the changes to the current working directory atomically.
     *
     * @param txId
     *            id of the current transaction
     */
    protected void commitTransaction(final String txId) {
        final FileResourceManager frm = getFRM();
        try {
            frm.prepareTransaction(txId);
            frm.commitTransaction(txId);
        } catch (final ResourceManagerException e) {
            rollback(txId, e);
            throw new TaskModelPersistenceException(e);
        }

    }

    /**
     * Get the current {@link FileResourceManager}. Use it for accessing file resources.
     *
     * @return
     */
    protected FileResourceManager getFRM() {
        return resourceManagers.get(this.workingPath);
    }

    /**
     * Create a new file transaction manager for the given directory.
     *
     * @param path
     *            directory
     */
    private synchronized void initFileTransactionsForDirectory(final String path) {
        if (resourceManagers.get(path) == null) {
            resourceManagers.put(path,
                    new FileResourceManager(path, path + "_dirty", false, new CommonsLoggingLogger(log)));
            try {
                resourceManagers.get(path).start();
            } catch (final ResourceManagerSystemException ex) {
                throw new TaskModelPersistenceException(ex);
            }
        }

    }

    /**
     * Rollback all changes to the current working directory.
     *
     * @param txId
     *            id of the current transaction
     * @param e
     *            cause of the rollback, will get wrapped in a {@link RuntimeException} and rethrown
     */
    protected void rollback(final String txId, final Throwable e) {
        try {
            getFRM().rollbackTransaction(txId);
        } catch (final ResourceManagerException e1) {
            throw new TaskModelPersistenceException(e);
        }
    }

    /**
     * Start a new transaction on the current working directory.
     *
     * @return an unique transaction id
     */
    protected String startTransaction() {
        final FileResourceManager frm = resourceManagers.get(workingPath);
        try {
            final String txId = frm.generatedUniqueTxId();
            frm.startTransaction(txId);
            return txId;
        } catch (final ResourceManagerSystemException e) {
            throw new TaskModelPersistenceException("Could not start new transaction on directory " + workingPath,
                    e);
        } catch (final ResourceManagerException e) {
            throw new TaskModelPersistenceException("Could not start new transaction on directory " + workingPath,
                    e);
        }
    }

}