Package com.caucho.jca.pool

Source Code of com.caucho.jca.pool.UserTransactionImpl

/*
* Copyright (c) 1998-2010 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source 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.
*
* Resin Open Source 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, or any warranty
* of NON-INFRINGEMENT.  See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
*   Free Software Foundation, Inc.
*   59 Temple Place, Suite 330
*   Boston, MA 02111-1307  USA
*
* @author Scott Ferguson
*/

package com.caucho.jca.pool;

import com.caucho.jca.ra.BeginResource;
import com.caucho.jca.ra.CloseResource;
import com.caucho.transaction.TransactionImpl;
import com.caucho.transaction.TransactionManagerImpl;
import com.caucho.util.L10N;

import javax.resource.spi.ConnectionRequestInfo;
import javax.resource.spi.ManagedConnectionFactory;
import javax.security.auth.Subject;
import javax.transaction.*;
import javax.transaction.xa.Xid;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* Implementation of the UserTransactionImpl for a thread instance.
*/
public class UserTransactionImpl
  implements UserTransaction
{
  private static final Logger log
    = Logger.getLogger(UserTransactionImpl.class.getName());
  private static final L10N L = new L10N(UserTransactionImpl.class);

  private TransactionManagerImpl _transactionManager;

  private ArrayList<UserPoolItem> _resources = new ArrayList<UserPoolItem>();
  private ArrayList<ManagedPoolItem> _poolItems = new ArrayList<ManagedPoolItem>();
  private ArrayList<BeginResource> _beginResources
    = new ArrayList<BeginResource>();
  private ArrayList<CloseResource> _closeResources
    = new ArrayList<CloseResource>();

  private boolean _isInContext;
  private boolean _isTransactionActive;
 
  /**
   * Creates the proxy.
  */
  public UserTransactionImpl(TransactionManagerImpl tm)
  {
    _transactionManager = tm;
  }
 
  /**
   * Sets the transaction's timeout.
   */
  public void setTransactionTimeout(int seconds)
    throws SystemException
  {
    _transactionManager.setTransactionTimeout(seconds);
  }
 
  /**
   * Gets the transaction's status
   */
  public int getStatus()
    throws SystemException
  {
    return _transactionManager.getStatus();
  }

  /**
   * inContext is valid within a managed UserTransactionImpl context, e.g
   * in a webApp, but not in a cron job.
   */
  public boolean isInContext()
  {
    return _isInContext;
  }

  /**
   * inContext is valid within a managed UserTransactionImpl context, e.g
   * in a webApp, but not in a cron job.
   */
  public void setInContext(boolean isInContext)
  {
    _isInContext = isInContext;
  }

  /**
   * Enlist a resource.
   */
  void enlistResource(UserPoolItem resource)
    throws SystemException, RollbackException
  {
    if (_resources.contains(resource))
      return;
   
    TransactionImpl xa = _transactionManager.getTransaction();
    if (xa != null && xa.isActive()) {
      ManagedPoolItem poolItem = resource.getXAPoolItem();

      enlistPoolItem(xa, poolItem);
    }
   
    _resources.add(resource);
  }

  private void enlistPoolItem(Transaction xa, ManagedPoolItem poolItem)
    throws SystemException, RollbackException
  {
    if (poolItem == null)
      return;
    else if (! poolItem.supportsTransaction()) {
      // server/164j
      return;
    }
   
    // XXX: new
    if (_poolItems.contains(poolItem))
      return;
 
    poolItem.setTransaction(this);

    if (xa instanceof TransactionImpl) {
      TransactionImpl xaImpl = (TransactionImpl) xa;
     
      // server/164l
      if (xaImpl.allowLocalTransactionOptimization())
  poolItem.enableLocalTransactionOptimization(true);
    }

    if (poolItem.getXid() == null)
      xa.enlistResource(poolItem);
   
    _poolItems.add(poolItem);
  }

  /**
   * Delist a pool item
   */
  void delistPoolItem(ManagedPoolItem poolItem, int flags)
    throws SystemException, RollbackException
  {
    Transaction xa = _transactionManager.getTransaction();

    try {
      if (xa != null)
  xa.delistResource(poolItem, flags);
    } finally {
      _poolItems.remove(poolItem);
    }
  }

  /**
   * Delist a resource.
   */
  void delistResource(UserPoolItem resource)
  {
    _resources.remove(resource);
  }

  /**
   * Enlist a resource automatically called when a transaction begins
   */
  public void enlistBeginResource(BeginResource resource)
  {
    _beginResources.add(resource);

    try {
      Transaction xa = _transactionManager.getTransaction();
      if (xa != null)
  resource.begin(xa);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  /**
   * Enlist a resource automatically closed when the context ends.
   */
  public void enlistCloseResource(CloseResource resource)
  {
    _closeResources.add(resource);
  }

  /**
   * Allocates a resource matching the parameters.  If none matches,
   * return null.
   */
  UserPoolItem allocate(ManagedConnectionFactory mcf,
      Subject subject,
      ConnectionRequestInfo info)
  {
    if (! _isTransactionActive)
      return null;
   
    ArrayList<ManagedPoolItem> poolItems = _poolItems;
    int length = poolItems.size();
   
    for (int i = 0; i < length; i++) {
      ManagedPoolItem poolItem = poolItems.get(i);

      UserPoolItem item = poolItem.allocateXA(mcf, subject, info);

      if (item != null)
  return item;
    }

    return null;
  }

  /**
   * Finds the pool item joined to this one.
   * return null.
   */
  ManagedPoolItem findJoin(ManagedPoolItem item)
  {
    if (! _isTransactionActive)
      return null;
   
    ArrayList<ManagedPoolItem> poolItems = _poolItems;
    int length = poolItems.size();
   
    for (int i = 0; i < length; i++) {
      ManagedPoolItem poolItem = poolItems.get(i);

      if (poolItem.isJoin(item))
  return poolItem;
    }

    return null;
  }

  /**
   * Returns the XID.
   */
  public Xid getXid()
    throws SystemException, RollbackException
  {
    TransactionImpl xa = (TransactionImpl) _transactionManager.getTransaction();

    if (xa != null)
      return xa.getXid();
    else
      return null;
  }

  /**
   * Returns the XID.
   */
  public int getEnlistedResourceCount()
    throws SystemException, RollbackException
  {
    TransactionImpl xa = (TransactionImpl) _transactionManager.getTransaction();

    if (xa != null)
      return xa.getEnlistedResourceCount();
    else
      return 0;
  }
 
  /**
   * Start the transaction.
   */
  public void begin()
    throws NotSupportedException, SystemException
  {
    if (_isTransactionActive)
      throw new IllegalStateException(L.l("UserTransaction.begin() is not allowed because an active transaction already exists.  This may be caused by either a missing commit/rollback or a nested begin().  Nested transactions are not supported."));
   
    _transactionManager.begin();
    _isTransactionActive = true;
    boolean isOkay = false;

    try {
      TransactionImpl xa = (TransactionImpl) _transactionManager.getTransaction();
      xa.setUserTransaction(this);
   
      _poolItems.clear();
   
      // enlist "cached" connections
      int length = _resources.size();

      for (int i = 0; i < length; i++) {
  UserPoolItem userPoolItem = _resources.get(i);

  for (int j = _poolItems.size() - 1; j >= 0; j--) {
    ManagedPoolItem poolItem = _poolItems.get(j);

    if (poolItem.share(userPoolItem)) {
      break;
    }
  }

  ManagedPoolItem xaPoolItem = userPoolItem.getXAPoolItem();
  if (! _poolItems.contains(xaPoolItem))
    _poolItems.add(xaPoolItem);
      }

      for (int i = 0; i < _poolItems.size(); i++) {
  ManagedPoolItem poolItem = _poolItems.get(i);

  poolItem.enableLocalTransactionOptimization(_poolItems.size() == 1);

  try {
    xa.enlistResource(poolItem);
  } catch (Exception e) {
          String message = L.l("Failed to begin UserTransaction due to: {0}", e);
          log.log(Level.SEVERE, message, e);

    throw new SystemException(message);
  }
      }

      // enlist begin resources
      for (int i = 0; i < _beginResources.size(); i++) {
  try {
    BeginResource resource = _beginResources.get(i);

    resource.begin(xa);
  } catch (Throwable e) {
    log.log(Level.WARNING, e.toString(), e);
  }
      }

      isOkay = true;
    } finally {
      if (! isOkay) {
  Exception e1 = new IllegalStateException(L.l("Rolling back transaction from failed begin."));
  e1.fillInStackTrace();
  log.log(Level.WARNING, e1.toString(), e1);
 
  // something has gone very wrong
  _isTransactionActive = false;

  ArrayList<ManagedPoolItem> recoveryList = new ArrayList<ManagedPoolItem>(_poolItems);
  _poolItems.clear();
  _resources.clear();

  for (int i = 0; i < recoveryList.size(); i++) {
    try {
      ManagedPoolItem item = recoveryList.get(i);

      item.abortConnection();
     
      item.destroy();
    } catch (Throwable e) {
      log.log(Level.FINE, e.toString(), e);
    }
  }
 
  _transactionManager.rollback();
      }
    }
  }

  /**
   * Suspends the transaction.
   */
  public UserTransactionSuspendState userSuspend()
  {
    if (! _isTransactionActive)
      throw new IllegalStateException(L.l("UserTransaction.suspend may only be called in a transaction, but no transaction is active."));

    _isTransactionActive = false;
   
    UserTransactionSuspendState state;
    state = new UserTransactionSuspendState(_poolItems);
    _poolItems.clear();

    return state;
  }

  /**
   * Resumes the transaction.
   */
  public void userResume(UserTransactionSuspendState state)
  {
    if (_isTransactionActive)
      throw new IllegalStateException(L.l("UserTransaction.resume may only be called outside of a transaction, because the resumed transaction must not conflict with an active transaction."));

    _isTransactionActive = true;

    _poolItems.addAll(state.getPoolItems());
  }
 
  /**
   * Marks the transaction as rollback only.
   */
  public void setRollbackOnly()
    throws IllegalStateException, SystemException
  {
    _transactionManager.setRollbackOnly();
  }
 
  /**
   * Marks the transaction as rollback only.
   */
  public void setRollbackOnly(Exception e)
    throws IllegalStateException
  {
    _transactionManager.setRollbackOnly(e);
  }
 
  /**
   * Commits the transaction
   */
  public void commit()
    throws IllegalStateException, RollbackException, HeuristicMixedException,
     HeuristicRollbackException, SecurityException, SystemException
  {
    try {
      // XXX: interaction with hessian XA
      if (! _isTransactionActive)
  throw new IllegalStateException("UserTransaction.commit() requires an active transaction.  Either the UserTransaction.begin() is missing or the transaction has already been committed or rolled back.");

      _transactionManager.commit();
    } finally {
      _poolItems.clear();

      _isTransactionActive = false;
    }
  }
 
  /**
   * Rolls the transaction back
   */
  public void rollback()
    throws IllegalStateException, SecurityException, SystemException
  {
    try {
      _transactionManager.rollback();
    } finally {
      _isTransactionActive = false;
     
      _poolItems.clear();
    }
  }

  /**
   * Aborts the transaction.
   */
  public void abortTransaction()
    throws IllegalStateException
  {
    IllegalStateException exn = null;

    _isInContext = false;
   
    boolean isTransactionActive = _isTransactionActive;
    _isTransactionActive = false;

    if (! isTransactionActive && _poolItems.size() > 0) {
      Exception e = new IllegalStateException("Internal error: user transaction pool broken because poolItems exist, but no transaction is active.");
      log.log(Level.WARNING, e.toString(), e);
    }
   
    _poolItems.clear();

    if (isTransactionActive) {
      try {
  TransactionImpl xa = (TransactionImpl) _transactionManager.getTransaction();

  exn = new IllegalStateException(L.l("Transactions must have a commit() or rollback() in a finally block."));
     
  log.warning("Rolling back dangling transaction.  All transactions must have a commit() or rollback() in a finally block.");
     
  _transactionManager.rollback();
      } catch (Throwable e) {
  log.log(Level.WARNING, e.toString());
      }

    }

    _beginResources.clear();
   
    while (_closeResources.size() > 0) {
      try {
  CloseResource resource;

  resource = _closeResources.remove(_closeResources.size() - 1);
  resource.close();
      } catch (Throwable e) {
  log.log(Level.WARNING, e.toString(), e);
      }
    }

    boolean hasWarning = false;
   
    while (_resources.size() > 0) {
      UserPoolItem userPoolItem = _resources.remove(_resources.size() - 1);

      if (! userPoolItem.isCloseDanglingConnections())
  continue;
     
      if (! hasWarning) {
  hasWarning = true;

  log.warning("Closing dangling connections.  All connections must have a close() in a finally block.");
      }
     
      try {
  IllegalStateException stackTrace = userPoolItem.getAllocationStackTrace();

  if (stackTrace != null)
    log.log(Level.WARNING, stackTrace.getMessage(), stackTrace);
  else {
    // start saving the allocation stack trace.
    userPoolItem.setSaveAllocationStackTrace(true);
  }
     
  if (exn == null)
    exn = new IllegalStateException(L.l("Connection {0} was not closed. Connections must have a close() in a finally block.",
                userPoolItem.getUserConnection()));

  userPoolItem.abortConnection();
      } catch (Throwable e) {
  log.log(Level.WARNING, e.toString(), e);
      }
    }

    _poolItems.clear();

    try {
      _transactionManager.setTransactionTimeout(0);
    } catch (Throwable e) {
      log.log(Level.WARNING, e.toString(), e);
    }

    if (exn != null)
      throw exn;
  }
}
TOP

Related Classes of com.caucho.jca.pool.UserTransactionImpl

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.