| /* |
| * Copyright (c) 2021 Contributors to the Eclipse Foundation |
| * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0, which is available at |
| * http://www.eclipse.org/legal/epl-2.0. |
| * |
| * This Source Code may also be made available under the following Secondary |
| * Licenses when the conditions for such availability set forth in the |
| * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, |
| * version 2 with the GNU Classpath Exception, which is available at |
| * https://www.gnu.org/software/classpath/license.html. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 |
| */ |
| |
| package com.sun.enterprise.transaction; |
| |
| import java.rmi.RemoteException; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Hashtable; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Timer; |
| import java.util.TimerTask; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| |
| import javax.transaction.xa.XAException; |
| import javax.transaction.xa.XAResource; |
| import javax.transaction.xa.Xid; |
| |
| import org.glassfish.api.admin.ServerEnvironment; |
| import org.glassfish.api.invocation.ComponentInvocation; |
| import org.glassfish.api.invocation.InvocationException; |
| import org.glassfish.api.invocation.InvocationManager; |
| import org.glassfish.api.invocation.ResourceHandler; |
| import org.glassfish.common.util.Constants; |
| import org.glassfish.external.probe.provider.PluginPoint; |
| import org.glassfish.external.probe.provider.StatsProviderManager; |
| import org.glassfish.hk2.api.PostConstruct; |
| import org.glassfish.hk2.api.Rank; |
| import org.glassfish.hk2.api.ServiceLocator; |
| import org.jvnet.hk2.annotations.ContractsProvided; |
| import org.jvnet.hk2.annotations.Service; |
| |
| import com.sun.appserv.util.cache.BaseCache; |
| import com.sun.appserv.util.cache.Cache; |
| import com.sun.enterprise.config.serverbeans.ModuleMonitoringLevels; |
| import com.sun.enterprise.transaction.api.JavaEETransaction; |
| import com.sun.enterprise.transaction.api.JavaEETransactionManager; |
| import com.sun.enterprise.transaction.api.TransactionAdminBean; |
| import com.sun.enterprise.transaction.api.XAResourceWrapper; |
| import com.sun.enterprise.transaction.config.TransactionService; |
| import com.sun.enterprise.transaction.monitoring.TransactionServiceProbeProvider; |
| import com.sun.enterprise.transaction.monitoring.TransactionServiceStatsProvider; |
| import com.sun.enterprise.transaction.spi.JavaEETransactionManagerDelegate; |
| import com.sun.enterprise.transaction.spi.TransactionInternal; |
| import com.sun.enterprise.transaction.spi.TransactionalResource; |
| import com.sun.enterprise.util.i18n.StringManager; |
| import com.sun.logging.LogDomains; |
| |
| import jakarta.inject.Inject; |
| import jakarta.resource.spi.XATerminator; |
| import jakarta.resource.spi.work.WorkException; |
| import jakarta.transaction.HeuristicMixedException; |
| import jakarta.transaction.HeuristicRollbackException; |
| import jakarta.transaction.InvalidTransactionException; |
| import jakarta.transaction.NotSupportedException; |
| import jakarta.transaction.RollbackException; |
| import jakarta.transaction.Status; |
| import jakarta.transaction.Synchronization; |
| import jakarta.transaction.SystemException; |
| import jakarta.transaction.Transaction; |
| import jakarta.transaction.TransactionManager; |
| |
| /** |
| * Implementation of jakarta.transaction.TransactionManager interface. This class provides non-XA local transaction |
| * support and delegates to implementation of the JavaEETransactionManagerDelegate for XA or LAO optimization, and |
| * complete JTS implementation. |
| * |
| * @author Tony Ng |
| * @author Marina Vatkina |
| */ |
| @Service |
| @ContractsProvided({ TransactionManager.class, JavaEETransactionManager.class }) |
| @Rank(Constants.DEFAULT_IMPLEMENTATION_RANK) // This should be the default impl if it is available |
| public class JavaEETransactionManagerSimplified implements JavaEETransactionManager, PostConstruct { |
| |
| protected Logger _logger = LogDomains.getLogger(JavaEETransactionManagerSimplified.class, LogDomains.JTA_LOGGER); |
| |
| @Inject |
| private ServiceLocator habitat; |
| |
| @Inject |
| protected InvocationManager invMgr; |
| |
| private JavaEETransactionManagerDelegate delegate; |
| |
| // Sting Manager for Localization |
| private static StringManager sm = StringManager.getManager(JavaEETransactionManagerSimplified.class); |
| |
| // Note: this is not inheritable because we dont want transactions |
| // to be inherited by child threads. |
| private ThreadLocal<JavaEETransaction> transactions; |
| |
| private ThreadLocal localCallCounter; |
| private ThreadLocal<JavaEETransactionManagerDelegate> delegates; |
| |
| // If multipleEnlistDelists is set to true, with in the transaction, for the same |
| // - connection multiple enlistments and delistments might happen |
| // - By setting the System property ALLOW_MULTIPLE_ENLISTS_DELISTS to true |
| // - multipleEnlistDelists can be enabled |
| private boolean multipleEnlistDelists = false; |
| |
| private int transactionTimeout; |
| private ThreadLocal<Integer> txnTmout = new ThreadLocal(); |
| |
| private int purgeCancelledTtransactions = 0; |
| |
| // admin and monitoring related parameters |
| private static final Hashtable statusMap = new Hashtable(); |
| private List activeTransactions = Collections.synchronizedList(new ArrayList()); |
| private boolean monitoringEnabled = false; |
| |
| private TransactionServiceProbeProvider monitor; |
| private Hashtable txnTable = null; |
| |
| private Cache resourceTable; |
| |
| private Timer _timer = new Timer("transaction-manager", true); |
| |
| static { |
| statusMap.put(Status.STATUS_ACTIVE, "Active"); |
| statusMap.put(Status.STATUS_MARKED_ROLLBACK, "MarkedRollback"); |
| statusMap.put(Status.STATUS_PREPARED, "Prepared"); |
| statusMap.put(Status.STATUS_COMMITTED, "Committed"); |
| statusMap.put(Status.STATUS_ROLLEDBACK, "RolledBack"); |
| statusMap.put(Status.STATUS_UNKNOWN, "UnKnown"); |
| statusMap.put(Status.STATUS_NO_TRANSACTION, "NoTransaction"); |
| statusMap.put(Status.STATUS_PREPARING, "Preparing"); |
| statusMap.put(Status.STATUS_COMMITTING, "Committing"); |
| statusMap.put(Status.STATUS_ROLLING_BACK, "RollingBack"); |
| |
| } |
| |
| public JavaEETransactionManagerSimplified() { |
| transactions = new ThreadLocal<JavaEETransaction>(); |
| localCallCounter = new ThreadLocal(); |
| delegates = new ThreadLocal<JavaEETransactionManagerDelegate>(); |
| } |
| |
| public void postConstruct() { |
| initDelegates(); |
| initProperties(); |
| } |
| |
| private void initProperties() { |
| int maxEntries = 8192; // FIXME: this maxEntry should be a config |
| float loadFactor = 0.75f; // FIXME: this loadFactor should be a config |
| // for now, let's get it from system prop |
| try { |
| String mEnlistDelists = System.getProperty("ALLOW_MULTIPLE_ENLISTS_DELISTS"); |
| if ("true".equals(mEnlistDelists)) { |
| multipleEnlistDelists = true; |
| if (_logger.isLoggable(Level.FINE)) |
| _logger.log(Level.FINE, "TM: multiple enlists, delists are enabled"); |
| } |
| String maxEntriesValue = System.getProperty("JTA_RESOURCE_TABLE_MAX_ENTRIES"); |
| if (maxEntriesValue != null) { |
| int temp = Integer.parseInt(maxEntriesValue); |
| if (temp > 0) { |
| maxEntries = temp; |
| } |
| } |
| String loadFactorValue = System.getProperty("JTA_RESOURCE_TABLE_DEFAULT_LOAD_FACTOR"); |
| if (loadFactorValue != null) { |
| float f = Float.parseFloat(loadFactorValue); |
| if (f > 0) { |
| loadFactor = f; |
| } |
| } |
| } catch (Exception ex) { |
| // ignore |
| } |
| |
| resourceTable = new BaseCache(); |
| ((BaseCache) resourceTable).init(maxEntries, loadFactor, null); |
| // END IASRI 4705808 TTT001 |
| |
| if (habitat != null) { |
| TransactionService txnService = habitat.getService(TransactionService.class, ServerEnvironment.DEFAULT_INSTANCE_NAME); |
| // running on the server side ? |
| if (txnService != null) { |
| transactionTimeout = Integer.parseInt(txnService.getTimeoutInSeconds()); |
| // the delegates will do the rest if they support it |
| |
| String v = txnService.getPropertyValue("purge-cancelled-transactions-after"); |
| if (v != null && v.length() > 0) { |
| purgeCancelledTtransactions = Integer.parseInt(v); |
| } |
| |
| TransactionServiceConfigListener listener = habitat.getService(TransactionServiceConfigListener.class); |
| listener.setTM(this); |
| } |
| ModuleMonitoringLevels levels = habitat.getService(ModuleMonitoringLevels.class); |
| // running on the server side ? |
| if (levels != null) { |
| String level = levels.getTransactionService(); |
| if (!("OFF".equals(level))) { |
| monitoringEnabled = true; |
| } |
| } |
| } |
| |
| // ENF OF BUG 4665539 |
| if (_logger.isLoggable(Level.FINE)) |
| _logger.log(Level.FINE, "TM: Tx Timeout = " + transactionTimeout); |
| |
| // START IASRI 4705808 TTT004 -- monitor resource table stats |
| try { |
| // XXX TODO: |
| if (Boolean.getBoolean("MONITOR_JTA_RESOURCE_TABLE_STATISTICS")) { |
| registerStatisticMonitorTask(); |
| } |
| |
| StatsProviderManager.register("transaction-service", // element in domain.xml <monitoring-service>/<monitoring-level> |
| PluginPoint.SERVER, "transaction-service", // server.transaction-service node in asadmin get |
| new TransactionServiceStatsProvider(this, _logger)); |
| } catch (Exception ex) { |
| // ignore |
| } |
| |
| monitor = new TransactionServiceProbeProvider(); |
| } |
| |
| /** |
| * Clears the transaction associated with the caller thread |
| */ |
| public void clearThreadTx() { |
| setCurrentTransaction(null); |
| delegates.set(null); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public String getTxLogLocation() { |
| return getDelegate().getTxLogLocation(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public void registerRecoveryResourceHandler(XAResource xaResource) { |
| getDelegate().registerRecoveryResourceHandler(xaResource); |
| } |
| |
| /****************************************************************************/ |
| /** Implementations of JavaEETransactionManager APIs **************************/ |
| /****************************************************************************/ |
| |
| /** |
| * Return true if a "null transaction context" was received from the client. See EJB2.0 spec section 19.6.2.1. A null tx |
| * context has no Coordinator objref. It indicates that the client had an active tx but the client container did not |
| * support tx interop. |
| */ |
| public boolean isNullTransaction() { |
| return getDelegate().isNullTransaction(); |
| } |
| |
| public void shutdown() { |
| _timer.cancel(); |
| } |
| |
| public void initRecovery(boolean force) { |
| getDelegate().initRecovery(force); |
| } |
| |
| public void recover(XAResource[] resourceList) { |
| getDelegate().recover(resourceList); |
| } |
| |
| public boolean enlistResource(Transaction tran, TransactionalResource h) |
| throws RollbackException, IllegalStateException, SystemException { |
| if (_logger.isLoggable(Level.FINE)) { |
| _logger.log(Level.FINE, "\n\nIn JavaEETransactionManagerSimplified.enlistResource, h=" + h + " h.xares=" + h.getXAResource() |
| /** +" h.alloc=" +h.getResourceAllocator() **/ |
| + " tran=" + tran); |
| } |
| |
| if (!h.isTransactional()) |
| return true; |
| |
| // If LazyEnlistment is suspended, do not enlist resource. |
| if (h.isEnlistmentSuspended()) { |
| return false; |
| } |
| |
| if (monitoringEnabled) { |
| JavaEETransaction tx = getDelegate().getJavaEETransaction(tran); |
| if (tx != null) { |
| ((JavaEETransactionImpl) tx).addResourceName(h.getName()); |
| } |
| } |
| |
| if (!(tran instanceof JavaEETransaction)) { |
| return enlistXAResource(tran, h); |
| } |
| |
| JavaEETransactionImpl tx = (JavaEETransactionImpl) tran; |
| |
| JavaEETransactionManagerDelegate d = setDelegate(); |
| boolean useLAO = d.useLAO(); |
| |
| if ((tx.getNonXAResource() != null) && (!useLAO || !h.supportsXA())) { |
| boolean isSameRM = false; |
| try { |
| isSameRM = h.getXAResource().isSameRM(tx.getNonXAResource().getXAResource()); |
| if (_logger.isLoggable(Level.FINE)) { |
| _logger.log(Level.FINE, "\n\nIn JavaEETransactionManagerSimplified.enlistResource, isSameRM? " + isSameRM); |
| } |
| } catch (XAException xex) { |
| throw new SystemException(sm.getString("enterprise_distributedtx.samerm_excep", xex)); |
| } catch (Exception ex) { |
| throw new SystemException(sm.getString("enterprise_distributedtx.samerm_excep", ex)); |
| } |
| if (!isSameRM) { |
| throw new IllegalStateException(sm.getString("enterprise_distributedtx.already_has_nonxa")); |
| } |
| } |
| |
| if (h.supportsXA()) { |
| if (!d.supportsXAResource()) { |
| throw new IllegalStateException(sm.getString("enterprise_distributedtx.xaresource_not_supported")); |
| } |
| |
| if (tx.isLocalTx()) { |
| d.enlistLAOResource(tx, tx.getNonXAResource()); |
| |
| /** |
| * XXX TO BE MOVED TO XA DELEGATE XXX ** startJTSTx(tx); |
| * |
| * //If transaction conatains a NonXA and no LAO, convert the existing //Non XA to LAO if(useLAO) { |
| * if(tx.getNonXAResource()!=null && (tx.getLAOResource()==null) ) { tx.setLAOResource(tx.getNonXAResource()); // XXX |
| * super.enlistLAOResource(tx, tx.getNonXAResource()); } } XXX TO BE MOVED TO XA DELEGATE XXX |
| **/ |
| } |
| return enlistXAResource(tx, h); |
| } else { // non-XA resource |
| if (tx.isImportedTransaction()) |
| throw new IllegalStateException(sm.getString("enterprise_distributedtx.nonxa_usein_jts")); |
| if (tx.getNonXAResource() == null) { |
| tx.setNonXAResource(h); |
| } |
| if (tx.isLocalTx()) { |
| // notify resource that it is being used for tx, |
| // e.g. this allows the correct physical connection to be |
| // swapped in for the logical connection. |
| // The flags parameter can be 0 because the flags are not |
| // used by the XAResource implementation for non-XA resources. |
| try { |
| h.getXAResource().start(tx.getLocalXid(), 0); |
| } catch (XAException ex) { |
| throw new RuntimeException(sm.getString("enterprise_distributedtx.xaresource_start_excep"), ex); |
| } |
| |
| h.enlistedInTransaction(tx); |
| return true; |
| } else { |
| return d.enlistDistributedNonXAResource(tx, h); |
| /** |
| * XXX TO BE MOVED TO XA DELEGATE? XXX if(useLAO) { return super.enlistResource(tx, h); } else { throw new |
| * IllegalStateException( sm.getString("enterprise_distributedtx.nonxa_usein_jts")); } XXX TO BE MOVED TO XA DELEGATE? |
| * XXX |
| **/ |
| } |
| } |
| } |
| |
| public void unregisterComponentResource(TransactionalResource h) { |
| if (_logger.isLoggable(Level.FINE)) { |
| _logger.log(Level.FINE, |
| "\n\nIn JavaEETransactionManagerSimplified.unregisterComponentResource, h=" + h + " h.xares=" + h.getXAResource()); |
| } |
| |
| Object instance = h.getComponentInstance(); |
| if (instance == null) |
| return; |
| h.setComponentInstance(null); |
| ComponentInvocation inv = invMgr.getCurrentInvocation(); |
| List l = getExistingResourceList(instance, inv); |
| |
| if (l != null) { |
| l.remove(h); |
| } |
| } |
| |
| public void startJTSTx(JavaEETransaction t) throws RollbackException, IllegalStateException, SystemException { |
| |
| JavaEETransactionImpl tx = (JavaEETransactionImpl) t; |
| TransactionInternal jtsTx = getDelegate().startJTSTx(tx, tx.isAssociatedTimeout()); |
| |
| // The local Transaction was promoted to global Transaction |
| if (monitoringEnabled) { |
| if (activeTransactions.remove(tx)) { |
| monitor.transactionDeactivatedEvent(); |
| } |
| } |
| |
| tx.setJTSTx(jtsTx); |
| jtsTx.registerSynchronization(new JTSSynchronization(jtsTx, this)); |
| } |
| |
| /** |
| * get the resources being used in the calling component's invocation context |
| * |
| * @param instance Calling component instance |
| * @param inv Calling component's invocation information |
| * @return List of resources |
| */ |
| public List getResourceList(Object instance, ComponentInvocation inv) { |
| if (inv == null) |
| return new ArrayList(0); |
| List l = null; |
| |
| /** |
| * XXX EJB CONTAINER ONLY XXX -- NEED TO CHECK THE NEW CODE BELOW ** if (inv.getInvocationType() == |
| * ComponentInvocation.ComponentInvocationType.EJB_INVOCATION) { ComponentContext ctx = inv.context; if (ctx != null) l |
| * = ctx.getResourceList(); else { l = new ArrayList(0); } } XXX EJB CONTAINER ONLY XXX |
| **/ |
| |
| ResourceHandler rh = inv.getResourceHandler(); |
| if (_logger.isLoggable(Level.FINE)) { |
| _logger.log(Level.FINE, "\n\nIn JavaEETransactionManagerSimplified.getResourceList, " |
| + ((rh == null) ? "" : (" ResourceHandler type: " + rh.getClass().getName())) + " ResourceHandler: " + rh); |
| } |
| |
| if (rh != null) { |
| l = rh.getResourceList(); |
| if (l == null) { |
| l = new ArrayList(0); |
| } |
| } else { |
| Object key = getResourceTableKey(instance, inv); |
| if (key == null) |
| return new ArrayList(0); |
| l = (List) resourceTable.get(key); |
| if (l == null) { |
| l = new ArrayList(); // FIXME: use an optimum size? |
| resourceTable.put(key, l); |
| } |
| } |
| return l; |
| } |
| |
| public void enlistComponentResources() throws RemoteException { |
| if (_logger.isLoggable(Level.FINE)) |
| _logger.log(Level.FINE, "TM: enlistComponentResources"); |
| |
| ComponentInvocation inv = invMgr.getCurrentInvocation(); |
| if (inv == null) |
| return; |
| try { |
| Transaction tran = getTransaction(); |
| inv.setTransaction((JavaEETransaction) tran); |
| enlistComponentResources(inv); |
| } catch (InvocationException ex) { |
| _logger.log(Level.SEVERE, "enterprise_distributedtx.excep_in_enlist", ex); |
| throw new RemoteException(ex.getMessage(), ex.getNestedException()); |
| } catch (Exception ex) { |
| _logger.log(Level.SEVERE, "enterprise_distributedtx.excep_in_enlist", ex); |
| throw new RemoteException(ex.getMessage(), ex); |
| } |
| } |
| |
| public boolean delistResource(Transaction tran, TransactionalResource h, int flag) throws IllegalStateException, SystemException { |
| if (_logger.isLoggable(Level.FINE)) { |
| _logger.log(Level.FINE, |
| "\n\nIn JavaEETransactionManagerSimplified.delistResource, h=" + h + " h.xares=" + h.getXAResource() + " tran=" + tran); |
| } |
| |
| if (!h.isTransactional()) |
| return true; |
| |
| if (!(tran instanceof JavaEETransaction)) |
| return delistJTSResource(tran, h, flag); |
| |
| JavaEETransactionImpl tx = (JavaEETransactionImpl) tran; |
| if (tx.isLocalTx()) { |
| // dissociate resource from tx |
| try { |
| h.getXAResource().end(tx.getLocalXid(), flag); |
| } catch (XAException ex) { |
| throw new RuntimeException(sm.getString("enterprise_distributedtx.xaresource_end_excep", ex)); |
| } |
| return true; |
| } else |
| return delistJTSResource(tran, h, flag); |
| } |
| |
| public void delistComponentResources(boolean suspend) throws RemoteException { |
| if (_logger.isLoggable(Level.FINE)) |
| _logger.log(Level.FINE, "TM: delistComponentResources"); |
| ComponentInvocation inv = invMgr.getCurrentInvocation(); |
| // BEGIN IASRI# 4646060 |
| if (inv == null) { |
| return; |
| } |
| // END IASRI# 4646060 |
| try { |
| delistComponentResources(inv, suspend); |
| } catch (InvocationException ex) { |
| _logger.log(Level.SEVERE, "enterprise_distributedtx.excep_in_delist", ex); |
| throw new RemoteException("", ex.getNestedException()); |
| } catch (Exception ex) { |
| _logger.log(Level.SEVERE, "enterprise_distributedtx.excep_in_delist", ex); |
| throw new RemoteException("", ex); |
| } |
| } |
| |
| public void registerComponentResource(TransactionalResource h) { |
| ComponentInvocation inv = invMgr.getCurrentInvocation(); |
| if (inv != null) { |
| Object instance = inv.getInstance(); |
| if (instance == null) |
| return; |
| h.setComponentInstance(instance); |
| List l = getResourceList(instance, inv); |
| if (_logger.isLoggable(Level.FINE)) { |
| _logger.log(Level.FINE, |
| "\n\nIn JavaEETransactionManagerSimplified.registerComponentResource, h=" + h + " h.xares=" + h.getXAResource()); |
| } |
| |
| l.add(h); |
| } |
| } |
| |
| private JavaEETransactionImpl initJavaEETransaction(int timeout) { |
| JavaEETransactionImpl tx = null; |
| // Do not need to use injection. |
| if (timeout > 0) |
| tx = new JavaEETransactionImpl(timeout, this); |
| else |
| tx = new JavaEETransactionImpl(this); |
| |
| setCurrentTransaction(tx); |
| return tx; |
| } |
| |
| public List getExistingResourceList(Object instance, ComponentInvocation inv) { |
| if (inv == null) |
| return null; |
| List l = null; |
| |
| /** |
| * XXX EJB CONTAINER ONLY XXX -- NEED TO CHECK THE NEW CODE BELOW ** if (inv.getInvocationType() == |
| * ComponentInvocation.ComponentInvocationType.EJB_INVOCATION) { ComponentContext ctx = inv.context; if (ctx != null) l |
| * = ctx.getResourceList(); return l; } XXX EJB CONTAINER ONLY XXX |
| **/ |
| |
| ResourceHandler rh = inv.getResourceHandler(); |
| if (_logger.isLoggable(Level.FINE)) { |
| _logger.log(Level.FINE, "\n\nIn JavaEETransactionManagerSimplified.getExistingResourceList, " |
| + ((rh == null) ? "" : (" ResourceHandler type: " + rh.getClass().getName())) + " ResourceHandler: " + rh); |
| } |
| |
| if (rh != null) { |
| l = rh.getResourceList(); |
| } else { |
| Object key = getResourceTableKey(instance, inv); |
| if (key != null) |
| l = (List) resourceTable.get(key); |
| } |
| return l; |
| } |
| |
| public void preInvoke(ComponentInvocation prev) throws InvocationException { |
| if (prev != null && prev.getTransaction() != null && prev.isTransactionCompleting() == false) { |
| // do not worry about delisting previous invocation resources |
| // if transaction is being completed |
| delistComponentResources(prev, true); // delist with TMSUSPEND |
| } |
| |
| } |
| |
| public void postInvoke(ComponentInvocation curr, ComponentInvocation prev) throws InvocationException { |
| |
| if (curr != null && curr.getTransaction() != null) |
| delistComponentResources(curr, false); // delist with TMSUCCESS |
| if (prev != null && prev.getTransaction() != null && prev.isTransactionCompleting() == false) { |
| // do not worry about re-enlisting previous invocation resources |
| // if transaction is being completed |
| enlistComponentResources(prev); |
| } |
| |
| } |
| |
| public void componentDestroyed(Object instance) { |
| componentDestroyed(instance, null); |
| } |
| |
| public void componentDestroyed(Object instance, ComponentInvocation inv) { |
| if (_logger.isLoggable(Level.FINE)) { |
| _logger.log(Level.FINE, "TM: componentDestroyed" + instance); |
| _logger.log(Level.FINE, "TM: resourceTable before: " + resourceTable.getEntryCount()); |
| } |
| |
| // Access resourceTable directly to avoid adding an empty list then removing it |
| List l = (List) resourceTable.remove(getResourceTableKey(instance, inv)); |
| processResourceList(l); |
| |
| if (_logger.isLoggable(Level.FINE)) |
| _logger.log(Level.FINE, "TM: resourceTable after: " + resourceTable.getEntryCount()); |
| } |
| |
| public void componentDestroyed(ResourceHandler rh) { |
| if (_logger.isLoggable(Level.FINE)) |
| _logger.log(Level.FINE, " componentDestroyed: " + rh); |
| |
| if (rh != null) { |
| processResourceList(rh.getResourceList()); |
| } |
| } |
| |
| public boolean isTimedOut() { |
| JavaEETransaction tx = transactions.get(); |
| if (tx != null) |
| return tx.isTimedOut(); |
| else |
| return false; |
| } |
| |
| /** |
| * Called from the CORBA Interceptors on the server-side when the server is replying to the client (local + remote |
| * client). Check if there is an active transaction and remove it from TLS. |
| */ |
| public void checkTransactionImport() { |
| // First check if this is a local call |
| int[] count = (int[]) localCallCounter.get(); |
| if (count != null && count[0] > 0) { |
| count[0]--; |
| return; |
| } else { |
| // A remote call, clear TLS so that if this thread is reused |
| // later, the current tx doesnt hang around. |
| clearThreadTx(); |
| } |
| } |
| |
| /** |
| * Called from the CORBA Interceptors on the client-side when a client makes a call to a remote object (not in the same |
| * JVM). Check if there is an active, exportable transaction. |
| * |
| * @exception RuntimeException if the transaction is not exportable |
| */ |
| public void checkTransactionExport(boolean isLocal) { |
| |
| if (isLocal) { |
| // Put a counter in TLS indicating this is a local call. |
| // Use int[1] as a mutable java.lang.Integer! |
| int[] count = (int[]) localCallCounter.get(); |
| if (count == null) { |
| count = new int[1]; |
| localCallCounter.set(count); |
| } |
| count[0]++; |
| return; |
| } |
| |
| JavaEETransaction tx = transactions.get(); |
| if (tx == null) |
| return; |
| |
| if (!tx.isLocalTx()) // a JTS tx, can be exported |
| return; |
| |
| // Check if a local tx with non-XA resource is being exported. |
| // XXX what if this is a call on a non-transactional remote object ? |
| if (tx.getNonXAResource() != null) |
| throw new RuntimeException(sm.getString("enterprise_distributedtx.cannot_export_transaction_having_nonxa")); |
| |
| // If we came here, it means we have a local tx with no registered |
| // resources, so start a JTS tx which can be exported. |
| try { |
| startJTSTx(tx); |
| } catch (RollbackException rlex) { |
| throw new RuntimeException(sm.getString("enterprise_distributedtx.unable_tostart_JTSTransaction"), rlex); |
| } catch (IllegalStateException isex) { |
| throw new RuntimeException(sm.getString("enterprise_distributedtx.unable_tostart_JTSTransaction"), isex); |
| } catch (SystemException ex) { |
| throw new RuntimeException(sm.getString("enterprise_distributedtx.unable_tostart_JTSTransaction"), ex); |
| } catch (Exception excep) { |
| throw new RuntimeException(sm.getString("enterprise_distributedtx.unable_tostart_JTSTransaction"), excep); |
| } |
| } |
| |
| /** |
| * This is used by importing transactions via the Connector contract. Should not be called |
| * |
| * @return a <code>XATerminator</code> instance. |
| * @throws UnsupportedOperationException |
| */ |
| public XATerminator getXATerminator() { |
| return getDelegate().getXATerminator(); |
| } |
| |
| /** |
| * Release a transaction. This call causes the calling thread to be dissociated from the specified transaction. |
| * <p> |
| * This is used by importing transactions via the Connector contract. |
| * |
| * @param xid the Xid object representing a transaction. |
| */ |
| public void release(Xid xid) throws WorkException { |
| getDelegate().release(xid); |
| } |
| |
| /** |
| * Recreate a transaction based on the Xid. This call causes the calling thread to be associated with the specified |
| * transaction. |
| * <p> |
| * This is used by importing transactions via the Connector contract. |
| * |
| * @param xid the Xid object representing a transaction. |
| */ |
| public void recreate(Xid xid, long timeout) throws WorkException { |
| getDelegate().recreate(xid, timeout); |
| } |
| |
| /****************************************************************************/ |
| /** Implementations of JTA TransactionManager APIs **************************/ |
| /****************************************************************************/ |
| |
| public void registerSynchronization(Synchronization sync) throws IllegalStateException, SystemException { |
| if (_logger.isLoggable(Level.FINE)) |
| _logger.log(Level.FINE, "TM: registerSynchronization"); |
| |
| try { |
| Transaction tran = getTransaction(); |
| if (tran != null) { |
| tran.registerSynchronization(sync); |
| } |
| } catch (RollbackException ex) { |
| _logger.log(Level.SEVERE, "enterprise_distributedtx.rollbackexcep_in_regsynch", ex); |
| throw new IllegalStateException(); |
| } |
| } |
| |
| // Implementation of begin() is moved to begin(int timeout) |
| public void begin() throws NotSupportedException, SystemException { |
| begin(getEffectiveTimeout()); |
| } |
| |
| /** |
| * This method is introduced as part of implementing the local transaction timeout capability. Implementation of begin() |
| * moved here. Previpusly there is no timeout infrastructure for local txns, so when ever a timeout required for local |
| * txn, it uses the globaltxn timeout infrastructure by doing an XA simulation. |
| **/ |
| public void begin(int timeout) throws NotSupportedException, SystemException { |
| // Check if tx already exists |
| if (transactions.get() != null) |
| throw new NotSupportedException(sm.getString("enterprise_distributedtx.notsupported_nested_transaction")); |
| |
| setDelegate(); |
| |
| // Check if JTS tx exists, without starting JTS tx. |
| // This is needed in case the JTS tx was imported from a client. |
| if (getStatus() != Status.STATUS_NO_TRANSACTION) |
| throw new NotSupportedException(sm.getString("enterprise_distributedtx.notsupported_nested_transaction")); |
| |
| // START IASRI 4662745 |
| if (monitoringEnabled) { |
| getDelegate().getReadLock().lock(); // XXX acquireReadLock(); |
| try { |
| JavaEETransactionImpl tx = initJavaEETransaction(timeout); |
| activeTransactions.add(tx); |
| monitor.transactionActivatedEvent(); |
| ComponentInvocation inv = invMgr.getCurrentInvocation(); |
| if (inv != null && inv.getInstance() != null) { |
| tx.setComponentName(inv.getInstance().getClass().getName()); |
| } |
| } finally { |
| getDelegate().getReadLock().unlock(); // XXX releaseReadLock(); |
| } |
| } else { |
| initJavaEETransaction(timeout); |
| } |
| // START IASRI 4662745 |
| } |
| |
| public void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, |
| IllegalStateException, SystemException { |
| |
| boolean acquiredlock = false; |
| try { |
| JavaEETransaction tx = transactions.get(); |
| if (tx != null && tx.isLocalTx()) { |
| if (monitoringEnabled) { |
| getDelegate().getReadLock().lock(); // XXX acquireReadLock(); |
| acquiredlock = true; |
| } |
| tx.commit(); // commit local tx |
| } else { |
| try { |
| // an XA transaction |
| getDelegate().commitDistributedTransaction(); |
| } finally { |
| if (tx != null) { |
| ((JavaEETransactionImpl) tx).onTxCompletion(true); |
| } |
| } |
| } |
| |
| } finally { |
| setCurrentTransaction(null); // clear current thread's tx |
| delegates.set(null); |
| if (acquiredlock) { |
| getDelegate().getReadLock().unlock(); // XXX releaseReadLock(); |
| } |
| } |
| // END IASRI 4662745 |
| } |
| |
| public void rollback() throws IllegalStateException, SecurityException, SystemException { |
| boolean acquiredlock = false; |
| try { |
| JavaEETransaction tx = transactions.get(); |
| if (tx != null && tx.isLocalTx()) { |
| if (monitoringEnabled) { |
| getDelegate().getReadLock().lock(); // XXX acquireReadLock(); |
| acquiredlock = true; |
| } |
| tx.rollback(); // rollback local tx |
| } else { |
| try { |
| // an XA transaction |
| getDelegate().rollbackDistributedTransaction(); |
| } finally { |
| if (tx != null) { |
| ((JavaEETransactionImpl) tx).onTxCompletion(false); |
| } |
| } |
| } |
| |
| } finally { |
| setCurrentTransaction(null); // clear current thread's tx |
| delegates.set(null); |
| if (acquiredlock) { |
| getDelegate().getReadLock().unlock(); // XXX releaseReadLock(); |
| } |
| } |
| } |
| |
| public int getStatus() throws SystemException { |
| return getDelegate().getStatus(); |
| } |
| |
| public Transaction getTransaction() throws SystemException { |
| return getDelegate().getTransaction(); |
| |
| /** |
| * XXX CHECK WHAT'S NEEDED FOR XA DELEGATE XXX ** TransactionInternal jtsTx = super.getTransaction(); if ( jtsTx == null |
| * ) return null; else { // check if this JTS Transaction was previously active // in this JVM (possible for distributed |
| * loopbacks). tx = (JavaEETransaction)globalTransactions.get(jtsTx); if ( tx == null ) { tx = new |
| * JavaEETransaction(jtsTx, this); try { jtsTx.registerSynchronization( new JTSSynchronization(jtsTx, this)); } catch ( |
| * RollbackException rlex ) { throw new SystemException(rlex.toString()); } catch ( IllegalStateException isex ) { throw |
| * new SystemException(isex.toString()); } catch ( Exception ex ) { throw new SystemException(ex.toString()); } |
| * |
| * globalTransactions.put(jtsTx, tx); } setCurrentTransaction(tx); // associate tx with thread return tx; } XXX CHECK |
| * WHAT'S NEEDED FOR XA DELEGATE XXX |
| **/ |
| } |
| |
| public void setRollbackOnly() throws IllegalStateException, SystemException { |
| |
| JavaEETransaction tx = transactions.get(); |
| // START IASRI 4662745 |
| if (tx != null && tx.isLocalTx()) { |
| if (monitoringEnabled) { |
| getDelegate().getReadLock().lock(); // XXX acquireReadLock(); |
| try { |
| tx.setRollbackOnly(); |
| } finally { |
| getDelegate().getReadLock().unlock(); // XXX releaseReadLock(); |
| } |
| } else { |
| tx.setRollbackOnly(); |
| } |
| } else |
| getDelegate().setRollbackOnlyDistributedTransaction(); // probably a JTS imported tx |
| } |
| |
| public Transaction suspend() throws SystemException { |
| return getDelegate().suspend(transactions.get()); |
| |
| /** |
| * XXX TO BE MOVED TO DELEGATES XXX ** if ( tx != null ) { if ( !tx.isLocalTx() ) super.suspend(); |
| * |
| * setCurrentTransaction(null); return tx; } else { return super.suspend(); // probably a JTS imported tx } XXX TO BE |
| * MOVED TO DELEGATES XXX |
| **/ |
| } |
| |
| public void resume(Transaction tobj) throws InvalidTransactionException, IllegalStateException, SystemException { |
| |
| JavaEETransaction tx = transactions.get(); |
| if (tx != null) |
| throw new IllegalStateException(sm.getString("enterprise_distributedtx.transaction_exist_on_currentThread")); |
| |
| if (tobj != null) { |
| int status = tobj.getStatus(); |
| if (status == Status.STATUS_ROLLEDBACK || status == Status.STATUS_COMMITTED || status == Status.STATUS_NO_TRANSACTION |
| || status == Status.STATUS_UNKNOWN) { |
| throw new InvalidTransactionException(sm.getString("enterprise_distributedtx.resume_invalid_transaction", tobj)); |
| } |
| } else { |
| throw new InvalidTransactionException(sm.getString("enterprise_distributedtx.resume_invalid_transaction", "null")); |
| } |
| |
| if (tobj instanceof JavaEETransactionImpl) { |
| JavaEETransactionImpl javaEETx = (JavaEETransactionImpl) tobj; |
| if (!javaEETx.isLocalTx()) |
| getDelegate().resume(javaEETx.getJTSTx()); |
| |
| setCurrentTransaction(javaEETx); |
| } else { |
| getDelegate().resume(tobj); // probably a JTS imported tx |
| } |
| } |
| |
| /** |
| * Modify the value of the timeout value that is associated with the transactions started by the current thread with the |
| * begin method. |
| * |
| * <p> |
| * If an application has not called this method, the transaction service uses some default value for the transaction |
| * timeout. |
| * |
| * @exception SystemException Thrown if the transaction manager encounters an unexpected error condition |
| * |
| */ |
| public void setTransactionTimeout(int seconds) throws SystemException { |
| if (seconds < 0) { |
| throw new SystemException(sm.getString("enterprise_distributedtx.invalid_timeout")); |
| } |
| |
| txnTmout.set(seconds); |
| // transactionTimeout = seconds; |
| } |
| |
| /** |
| * Modify the value to be used to purge transaction tasks after the specified number of cancelled tasks. |
| */ |
| public void setPurgeCancelledTtransactionsAfter(int num) { |
| purgeCancelledTtransactions = num; |
| } |
| |
| /** |
| * Returns the value to be used to purge transaction tasks after the specified number of cancelled tasks. |
| */ |
| public int getPurgeCancelledTtransactionsAfter() { |
| return purgeCancelledTtransactions; |
| } |
| |
| public JavaEETransaction getCurrentTransaction() { |
| return transactions.get(); |
| } |
| |
| public void setCurrentTransaction(JavaEETransaction t) { |
| transactions.set(t); |
| } |
| |
| public XAResourceWrapper getXAResourceWrapper(String clName) { |
| return getDelegate().getXAResourceWrapper(clName); |
| } |
| |
| public void handlePropertyUpdate(String name, Object value) { |
| delegate.handlePropertyUpdate(name, value); |
| // XXX Check if the current delegate needs to be called as well. |
| } |
| |
| public boolean recoverIncompleteTx(boolean delegated, String logPath, XAResource[] xaresArray) throws Exception { |
| return delegate.recoverIncompleteTx(delegated, logPath, xaresArray); |
| } |
| |
| /****************************************************************************/ |
| /*********************** Called by Admin Framework **************************/ |
| /****************************************************************************/ |
| /* |
| * Called by Admin Framework to freeze the transactions. |
| */ |
| public synchronized void freeze() { |
| getDelegate().acquireWriteLock(); |
| monitor.freezeEvent(true); |
| } |
| |
| /* |
| * Called by Admin Framework to freeze the transactions. These undoes the work done by the freeze. |
| */ |
| public synchronized void unfreeze() { |
| getDelegate().releaseWriteLock(); |
| monitor.freezeEvent(false); |
| } |
| |
| /** |
| * XXX ??? |
| */ |
| public boolean isFrozen() { |
| return getDelegate().isWriteLocked(); |
| } |
| |
| public void cleanTxnTimeout() { |
| txnTmout.set(null); |
| } |
| |
| public int getEffectiveTimeout() { |
| Integer tmout = txnTmout.get(); |
| if (tmout == null) { |
| return transactionTimeout; |
| } else { |
| return tmout; |
| } |
| } |
| |
| public void setDefaultTransactionTimeout(int seconds) { |
| if (seconds < 0) |
| seconds = 0; |
| transactionTimeout = seconds; |
| } |
| |
| /* |
| * This method returns the details of the Currently Active Transactions Called by Admin Framework when transaction |
| * monitoring is enabled |
| * |
| * @return ArrayList of TransactionAdminBean |
| * |
| * @see TransactionAdminBean |
| */ |
| public ArrayList getActiveTransactions() { |
| ArrayList tranBeans = new ArrayList(); |
| txnTable = new Hashtable(); |
| Object[] activeCopy = activeTransactions.toArray(); // get the clone of the active transactions |
| for (int i = 0; i < activeCopy.length; i++) { |
| try { |
| Transaction tran = (Transaction) activeCopy[i]; |
| TransactionAdminBean tBean = getDelegate().getTransactionAdminBean(tran); |
| if (tBean == null) { |
| // Shouldn't happen |
| _logger.warning("enterprise_distributedtx.txbean_null" + tran); |
| } else { |
| if (_logger.isLoggable(Level.FINE)) |
| _logger.log(Level.FINE, "TM: Adding txnId " + tBean.getId() + " to txnTable"); |
| |
| txnTable.put(tBean.getId(), tran); |
| tranBeans.add(tBean); |
| } |
| } catch (Exception ex) { |
| _logger.log(Level.SEVERE, "transaction.monitor.error_while_getting_monitor_attr", ex); |
| } |
| } |
| return tranBeans; |
| } |
| |
| public TransactionAdminBean getTransactionAdminBean(Transaction tran) throws jakarta.transaction.SystemException { |
| |
| TransactionAdminBean tBean = null; |
| if (tran instanceof JavaEETransaction) { |
| JavaEETransactionImpl tran1 = (JavaEETransactionImpl) tran; |
| String id = tran1.getTransactionId(); |
| long startTime = tran1.getStartTime(); |
| String componentName = tran1.getComponentName(); |
| ArrayList<String> resourceNames = tran1.getResourceNames(); |
| long elapsedTime = System.currentTimeMillis() - startTime; |
| String status = getStatusAsString(tran.getStatus()); |
| |
| tBean = new TransactionAdminBean(tran, id, status, elapsedTime, componentName, resourceNames); |
| } |
| |
| return tBean; |
| } |
| |
| /* |
| * Called by Admin Framework when transaction monitoring is enabled |
| */ |
| public void forceRollback(String txnId) throws IllegalStateException, SystemException { |
| // XXX - WORK AROUND MONITORING BUG |
| if (txnTable == null || txnTable.size() == 0) |
| getActiveTransactions(); |
| // XXX - WORK AROUND MONITORING BUG |
| |
| if (txnTable == null || txnTable.get(txnId) == null) { |
| String result = sm.getString("transaction.monitor.rollback_invalid_id"); |
| throw new IllegalStateException(result); |
| } else { |
| if (_logger.isLoggable(Level.FINE)) |
| _logger.log(Level.FINE, "TM: Marking txnId " + txnId + " for rollback"); |
| |
| ((Transaction) txnTable.get(txnId)).setRollbackOnly(); |
| } |
| |
| } |
| |
| public void setMonitoringEnabled(boolean enabled) { |
| monitoringEnabled = enabled; |
| // reset the variables |
| activeTransactions.clear(); |
| } |
| |
| private void _monitorTxCompleted(Object obj, boolean committed) { |
| if (obj != null) { |
| if (obj instanceof JavaEETransactionImpl) { |
| JavaEETransactionImpl t = (JavaEETransactionImpl) obj; |
| if (!t.isLocalTx()) { |
| obj = t.getJTSTx(); |
| } |
| } |
| if (activeTransactions.remove(obj)) { |
| if (committed) { |
| monitor.transactionCommittedEvent(); |
| } else { |
| monitor.transactionRolledbackEvent(); |
| } |
| } else { |
| // WARN ??? |
| } |
| } |
| } |
| |
| // Mods: Adding method for statistic dumps using TimerTask |
| private void registerStatisticMonitorTask() { |
| TimerTask task = new StatisticMonitorTask(); |
| // for now, get monitoring interval from system prop |
| int statInterval = 2 * 60 * 1000; |
| try { |
| String interval = System.getProperty("MONITOR_JTA_RESOURCE_TABLE_SECONDS"); |
| int temp = Integer.parseInt(interval); |
| if (temp > 0) { |
| statInterval = temp; |
| } |
| } catch (Exception ex) { |
| // ignore |
| } |
| |
| _timer.scheduleAtFixedRate(task, 0, statInterval); |
| } |
| |
| // Mods: Adding TimerTask class for statistic dumps |
| class StatisticMonitorTask extends TimerTask { |
| public void run() { |
| if (resourceTable != null) { |
| Map stats = resourceTable.getStats(); |
| Iterator it = stats.entrySet().iterator(); |
| _logger.log(Level.INFO, "********** JavaEETransactionManager resourceTable stats *****"); |
| while (it.hasNext()) { |
| Map.Entry entry = (Map.Entry) it.next(); |
| _logger.log(Level.INFO, (String) entry.getKey() + ": " + entry.getValue()); |
| } |
| } |
| } |
| } |
| |
| /****************************************************************************/ |
| /************************* Helper Methods ***********************************/ |
| /****************************************************************************/ |
| public static String getStatusAsString(int status) { |
| return (String) statusMap.get(status); |
| } |
| |
| private void delistComponentResources(ComponentInvocation inv, boolean suspend) throws InvocationException { |
| |
| try { |
| Transaction tran = (Transaction) inv.getTransaction(); |
| if (isTransactionActive(tran)) { |
| List l = getExistingResourceList(inv.getInstance(), inv); |
| if (l == null || l.size() == 0) |
| return; |
| |
| int flag = (suspend) ? XAResource.TMSUSPEND : XAResource.TMSUCCESS; |
| Iterator it = l.iterator(); |
| while (it.hasNext()) { |
| TransactionalResource h = (TransactionalResource) it.next(); |
| try { |
| if (h.isEnlisted()) { |
| delistResource(tran, h, flag); |
| } |
| } catch (IllegalStateException ex) { |
| if (_logger.isLoggable(Level.FINE)) |
| _logger.log(Level.FINE, "TM: Exception in delistResource", ex); |
| // ignore error due to tx time out |
| } catch (Exception ex) { |
| if (_logger.isLoggable(Level.FINE)) |
| _logger.log(Level.FINE, "TM: Exception in delistResource", ex); |
| it.remove(); |
| handleResourceError(h, ex, tran); |
| } |
| } |
| // END OF IASRI 4658504 |
| } |
| } catch (Exception ex) { |
| _logger.log(Level.SEVERE, "enterprise_distributedtx.excep_in_delist", ex); |
| } |
| } |
| |
| protected boolean enlistXAResource(Transaction tran, TransactionalResource h) |
| throws RollbackException, IllegalStateException, SystemException { |
| |
| if (_logger.isLoggable(Level.FINE)) { |
| _logger.log(Level.FINE, "\n\nIn JavaEETransactionManagerSimplified.enlistXAResource, h=" + h + " h.xares=" + h.getXAResource() |
| + " tran=" + tran); |
| } |
| |
| if (resourceEnlistable(h)) { |
| if (_logger.isLoggable(Level.FINE)) { |
| _logger.log(Level.FINE, "\n\nIn JavaEETransactionManagerSimplified.enlistXAResource - enlistable"); |
| } |
| |
| XAResource res = h.getXAResource(); |
| boolean result = tran.enlistResource(res); |
| if (!h.isEnlisted()) |
| if (_logger.isLoggable(Level.FINE)) { |
| _logger.log(Level.FINE, "\n\nIn JavaEETransactionManagerSimplified.enlistXAResource - enlist"); |
| } |
| |
| h.enlistedInTransaction(tran); |
| return result; |
| } else { |
| return true; |
| } |
| } |
| |
| private void enlistComponentResources(ComponentInvocation inv) throws InvocationException { |
| |
| try { |
| Transaction tran = (Transaction) inv.getTransaction(); |
| if (isTransactionActive(tran)) { |
| List l = getExistingResourceList(inv.getInstance(), inv); |
| if (l == null || l.size() == 0) |
| return; |
| Iterator it = l.iterator(); |
| // END IASRI 4705808 TTT002 |
| while (it.hasNext()) { |
| TransactionalResource h = (TransactionalResource) it.next(); |
| try { |
| enlistResource(tran, h); |
| } catch (Exception ex) { |
| if (_logger.isLoggable(Level.FINE)) |
| _logger.log(Level.WARNING, "enterprise_distributedtx.pooling_excep", ex); |
| |
| it.remove(); |
| handleResourceError(h, ex, tran); |
| } |
| } |
| // END OF IASRI 4658504 |
| } |
| } catch (Exception ex) { |
| _logger.log(Level.SEVERE, "enterprise_distributedtx.excep_in_enlist", ex); |
| } |
| } |
| |
| /** |
| * Called by #componentDestroyed() |
| */ |
| private void processResourceList(List l) { |
| if (l != null && l.size() > 0) { |
| Iterator it = l.iterator(); |
| while (it.hasNext()) { |
| TransactionalResource h = (TransactionalResource) it.next(); |
| try { |
| h.closeUserConnection(); |
| } catch (Exception ex) { |
| if (_logger.isLoggable(Level.FINE)) |
| _logger.log(Level.WARNING, "enterprise_distributedtx.pooling_excep", ex); |
| } |
| } |
| l.clear(); |
| } |
| } |
| |
| private void handleResourceError(TransactionalResource h, Exception ex, Transaction tran) { |
| |
| if (_logger.isLoggable(Level.FINE)) { |
| if (h.isTransactional()) { |
| _logger.log(Level.FINE, "TM: HandleResourceError " + h.getXAResource() + ", " + ex); |
| } |
| } |
| try { |
| if (tran != null && h.isTransactional() && h.isEnlisted()) { |
| tran.delistResource(h.getXAResource(), XAResource.TMSUCCESS); |
| } |
| } catch (Exception ex2) { |
| // ignore |
| } |
| |
| if (ex instanceof RollbackException) { |
| // transaction marked as rollback |
| return; |
| } else if (ex instanceof IllegalStateException) { |
| // transaction aborted by time out |
| // close resource |
| try { |
| h.closeUserConnection(); |
| } catch (Exception ex2) { |
| // Log.err.println(ex2); |
| } |
| } else { |
| // destroy resource. RM Error. |
| try { |
| h.destroyResource(); |
| } catch (Exception ex2) { |
| // Log.err.println(ex2); |
| } |
| } |
| } |
| |
| private Object getResourceTableKey(Object instance, ComponentInvocation inv) { |
| Object key = null; |
| if (inv != null) { |
| key = inv.getResourceTableKey(); |
| } |
| |
| // If ComponentInvocation is null or doesn't hold the key, |
| // use instance as the key. |
| if (key == null) { |
| key = instance; |
| } |
| return key; |
| } |
| |
| private boolean isTransactionActive(Transaction tran) { |
| return (tran != null); |
| } |
| |
| /** |
| * JTS version of the #delistResource |
| * |
| * @param suspend true if the transaction association should be suspended rather than ended. |
| */ |
| private boolean delistJTSResource(Transaction tran, TransactionalResource h, int flag) throws IllegalStateException, SystemException { |
| |
| // ** XXX Throw an exception instead ??? XXX ** |
| if (_logger.isLoggable(Level.FINE)) { |
| _logger.log(Level.FINE, "\n\nIn JavaEETransactionManagerSimplified.delistJTSResource, h=" + h + " h.xares=" + h.getXAResource() |
| + " tran=" + tran + " flag=" + flag); |
| } |
| |
| if (!h.isShareable() || multipleEnlistDelists) { |
| if (_logger.isLoggable(Level.FINE)) { |
| _logger.log(Level.FINE, |
| "\n\nIn JavaEETransactionManagerSimplified.delistJTSResource " + "- !h.isShareable() || multipleEnlistDelists"); |
| } |
| |
| if (h.isTransactional() && h.isEnlisted()) { |
| if (_logger.isLoggable(Level.FINE)) { |
| _logger.log(Level.FINE, "\n\nIn JavaEETransactionManagerSimplified.delistJTSResource - delist"); |
| } |
| |
| return tran.delistResource(h.getXAResource(), flag); |
| } else { |
| return true; |
| } |
| } |
| return true; |
| } |
| |
| private void remove(Transaction tx) { |
| getDelegate().removeTransaction(tx); |
| |
| /** |
| * XXX TO BE MOVED TO XA DELEGATE XXX javaEETM.globalTransactions.remove(jtsTx); XXX TO BE MOVED TO XA DELEGATE XXX |
| **/ |
| } |
| |
| /** |
| * Called by JavaEETransactionImpl also |
| */ |
| synchronized JavaEETransactionManagerDelegate getDelegate() { |
| JavaEETransactionManagerDelegate d = delegates.get(); |
| return (d == null) ? delegate : d; |
| } |
| |
| private JavaEETransactionManagerDelegate setDelegate() { |
| JavaEETransactionManagerDelegate d = delegates.get(); |
| if (d == null) { |
| d = delegate; |
| delegates.set(d); |
| } |
| |
| return d; |
| } |
| |
| public boolean isDelegate(JavaEETransactionManagerDelegate d) { |
| if (delegate == null) |
| return false; |
| |
| return (d.getClass().getName().equals(delegate.getClass().getName())); |
| } |
| |
| private void initDelegates() { |
| if (habitat == null) |
| return; // the delegate will be set explicitly |
| |
| for (JavaEETransactionManagerDelegate d : habitat |
| .<JavaEETransactionManagerDelegate>getAllServices(JavaEETransactionManagerDelegate.class)) { |
| setDelegate(d); |
| } |
| |
| if (delegate != null && _logger.isLoggable(Level.FINE)) |
| _logger.log(Level.INFO, "enterprise_used_delegate_name", delegate.getClass().getName()); |
| |
| } |
| |
| public synchronized void setDelegate(JavaEETransactionManagerDelegate d) { |
| // XXX Check if it's valid to set or if we need to remember all that asked. |
| |
| int curr = 0; |
| if (delegate != null) { |
| curr = delegate.getOrder(); |
| } |
| if (d.getOrder() > curr) { |
| delegate = d; |
| |
| // XXX Hk2 work around XXX |
| delegate.setTransactionManager(this); |
| |
| if (_logger.isLoggable(Level.FINE)) |
| _logger.log(Level.FINE, "Replaced delegate with " + d.getClass().getName()); |
| } |
| } |
| |
| public Logger getLogger() { |
| return _logger; |
| } |
| |
| public void monitorTxCompleted(Object obj, boolean b) { |
| if (monitoringEnabled) { |
| _monitorTxCompleted(obj, b); |
| } |
| } |
| |
| public void monitorTxBegin(Transaction tx) { |
| if (monitoringEnabled) { |
| activeTransactions.add(tx); |
| monitor.transactionActivatedEvent(); |
| } |
| } |
| |
| public boolean resourceEnlistable(TransactionalResource h) { |
| return (h.isTransactional() && (!h.isEnlisted() || !h.isShareable() || multipleEnlistDelists)); |
| } |
| |
| public boolean isInvocationStackEmpty() { |
| return (invMgr == null || invMgr.isInvocationStackEmpty()); |
| } |
| |
| public void setTransactionCompeting(boolean b) { |
| ComponentInvocation curr = invMgr.getCurrentInvocation(); |
| if (curr != null) |
| curr.setTransactionCompeting(b); |
| } |
| |
| public JavaEETransaction createImportedTransaction(TransactionInternal jtsTx) throws SystemException { |
| JavaEETransactionImpl tx = new JavaEETransactionImpl(jtsTx, this); |
| try { |
| jtsTx.registerSynchronization(new JTSSynchronization(jtsTx, this)); |
| } catch (RollbackException rlex) { |
| throw new SystemException(rlex.toString()); |
| } catch (IllegalStateException isex) { |
| throw new SystemException(isex.toString()); |
| } catch (Exception ex) { |
| throw new SystemException(ex.toString()); |
| } |
| |
| return tx; |
| } |
| |
| /****************************************************************************/ |
| /** Implementation of jakarta.transaction.Synchronization *********************/ |
| /****************************************************************************/ |
| private static class JTSSynchronization implements Synchronization { |
| private TransactionInternal jtsTx; |
| private JavaEETransactionManagerSimplified javaEETM; |
| |
| JTSSynchronization(TransactionInternal jtsTx, JavaEETransactionManagerSimplified javaEETM) { |
| this.jtsTx = jtsTx; |
| this.javaEETM = javaEETM; |
| } |
| |
| public void beforeCompletion() { |
| } |
| |
| public void afterCompletion(int status) { |
| javaEETM.remove(jtsTx); |
| } |
| } |
| } |