edu.illinois.ncsa.springdata.Transaction.java Source code

Java tutorial

Introduction

Here is the source code for edu.illinois.ncsa.springdata.Transaction.java

Source

/*******************************************************************************
 * Copyright (c) 2012 University of Illinois/NCSA.  All rights reserved.
 * 
 *   National Center for Supercomputing Applications (NCSA)
 *   http://www.ncsa.illinois.edu/
 * 
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the 
 * "Software"), to deal with the Software without restriction, including 
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 * 
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimers.
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimers in the
 *   documentation and/or other materials provided with the distribution.
 * - Neither the names of University of Illinois, NCSA, nor the names
 *   of its contributors may be used to endorse or promote products
 *   derived from this Software without specific prior written permission.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR
 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
 ******************************************************************************/
package edu.illinois.ncsa.springdata;

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

/**
 * Class to wrap a transaction. The transaction will keep the entitymanager
 * alive during the lifetime of the transaction allowing for lazy loading. The
 * transaction is started using the start method and finished either by
 * committing it, or rolling it back.
 * 
 * @author Rob Kooper <kooper@illinois.edu>
 * 
 */
public class Transaction {
    private static Logger logger = LoggerFactory.getLogger(Transaction.class);
    private static Map<Thread, Map<Transaction, Exception>> transactions = new HashMap<Thread, Map<Transaction, Exception>>();

    private PlatformTransactionManager transactionManager;

    private TransactionStatus status;

    static {
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                logger.debug(getAllTransactions());
            }
        });
    }

    /**
     * Create a new instance of the Transaction using the given transaction
     * manger. This class should be created using SpringData.getTransaction.
     * 
     * @param transactionManager
     *            the transaction manager keeping track of transactions.
     */
    public Transaction(PlatformTransactionManager transactionManager) {
        this.transactionManager = transactionManager;
        this.status = null;
    }

    /**
     * Start a new transaction. This will call start with readonly set to false.
     * 
     * @throws Exception
     *             throws an exception if there was a problem creating the
     *             transaction or another transaction is already in use.
     */
    public void start() throws Exception {
        start(false);
    }

    /**
     * Start a new transaction. This will use the readonly flag as a hint for
     * the transaction manager..
     * 
     * @param readonly
     *            a hint for the transaction manager that the transaction should
     *            be used for readonly operations only.
     * @throws Exception
     *             throws an exception if there was a problem creating the
     *             transaction or another transaction is already in use.
     */
    public void start(boolean readonly) throws Exception {
        if (status != null) {
            throw (new Exception("Already have transaction open."));
        }
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setReadOnly(readonly);
        status = transactionManager.getTransaction(def);

        Map<Transaction, Exception> localtrans = transactions.get(Thread.currentThread());
        if (localtrans == null) {
            localtrans = new HashMap<Transaction, Exception>();
            transactions.put(Thread.currentThread(), localtrans);
        } else {
            if (localtrans.size() > 0) {
                logger.info(getThreadTransaction());
            }
        }
        localtrans.put(this, new Exception());
    }

    /**
     * Commit the transaction. This will write all the changes to the backing
     * store.
     * 
     * @throws Exception
     *             throws an exception if there was an error committing the
     *             transaction.
     */
    public void commit() throws Exception {
        if (status == null) {
            throw (new Exception("No transaction is open."));
        }
        transactionManager.commit(status);
        transactions.get(Thread.currentThread()).remove(this);
    }

    /**
     * Rolls back the transaction. This will remove all the changes to the
     * backing store.
     * 
     * @throws Exception
     *             throws an exception if there was an error committing the
     *             transaction.
     */
    public void rollback() throws Exception {
        if (status == null) {
            throw (new Exception("No transaction is open."));
        }
        transactionManager.rollback(status);
        transactions.get(Thread.currentThread()).remove(this);
    }

    public static String getAllTransactions() {
        StringBuilder sb = new StringBuilder();

        StackTraceElement[] elems = new Exception().getStackTrace();
        for (int j = 0; j < elems.length; j++) {
            if (!elems[j].toString().startsWith("edu.illinois.ncsa.springdata.Transaction")) {
                sb.append(String.format("Called from %s\n", elems[j].toString()));
                break;
            }
        }

        for (Thread thr : transactions.keySet()) {
            sb.append(String.format("%s\n", thr.getName()));
            getTransaction(sb, thr);
        }
        return sb.toString();
    }

    public static String getThreadTransaction() {
        StringBuilder sb = new StringBuilder();

        StackTraceElement[] elems = new Exception().getStackTrace();
        for (int j = 0; j < elems.length; j++) {
            if (!elems[j].toString().startsWith("edu.illinois.ncsa.springdata.Transaction")) {
                sb.append(String.format("Called from %s\n", elems[j].toString()));
                break;
            }
        }

        getTransaction(sb, Thread.currentThread());
        return sb.toString();
    }

    private static void getTransaction(StringBuilder sb, Thread thread) {
        Map<Transaction, Exception> transmap = transactions.get(thread);
        if (transmap == null) {
            sb.append("\tThere are no open transactions.\n");
        } else {
            sb.append(String.format("\tThere are %d open transactions : \n", transmap.size()));
            int i = 1;
            for (Entry<Transaction, Exception> entry : transmap.entrySet()) {
                StackTraceElement[] elems = entry.getValue().getStackTrace();
                for (int j = 0; j < elems.length; j++) {
                    if (!elems[j].toString().startsWith("edu.illinois.ncsa.springdata.Transaction")) {
                        sb.append(String.format("\t[%2d] created at %s\n", i++, elems[j].toString()));
                        break;
                    }
                }
            }
        }
    }
}