Package org.xorm

Source Code of org.xorm.TransactionImpl

/*
  $Header: /cvsroot/xorm/xorm/src/org/xorm/TransactionImpl.java,v 1.36 2004/05/17 22:55:47 wbiggs Exp $

  This file is part of XORM.

  XORM 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.

  XORM 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 XORM; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
package org.xorm;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.TreeSet;

import javax.transaction.Status;
import javax.transaction.Synchronization;

import javax.jdo.InstanceCallbacks;
import javax.jdo.Transaction;
import javax.jdo.PersistenceManager;
import javax.jdo.JDODataStoreException;
import javax.jdo.JDOFatalDataStoreException;
import javax.jdo.JDOUserException;

import org.xorm.datastore.DatastoreDriver;
import org.xorm.datastore.DriverException;
import org.xorm.datastore.Row;

/**
* A transaction with ACID properties, to which may be attached
* any number of objects.
*/
public class TransactionImpl implements Transaction, I15d {
    private int status = Status.STATUS_NO_TRANSACTION;
    private Synchronization synchronization;

    // Objects in hash are InterfaceInvocationHandlers
    private HashSet objects = new HashSet();
    // and here, RelationshipProxies
    private HashSet relationships = new HashSet();

    private InterfaceManager mgr;
    private DatastoreDriver driver;
    private Options jdoOptions;

    public TransactionImpl(InterfaceManager mgr, DatastoreDriver driver, Options jdoOptions) {
        this.mgr = mgr;
        this.driver = driver;
        this.jdoOptions = jdoOptions;
    }

    public PersistenceManager getPersistenceManager() {
        return mgr;
    }

    InterfaceManager getInterfaceManager() {
        return mgr;
    }

    public DatastoreDriver getDriver() {
        return driver;
    }

    // Adds a proxy object to the transaction
    // Assumes the object is marked as transactional
    public void attach(Object o) {
        InterfaceInvocationHandler handler = InterfaceInvocationHandler.getHandler(o);
        objects.add(handler);
    }

    void attachRelationship(RelationshipProxy rp) {
        relationships.add(rp);
    }

    public boolean contains(Object o) {
        InterfaceInvocationHandler handler = InterfaceInvocationHandler.getHandler(o);
        return objects.contains(handler);
    }

    public Object get(Class clazz, Object primaryKey) {
        // TODO change implementation for performance
        Iterator i = objects.iterator();
        while (i.hasNext()) {
            InterfaceInvocationHandler handler = (InterfaceInvocationHandler)
                i.next();
            if (handler.getClassMapping().getMappedClass().equals(clazz)
                && handler.getObjectId().equals(primaryKey)) {
                return handler.getProxy();
            }
        }
        return null;
    }

    public void detach(Object o) {
        InterfaceInvocationHandler handler = InterfaceInvocationHandler.getHandler(o);
        objects.remove(handler);
    }

    public void detachRelationship(RelationshipProxy rp) {
        relationships.remove(rp);
    }

    public void begin() {
        begin(false);
    }

    public void begin(boolean readOnly) {
        // Avoid race conditions here
        synchronized (this) {
            if (isActive()) {
                throw new JDOUserException(I18N.msg("E_begin_active_txn"));
            }
            status = Status.STATUS_ACTIVE;
        }
        try {
            driver.begin(readOnly);
            //  } catch (DriverException e) {
        } catch (Throwable e) {
            e.printStackTrace();
            status = Status.STATUS_NO_TRANSACTION;
            throw new JDODataStoreException("Cannot begin transaction", e);
        }
    }
   
    private void notifyExit(boolean commit) {
        // Now tell everything the transaction's over.
        Iterator i = objects.iterator();
        while (i.hasNext()) {
            if (((InterfaceInvocationHandler) i.next())
                .exitTransaction(commit)) {
                i.remove();
            }
        }
        i = relationships.iterator();
        while (i.hasNext()) {
            ((RelationshipProxy) i.next())
                .exitTransaction(commit);
        }
    }

    public void rollback() {
        if (status == Status.STATUS_NO_TRANSACTION) {
            throw new JDOUserException(I18N.msg("E_rollback_no_txn"));
        }

        status = Status.STATUS_COMMITTING; // Is this correct?
       
        try {
            driver.rollback();
            //} catch (DriverException e) {
        } catch (Throwable e) {
            e.printStackTrace();
            throw new JDOFatalDataStoreException("Rollback failed", e);
        }
        notifyExit(false);
        reset();
        if (synchronization != null) {
            synchronization.afterCompletion(Status.STATUS_ROLLEDBACK);
        }
    }
   
    public void commit() {
        if (status == Status.STATUS_NO_TRANSACTION) {
            throw new JDOUserException(I18N.msg("E_commit_no_txn"));
        }

        status = Status.STATUS_COMMITTING;
        ArrayList driverExceptions = new ArrayList();
       
        if (synchronization != null) {
            synchronization.beforeCompletion();
        }
       
        // Iterate over objects and build the list of those which
        // need to be inserted/updated
        TreeSet tree = new TreeSet(new DependencyComparator());
        Iterator i = objects.iterator();
        while (i.hasNext()) {
            InterfaceInvocationHandler handler = (InterfaceInvocationHandler)
                i.next();
            if (handler.isPersistent()) {
                if (handler.isDirty() ||
                    handler.isNew() ||
                    handler.isDeleted()) {
                    tree.add(handler);
                }
            }
        }

        // Remove deleted many-to-many relationship rows
        i = relationships.iterator();
        while (i.hasNext()) {
            RelationshipProxy rp = (RelationshipProxy) i.next();
            if (rp.getRelationshipMapping().isMToN()) {
                try {
                    Iterator j = rp.getDeletedRows().iterator();
                    while (j.hasNext()) {
                        driver.delete((Row) j.next());
                        j.remove();
                    }
                } catch (DriverException e) {
                    driverExceptions.add(e);
                }
            }
        }
       
        // Now walk the list and take appropriate actions
        i = tree.iterator();
        while (i.hasNext()) {
            InterfaceInvocationHandler handler = (InterfaceInvocationHandler)
                i.next();
            try {
                if (handler.isNew()) {
                    if (!handler.isDeleted()) {
                        if (handler.getProxy() instanceof InstanceCallbacks) {
                            ((InstanceCallbacks) handler.getProxy()).jdoPreStore();
                        }
                        Object oldID = handler.getObjectId();
                        driver.create(handler.getRow());
                        handler.refreshObjectId();
                        Object newID = handler.getObjectId();
                        notifyIDChangedAll(tree, oldID, newID);
                       
                        // Create the 2nd level cache entry
                        mgr.addToCache(handler.getRow());
                    }
                } else if (handler.isDeleted()) {
                    if (handler.getProxy() instanceof InstanceCallbacks) {
                        ((InstanceCallbacks) handler.getProxy()).jdoPreDelete();
                    }
                    driver.delete(handler.getRow());
                   
                    // Remove from the 2nd level cache
                    mgr.removeFromCache(handler.getRow());
                } else if (handler.getRow().isDirty()) {
                    if (handler.getProxy() instanceof InstanceCallbacks) {
                        ((InstanceCallbacks) handler.getProxy()).jdoPreStore();
                    }
                    driver.update(handler.getRow());
                   
                    // Replace the 2nd level cache entry
                    mgr.addToCache(handler.getRow());
                }
            } catch (Throwable e) {
                e.printStackTrace();
                driverExceptions.add(e);
            }
        }
       
        // After data objects are inserted, insert new relationship rows
        i = relationships.iterator();
        while (i.hasNext()) {
            RelationshipProxy rp = (RelationshipProxy) i.next();
            if (rp.getRelationshipMapping().isMToN()) {
                try {
                    Iterator j = rp.getNewRows().iterator();
                    while (j.hasNext()) {
                        driver.create((Row) j.next());
                        j.remove();
                    }
                } catch (Throwable e) {
                    e.printStackTrace();
                    driverExceptions.add(e);
                }
            }
        }
       
        if (!driverExceptions.isEmpty()) {
            // An error occurred
            rollback();
            throw new JDODataStoreException("Errors writing to datastore, transaction rolled back", (Throwable[]) driverExceptions.toArray(new Throwable[0]));
        }
       
        try {
            driver.commit();
            status = Status.STATUS_COMMITTED;
        } catch (Throwable e) {
            e.printStackTrace();
            try {
                rollback();
                throw new JDODataStoreException("Commit failed, rolled back instead", e);
            } catch (JDOFatalDataStoreException e2) {
                throw new JDOFatalDataStoreException("Commit failed, could not rollback", e);
            }
        }
       
        // Tell the contained objects to perform state transitions
        notifyExit(true);
       
        if (synchronization != null) {
            synchronization.afterCompletion(status);
        }
        reset();
    }

    private void notifyIDChangedAll(TreeSet tree, Object oldID, Object newID) {
        Iterator i = tree.iterator();
        while (i.hasNext()) {
            InterfaceInvocationHandler handler = (InterfaceInvocationHandler)
                i.next();
            handler.notifyIDChanged(oldID, newID);
        }
        i = relationships.iterator();
        while (i.hasNext()) {
            RelationshipProxy rp = (RelationshipProxy)
                i.next();
            rp.notifyIDChanged(oldID, newID);
        }
    }
   
    private void reset() {
        status = Status.STATUS_NO_TRANSACTION;
        objects = new HashSet();
        relationships = new HashSet();
    }
   
    public boolean isActive() {
        return status != Status.STATUS_NO_TRANSACTION;
    }
   
    public void setSynchronization(Synchronization synchronization) {
        // Per JDO Spec 13.4.3, throw exception if called from callback
        if (status == Status.STATUS_COMMITTING) {
            throw new JDOUserException(I18N.msg("E_txn_committing"));
        }
        this.synchronization = synchronization;
    }
    public Synchronization getSynchronization() {
        return synchronization;
    }
   
    void refreshAll() {
        // Reloads all objects in the scope of the current transaction
        Iterator i = objects.iterator();
        while (i.hasNext()) {
            InterfaceInvocationHandler handler = (InterfaceInvocationHandler)
                i.next();
            mgr.refresh(handler.getProxy());
        }
    }
   
    // The JDO options get/set methods are wrapper pattern.
   
    public void setNontransactionalRead(boolean value) {
        jdoOptions.setNontransactionalRead(value);
    }

    public boolean getNontransactionalRead() {
        return jdoOptions.getNontransactionalRead();
    }
   
    public void setNontransactionalWrite(boolean value) {
        jdoOptions.setNontransactionalWrite(value);
    }
   
    public boolean getNontransactionalWrite() {
        return jdoOptions.getNontransactionalWrite();
    }
   
    public void setOptimistic(boolean value) {
        jdoOptions.setOptimistic(value);
    }
   
    public boolean getOptimistic() {
        return jdoOptions.getOptimistic();
    }
   
    public void setRetainValues(boolean value) {
        jdoOptions.setRetainValues(value);
    }
   
    public boolean getRetainValues() {
        return jdoOptions.getRetainValues();
    }
   
    public void setRestoreValues(boolean restoreValues) {
        jdoOptions.setRestoreValues(restoreValues);
    }
   
    public boolean getRestoreValues() {
        return jdoOptions.getRestoreValues();
    }
   
    protected void finalize() throws Throwable {
        if (isActive()) {
            closeImpl();
        }
    }
   
    /**
     * Releases active transactional resources by rolling back the
     * datastore transaction if necessary.
     */
    void closeImpl() {
        try {
            rollback();
            System.err.println(I18N.msg("W_finalize_active_txn"));
        } catch(Throwable e) {
            System.err.println(I18N.msg("W_finalize_active_txn_fail"));
            //no logger, and throwing an exception in finalize is bad.
            //Oh well, just System.err it.
            e.printStackTrace();
            status = Status.STATUS_NO_TRANSACTION;
        }
    }
}
TOP

Related Classes of org.xorm.TransactionImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.