From 041e7889aad2ea67497cd01b82dedd7e2d3c40a5 Mon Sep 17 00:00:00 2001 From: "R.M. Morrien" Date: Thu, 4 Apr 2024 14:00:04 +0200 Subject: [PATCH] Part 1 for issue #24900 Document and rename some ResourceHandle fields. Document and rename some ConnectionPool fields. Remove some unused methods and constructor parameters. Functional / possible incompatibility changes in DataStructureFactory and in ResourceHandle due to constructor changes. Part 2 for issue #24900 Start with a new PoolManagertImpl unit test to understand enlisted versus busy states of Resource handles and the wiring inside a transaction to keep track of all used resources. Part 3 for issue #24900 More documentation and more unit test coverage. Part 4 for issue #24900 Add more detailed logging to improve log file analysis. --- .../inbound/BasicResourceAllocator.java | 8 +- .../inbound/ConnectorMessageBeanClient.java | 4 +- .../internal/api/ResourceHandle.java | 59 +- .../internal/api/TransactedPoolManager.java | 25 +- .../connectors/ConnectorConnectionPool.java | 11 +- .../connectors/ConnectorRuntime.java | 3 +- .../util/ConnectionPoolReconfigHelper.java | 9 + .../AssocWithThreadResourceHandle.java | 8 +- .../resource/ConnectorXAResource.java | 14 +- .../enterprise/resource/ResourceHandle.java | 247 ++++++--- .../sun/enterprise/resource/ResourceSpec.java | 19 +- .../enterprise/resource/ResourceState.java | 32 +- .../allocator/AbstractConnectorAllocator.java | 17 +- .../allocator/ConnectorAllocator.java | 14 +- .../allocator/LocalTxConnectorAllocator.java | 13 +- .../allocator/NoTxConnectorAllocator.java | 19 +- .../resource/allocator/ResourceAllocator.java | 22 +- .../LocalTxConnectionEventListener.java | 32 ++ .../resource/pool/AbstractPoolManager.java | 48 +- .../pool/AssocWithThreadResourcePool.java | 4 +- .../resource/pool/ConnectionPool.java | 238 ++++---- .../enterprise/resource/pool/PoolManager.java | 144 ++++- .../resource/pool/PoolManagerImpl.java | 94 +++- .../enterprise/resource/pool/PoolStatus.java | 5 + .../resource/pool/PoolTxHelper.java | 25 +- .../resource/pool/ResourcePool.java | 79 ++- .../resource/pool/UnpooledResource.java | 7 +- .../pool/datastructure/DataStructure.java | 16 +- .../datastructure/DataStructureFactory.java | 22 +- .../pool/datastructure/ListDataStructure.java | 10 +- .../datastructure/RWLockDataStructure.java | 13 +- .../resizer/AssocWithThreadPoolResizer.java | 2 +- .../resource/pool/resizer/Resizer.java | 16 +- .../rm/LazyEnlistableResourceManagerImpl.java | 12 +- .../resource/rm/ResourceManager.java | 4 +- .../resource/rm/ResourceManagerImpl.java | 40 +- .../LocalTxConnectionEventListenerTest.java | 2 +- .../resource/pool/ConnectionPoolTest.java | 47 +- .../resource/pool/InjectionUtil.java | 37 ++ .../resource/pool/PoolManagerImplTest.java | 515 ++++++++++++++++++ .../RWLockDataStructureTest.java | 27 +- .../mock/JavaEETransactionManagerMock.java | 280 ++++++++++ .../pool/mock/JavaEETransactionMock.java | 139 +++++ .../EJBContainerTransactionManager.java | 57 ++ .../sun/gjc/spi/ManagedConnectionImpl.java | 2 +- .../sun/gjc/spi/base/ConnectionHolder.java | 8 + .../transaction/api/JavaEETransaction.java | 9 +- .../api/JavaEETransactionManager.java | 12 +- .../spi/TransactionalResource.java | 68 ++- .../transaction/JavaEETransactionImpl.java | 31 +- .../JavaEETransactionManagerSimplified.java | 7 - .../JavaEETransactionManagerJTSDelegate.java | 49 +- .../jts/JavaEETransactionManagerTest.java | 21 +- 53 files changed, 2138 insertions(+), 508 deletions(-) create mode 100644 appserver/connectors/connectors-runtime/src/test/java/com/sun/enterprise/resource/pool/InjectionUtil.java create mode 100644 appserver/connectors/connectors-runtime/src/test/java/com/sun/enterprise/resource/pool/PoolManagerImplTest.java create mode 100644 appserver/connectors/connectors-runtime/src/test/java/com/sun/enterprise/resource/pool/mock/JavaEETransactionManagerMock.java create mode 100644 appserver/connectors/connectors-runtime/src/test/java/com/sun/enterprise/resource/pool/mock/JavaEETransactionMock.java diff --git a/appserver/connectors/connectors-inbound-runtime/src/main/java/com/sun/enterprise/connectors/inbound/BasicResourceAllocator.java b/appserver/connectors/connectors-inbound-runtime/src/main/java/com/sun/enterprise/connectors/inbound/BasicResourceAllocator.java index b7f4ffe9ef2..28e4f6a3509 100755 --- a/appserver/connectors/connectors-inbound-runtime/src/main/java/com/sun/enterprise/connectors/inbound/BasicResourceAllocator.java +++ b/appserver/connectors/connectors-inbound-runtime/src/main/java/com/sun/enterprise/connectors/inbound/BasicResourceAllocator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Contributors to the Eclipse Foundation + * Copyright (c) 2022, 2024 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 @@ -42,13 +42,11 @@ public final class BasicResourceAllocator extends AbstractConnectorAllocator { public BasicResourceAllocator() { } - @Override public ResourceHandle createResource() throws PoolingException { throw new UnsupportedOperationException(); } - public ResourceHandle createResource(XAResource xaResource) throws PoolingException { ResourceHandle resourceHandle = null; ResourceSpec spec = new ResourceSpec(JMS_RESOURCE_FACTORY, ResourceSpec.JMS); @@ -56,7 +54,7 @@ public ResourceHandle createResource(XAResource xaResource) throws PoolingExcept if (xaResource != null) { logger.logp(Level.FINEST, "BasicResourceAllocator", "createResource", "NOT NULL", xaResource); try { - resourceHandle = new ResourceHandle(null, spec, this, null); + resourceHandle = new ResourceHandle(null, spec, this); if (logger.isLoggable(Level.FINEST)) { xaResource = new XAResourceWrapper(xaResource); } @@ -71,14 +69,12 @@ public ResourceHandle createResource(XAResource xaResource) throws PoolingExcept return resourceHandle; } - @Override public void closeUserConnection(ResourceHandle resourceHandle) throws PoolingException { throw new UnsupportedOperationException(); } - @Override public boolean matchConnection(ResourceHandle resourceHandle) { return false; diff --git a/appserver/connectors/connectors-inbound-runtime/src/main/java/com/sun/enterprise/connectors/inbound/ConnectorMessageBeanClient.java b/appserver/connectors/connectors-inbound-runtime/src/main/java/com/sun/enterprise/connectors/inbound/ConnectorMessageBeanClient.java index 62978708c44..35425716ee2 100755 --- a/appserver/connectors/connectors-inbound-runtime/src/main/java/com/sun/enterprise/connectors/inbound/ConnectorMessageBeanClient.java +++ b/appserver/connectors/connectors-inbound-runtime/src/main/java/com/sun/enterprise/connectors/inbound/ConnectorMessageBeanClient.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023 Contributors to the Eclipse Foundation + * Copyright (c) 2022, 2024 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 @@ -353,7 +353,7 @@ private EjbMessageBeanDescriptor getDescriptor() { */ @Override public MessageEndpoint createEndpoint(XAResource xa) throws UnavailableException { - // This is a temporary workaround for blocking the the create enpoint + // This is a temporary workaround for blocking the created endpoint // until the deployment completes. One thread would wait for maximum a // a minute. return createEndpoint(xa, WAIT_TIME); diff --git a/appserver/connectors/connectors-internal-api/src/main/java/com/sun/appserv/connectors/internal/api/ResourceHandle.java b/appserver/connectors/connectors-internal-api/src/main/java/com/sun/appserv/connectors/internal/api/ResourceHandle.java index 8462afe9977..574007f6aaf 100644 --- a/appserver/connectors/connectors-internal-api/src/main/java/com/sun/appserv/connectors/internal/api/ResourceHandle.java +++ b/appserver/connectors/connectors-internal-api/src/main/java/com/sun/appserv/connectors/internal/api/ResourceHandle.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2022, 2024 Contributors to the Eclipse Foundation. * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the @@ -19,33 +20,85 @@ import javax.transaction.xa.XAResource; /** - * ResourceHandle interface to be used by transaction manager components + * ResourceHandle interface to be used by transaction manager components. * * @author Marina Vatkina */ public interface ResourceHandle { + /** + * Returns true if the resource is part of a transaction. + * + * @return true if the resource is part of a transaction. + */ public boolean isTransactional(); - //TODO V3 not needed as of now. + /** + * To check whether lazy enlistment is suspended or not.
+ * If {@code true}, transaction manager will not do enlist/lazy enlist. + * + * @return true if enlistment is suspended, otherwise false. + */ public boolean isEnlistmentSuspended(); + /** + * Allows a ResourceManager to change the enlistement suspended state of a resource to enlist a resource in a + * transaction. + * + * @param enlistmentSuspended true if enlistment in a transaction is suspended, false if enlistment is not suspended. + */ public void setEnlistmentSuspended(boolean enlistmentSuspended); + /** + * Returns the (optional) XAResource reference for this resource handle. + * + * @return the XAResource reference for this resource handle or null if no reference is set. + */ public XAResource getXAResource(); + /** + * Returns true if the ResourceHandle is supported in an XA transaction. + * + * @return true if the ResourceHandle is supported in an XA transaction, otherwise false. + */ public boolean supportsXA(); + /** + * Returns the component instance holding this resource handle. + * + * @return the component instance holding this resource handle. + */ public Object getComponentInstance(); + /** + * Sets the component instance holding this resource handle. + * + * @param instance the component instance holding this resource handle. + */ public void setComponentInstance(Object instance); + /** + * Closes the (optional) 'userConnection' / 'connection handle' (used by the application code to refer to the underlying + * physical connection). Example: the ManagedConnection represented by this ResourceHandle is closed / cleaned up. + * + * @throws PoolingException wrapping any 'userConnection' specific exception that might occur during the close call. + * @throws UnsupportedOperationException when the method is not implemented. + */ public void closeUserConnection() throws PoolingException; + /** + * Returns true if the resource handle is enlisted in a transaction. + * + * @return true if the resource handle is enlisted in a transaction. + */ public boolean isEnlisted(); + /** + * Returns true if the resource handle is sharable within the component. + * + * @return true if the resource handle is sharable within the component. + */ public boolean isShareable(); - public void destroyResource(); } diff --git a/appserver/connectors/connectors-internal-api/src/main/java/com/sun/appserv/connectors/internal/api/TransactedPoolManager.java b/appserver/connectors/connectors-internal-api/src/main/java/com/sun/appserv/connectors/internal/api/TransactedPoolManager.java index 8e553557ce2..fbcd5e9986e 100644 --- a/appserver/connectors/connectors-internal-api/src/main/java/com/sun/appserv/connectors/internal/api/TransactedPoolManager.java +++ b/appserver/connectors/connectors-internal-api/src/main/java/com/sun/appserv/connectors/internal/api/TransactedPoolManager.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2024 Eclipse Foundation and/or its affiliates. All rights reserved. * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the @@ -29,24 +30,30 @@ public interface TransactedPoolManager { /** - * Indicate that a resource is enlisted. + * Indicate that a resource is enlisted.
+ * Expecting this method is called from the + * {@link com.sun.enterprise.resource.ResourceHandle#enlistedInTransaction(Transaction)} method and not directly from a + * pool manager. + * * @param tran Transaction to which the resource is enlisted - * @param res Resource that is enlisted + * @param resource Resource that is enlisted * @throws IllegalStateException when unable to enlist the resource */ - void resourceEnlisted(Transaction tran, ResourceHandle res) throws IllegalStateException; + void resourceEnlisted(Transaction tran, ResourceHandle resource) throws IllegalStateException; /** - * registers the provided resource with the component & enlists the resource in the transaction - * @param handle resource-handle + * Registers the provided resource with the component & enlists the resource in the transaction + * + * @param resource Resource to be registered. * @throws PoolingException when unable to register the resource */ - void registerResource(ResourceHandle handle) throws PoolingException; + void registerResource(ResourceHandle resource) throws PoolingException; /** - * unregisters the resource from the component and delists the resource from the transaction - * @param resource resource-handle - * @param xaresFlag + * Unregisters the resource from the component and delists the resource from the transaction + * + * @param resource Resource to be unregistered. + * @param xaresFlag flag indicating transaction success. This can be XAResource.TMSUCCESS or XAResource.TMFAIL */ void unregisterResource(ResourceHandle resource, int xaresFlag); } diff --git a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/connectors/ConnectorConnectionPool.java b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/connectors/ConnectorConnectionPool.java index f8ecd208552..6abb6bd8c8b 100755 --- a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/connectors/ConnectorConnectionPool.java +++ b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/connectors/ConnectorConnectionPool.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Contributors to the Eclipse Foundation + * Copyright (c) 2022, 2024 Contributors to the Eclipse Foundation. * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the @@ -75,7 +75,6 @@ public class ConnectorConnectionPool implements Serializable { private String poolWaitQueue; private String dataStructureParameters; private String resourceGatewayClass; - private String resourceSelectionStrategyClass; private boolean nonTransactional_; private boolean nonComponent_; @@ -774,14 +773,6 @@ public void setResourceGatewayClass(String resourceGatewayClass) { this.resourceGatewayClass = resourceGatewayClass; } - public String getResourceSelectionStrategyClass() { - return resourceSelectionStrategyClass; - } - - public void setResourceSelectionStrategyClass(String resourceSelectionStrategyClass) { - this.resourceSelectionStrategyClass = resourceSelectionStrategyClass; - } - public boolean isPreferValidateOverRecreate() { return preferValidateOverRecreate_; } diff --git a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/connectors/ConnectorRuntime.java b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/connectors/ConnectorRuntime.java index 972b22999c1..eca2cbe8104 100755 --- a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/connectors/ConnectorRuntime.java +++ b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/connectors/ConnectorRuntime.java @@ -234,9 +234,8 @@ public class ConnectorRuntime implements com.sun.appserv.connectors.internal.api @Inject private Provider resourceManagerProvider; - /* protected for unit test */ @Inject - protected ProcessEnvironment processEnvironment; + private ProcessEnvironment processEnvironment; @Inject private DriverLoader driverLoader; diff --git a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/connectors/util/ConnectionPoolReconfigHelper.java b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/connectors/util/ConnectionPoolReconfigHelper.java index fa4d513d6c5..578ff3a2e4a 100644 --- a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/connectors/util/ConnectionPoolReconfigHelper.java +++ b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/connectors/util/ConnectionPoolReconfigHelper.java @@ -34,8 +34,17 @@ public final class ConnectionPoolReconfigHelper { LogDomains.RSR_LOGGER); public enum ReconfigAction { + /** + * Recreate connection pool + */ RECREATE_POOL, + /** + * Update ManagedConnectionFactory and attributes + */ UPDATE_MCF_AND_ATTRIBUTES, + /** + * No operation + */ NO_OP } diff --git a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/AssocWithThreadResourceHandle.java b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/AssocWithThreadResourceHandle.java index a81d34acf60..c292d3a7858 100644 --- a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/AssocWithThreadResourceHandle.java +++ b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/AssocWithThreadResourceHandle.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Contributors to the Eclipse Foundation + * Copyright (c) 2022, 2024 Contributors to the Eclipse Foundation. * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the @@ -19,6 +19,8 @@ import com.sun.enterprise.resource.allocator.ResourceAllocator; +import jakarta.resource.spi.ManagedConnection; + /** * ResourceHandle with state related to assoc-with-thread pool * @@ -30,8 +32,8 @@ public class AssocWithThreadResourceHandle extends ResourceHandle { private long threadId_; private boolean dirty_; - public AssocWithThreadResourceHandle(Object resource, ResourceSpec spec, ResourceAllocator alloc, ClientSecurityInfo info) { - super(resource, spec, alloc, info); + public AssocWithThreadResourceHandle(ManagedConnection resource, ResourceSpec spec, ResourceAllocator alloc) { + super(resource, spec, alloc); } public boolean isDirty() { diff --git a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/ConnectorXAResource.java b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/ConnectorXAResource.java index 8efe7edf242..01db86e3751 100644 --- a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/ConnectorXAResource.java +++ b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/ConnectorXAResource.java @@ -81,7 +81,7 @@ private void handleResourceException(Exception ex) throws XAException { @Override public void commit(Xid xid, boolean onePhase) throws XAException { try { - ManagedConnection managedConnection = (ManagedConnection) getResourceHandle().getResource(); + ManagedConnection managedConnection = getResourceHandle().getResource(); managedConnection.getLocalTransaction().commit(); } catch (Exception ex) { handleResourceException(ex); @@ -96,7 +96,7 @@ public void start(Xid xid, int flags) throws XAException { try { ResourceHandle handle = getResourceHandle(); if (!localHandle_.equals(handle)) { - ManagedConnection managedConnection = (ManagedConnection) handle.getResource(); + ManagedConnection managedConnection = handle.getResource(); managedConnection.associateConnection(userHandle); LocalTxConnectionEventListener listener = (LocalTxConnectionEventListener) handle.getListener(); @@ -122,7 +122,7 @@ public void end(Xid xid, int flags) throws XAException { ResourceHandle handle = listener.removeAssociation(userHandle); if (handle != null) { // not needed, just to be sure. - ManagedConnection associatedConnection = (ManagedConnection) handle.getResource(); + ManagedConnection associatedConnection = handle.getResource(); associatedConnection.associateConnection(userHandle); _logger.log(FINE, "connection_sharing_reset_association", userHandle); } @@ -177,7 +177,7 @@ public Xid[] recover(int flag) throws XAException { public void rollback(Xid xid) throws XAException { try { ResourceHandle handle = getResourceHandle(); - ManagedConnection managedConnection = (ManagedConnection) handle.getResource(); + ManagedConnection managedConnection = handle.getResource(); managedConnection.getLocalTransaction().rollback(); } catch (Exception ex) { handleResourceException(ex); @@ -211,7 +211,7 @@ private ResourceHandle getResourceHandle() throws PoolingException { } if (resourceHandle.getResourceState().isUnenlisted()) { - ManagedConnection managedConnection = (ManagedConnection) resourceHandle.getResource(); + ManagedConnection managedConnection = resourceHandle.getResource(); // Begin the local transaction if first time // this ManagedConnection is used in this JTA transaction @@ -236,8 +236,8 @@ private void resetAssociation() throws XAException { // Clear the associations and Map all associated handles back to their actual Managed Connection. Map associatedHandles = listener.getAssociatedHandlesAndClearMap(); for (Entry userHandleEntry : associatedHandles.entrySet()) { - ResourceHandle associatedHandle = (ResourceHandle) userHandleEntry.getValue(); - ManagedConnection associatedConnection = (ManagedConnection) associatedHandle.getResource(); + ResourceHandle associatedHandle = userHandleEntry.getValue(); + ManagedConnection associatedConnection = associatedHandle.getResource(); associatedConnection.associateConnection(userHandleEntry.getKey()); _logger.log(FINE, "connection_sharing_reset_association", userHandleEntry.getKey()); } diff --git a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/ResourceHandle.java b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/ResourceHandle.java index 98e02488ed9..be672a1660a 100644 --- a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/ResourceHandle.java +++ b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/ResourceHandle.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023 Contributors to the Eclipse Foundation + * Copyright (c) 2022, 2024 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 @@ -27,11 +27,11 @@ import jakarta.resource.spi.ConnectionEventListener; import jakarta.resource.spi.DissociatableManagedConnection; import jakarta.resource.spi.LazyEnlistableManagedConnection; +import jakarta.resource.spi.ManagedConnection; import jakarta.transaction.Transaction; import java.util.logging.Logger; -import javax.security.auth.Subject; import javax.transaction.xa.XAResource; import static java.util.logging.Level.FINEST; @@ -47,38 +47,146 @@ public class ResourceHandle implements com.sun.appserv.connectors.internal.api.R private static final Logger logger = LogDomains.getLogger(ResourceHandle.class, LogDomains.RSR_LOGGER); - // unique ID for resource handles + /** + * Unique Id sequence generator value for generating unique ResourceHandle ids. + */ static private long idSequence; + /** + * The unique Id of this ResourceHandle instance. + */ private final long id; - private final ClientSecurityInfo info; - private final Object resource; // represents ManagedConnection - private ResourceSpec spec; - private XAResource xaRes; - private Object userConnection; // represents connection-handle to user + + /** + * The (optional) object represented by this handle. Normally the ManagedConnection object.
+ * THe object is known to be null in case of the BasicResourrceAllocator. + */ + private final ManagedConnection resource; + + /** + * The ResourceSpec reference, which can be used to find the correct connection pool for the resource handle.
+ * Note: this value is set in the constructor, but can be overwritten later during the lifetime of the resource handle, + * while {@link #resourceAllocator} is not overwritten. + */ + private ResourceSpec resourceSpec; + + /** + * The (optional) XAResource reference for this resource handle. The value is set in this resource handle together with + * the {@link #userConnection}. + */ + private XAResource xaResource; + + /** + * The (optional) 'userConnection' / 'connection handle', used by the application code to refer to the underlying + * physical connection.
+ * The value is set in this resource handle together with the {@link #xaResource}. + *

+ * TODO: consider renaming userConnection to connectionHandle + */ + private Object userConnection; + + /** + * The ResourceAllocator that created this resource handle instance. + */ private final ResourceAllocator resourceAllocator; - private Object instance; // the component instance holding this resource - private int shareCount; // sharing within a component (XA only) - private final boolean supportsXAResource; - private Subject subject; + /** + * The component instance holding this resource handle. + */ + private Object instance; + + /** + * Sharing within a component (XA only). For XA-capable connections, multiple connections within a component are + * collapsed into one. shareCount keeps track of the number of additional shared connections + */ + private int shareCount; + + /** + * Resource state class providing 'timestamp', 'enlisted' and 'busy' fields.
+ * Other state fields like 'enlistmentSuspended', 'shareCount', 'usageCount', 'lastValidated' and others are directly + * part of this class as a field. + */ + private ResourceState state = new ResourceState(); - private ResourceState state; + /** + * Used by LocalTxConnectorAllocator to save a listener for the resource handle. + */ private ConnectionEventListener listener; + /** + * Used by LazyEnlistableResourceManagerImpl to keep track of the enlistment suspended state of a resource handle. + * LazyEnlistableResourceManagerImpl is used when setting "lazy-connection-enlistment" to enabled in the connection + * pool.
+ * Default: false, false means: every resource is enlisted to a transaction. + */ private boolean enlistmentSuspended; - private boolean supportsLazyEnlistment_; - private boolean supportsLazyAssoc_; + /** + * False if the ResourceAllocator is of the type LocalTxConnectorAllocator, otherwise true. + */ + private final boolean supportsXAResource; + + /** + * True if the Resource object ManagedConnection is of the type LazyEnlistableManagedConnection, otherwise false. + */ + private boolean supportsLazyEnlistment; + /** + * True if the Resource object ManagedConnection is of the type DissociatableManagedConnection, otherwise false. + */ + private boolean supportsLazyAssociation; + + /** + * Resource handle specific lock, which can be used to lock the resource handle when state of the resource needs to be + * changed. This is currently only used in the AssocWithThreadResourcePool implementation. + */ public final Object lock = new Object(); - private long lastValidated; // holds the latest time at which the connection was validated. - private int usageCount; // holds the no. of times the handle(connection) is used so far. - private int partition; - private int index; + + /** + * Holds the latest time at which the connection was validated.
+ * Could also be seen as part of ResourceState. + */ + private long lastValidated; + + /** + * Holds the number of times the handle(connection) is used so far. It is used by the ConnectionPool logic in case the + * "max-connection-usage-count" option is set in the connection pool.
+ * Could also be seen as part of ResourceState. + */ + private int usageCount; + + /** + * Index of this ResourceHandle in the RWLockDataStructure internal ResourceHandle[]. RWLockDataStructure uses this + * index value for optimistic locking of the resource. + */ + private int rwLockDataStructureResourceIndex; + + /** + * Value isDestroyByLeakTimeOut is set to true if ConnectionPool reclaimConnection logic was called when a potential + * leak was found. The value is used when a resource is freed or closed by the poolLifeCycleListener to update + * statistics.
+ * Could also be seen as part of ResourceState. + */ private boolean isDestroyByLeakTimeOut; + + /** + * The connectionErrorOccurred field is set to true when a connection was aborted, or a connection error occurred, or + * when a connection being closes is bad.
+ * Could also be seen as part of ResourceState. + */ private boolean connectionErrorOccurred; + /** + * Value markedReclaim is set by the + * {@link com.sun.enterprise.resource.pool.ConnectionLeakDetector#potentialConnectionLeakFound(ResourceHandle)} method + * when the resource is set to be reclaimed. Note: this value is only used to update statistics, the resource itself is + * already removed from the pool the moment it is marked for reclaim. Code seems to suggest that this is also set when + * {@link #isDestroyByLeakTimeOut} is set. Perhaps this boolean is not needed, statistics could perhaps be updated + * immediately when isDestroyByLeakTimeOut is updated.
+ * Could also be seen as part of ResourceState. + */ + private boolean markedReclaim; + static private long getNextId() { synchronized (ResourceHandle.class) { idSequence++; @@ -86,27 +194,31 @@ static private long getNextId() { } } - private boolean markedReclaim; - - public ResourceHandle(Object resource, ResourceSpec spec, ResourceAllocator alloc, ClientSecurityInfo info) { + /** + * ResourceHandle constructor + * + * @param resource the (optional) object represented by this handle. Normally the ManagedConnection object. + * @param resourceSpec the ResourceSpec reference, allowing this resource handle to find the correct connection pool. + * @param resourceAllocator the ResourceAllocator that is creating this resource handle instance. + */ + public ResourceHandle(ManagedConnection resource, ResourceSpec resourceSpec, ResourceAllocator resourceAllocator) { this.id = getNextId(); - this.spec = spec; - this.info = info; this.resource = resource; - this.resourceAllocator = alloc; + this.resourceSpec = resourceSpec; + this.resourceAllocator = resourceAllocator; - if (alloc instanceof LocalTxConnectorAllocator) { + if (resourceAllocator instanceof LocalTxConnectorAllocator) { supportsXAResource = false; } else { supportsXAResource = true; } if (resource instanceof LazyEnlistableManagedConnection) { - supportsLazyEnlistment_ = true; + supportsLazyEnlistment = true; } if (resource instanceof DissociatableManagedConnection) { - supportsLazyAssoc_ = true; + supportsLazyAssociation = true; } } @@ -118,13 +230,6 @@ public boolean isTransactional() { return resourceAllocator.isTransactional(); } - /** - * To check whether lazy enlistment is suspended or not. - * - *

If {@code true}, TM will not do enlist/lazy enlist. - * - * @return boolean - */ @Override public boolean isEnlistmentSuspended() { return enlistmentSuspended; @@ -157,25 +262,21 @@ public ResourceAllocator getResourceAllocator() { return resourceAllocator; } - public Object getResource() { + public ManagedConnection getResource() { return resource; } - public ClientSecurityInfo getClientSecurityInfo() { - return info; - } - - public void setResourceSpec(ResourceSpec spec) { - this.spec = spec; + public void setResourceSpec(ResourceSpec resourceSpec) { + this.resourceSpec = resourceSpec; } public ResourceSpec getResourceSpec() { - return spec; + return resourceSpec; } @Override public XAResource getXAResource() { - return xaRes; + return xaResource; } public Object getUserConnection() { @@ -201,30 +302,27 @@ public long getId() { return id; } - public void fillInResourceObjects(Object userConnection, XAResource xaRes) { + public void fillInResourceObjects(Object userConnection, XAResource xaResource) { if (userConnection != null) { this.userConnection = userConnection; } - if (xaRes != null) { + if (xaResource != null) { if (logger.isLoggable(FINEST)) { // When Log level is Finest, XAResourceWrapper is used to log // all XA interactions - Don't wrap XAResourceWrapper if it is // already wrapped - if ((xaRes instanceof XAResourceWrapper) || (xaRes instanceof ConnectorXAResource)) { - this.xaRes = xaRes; + if ((xaResource instanceof XAResourceWrapper) || (xaResource instanceof ConnectorXAResource)) { + this.xaResource = xaResource; } else { - this.xaRes = new XAResourceWrapper(xaRes); + this.xaResource = new XAResourceWrapper(xaResource); } } else { - this.xaRes = xaRes; + this.xaResource = xaResource; } } } - // For XA-capable connections, multiple connections within a - // component are collapsed into one. shareCount keeps track of - // the number of additional shared connections public void incrementCount() { shareCount++; } @@ -241,14 +339,6 @@ public int getShareCount() { return shareCount; } - public void setSubject(Subject subject) { - this.subject = subject; - } - - public Subject getSubject() { - return subject; - } - @Override public boolean equals(Object other) { if (other == null) { @@ -269,7 +359,7 @@ public int hashCode() { @Override public String toString() { - return String.valueOf(id); + return ""; } public void setConnectionErrorOccurred() { @@ -280,10 +370,6 @@ public boolean hasConnectionErrorOccurred() { return connectionErrorOccurred; } - public void setResourceState(ResourceState state) { - this.state = state; - } - public ResourceState getResourceState() { return state; } @@ -301,14 +387,9 @@ public boolean isShareable() { return resourceAllocator.shareableWithinComponent(); } - @Override - public void destroyResource() { - throw new UnsupportedOperationException("Transaction is not supported yet"); - } - @Override public boolean isEnlisted() { - return state != null && state.isEnlisted(); + return state.isEnlisted(); } public long getLastValidated() { @@ -327,33 +408,25 @@ public void incrementUsageCount() { usageCount++; } - public int getPartition() { - return partition; - } - - public void setPartition(int partition) { - this.partition = partition; - } - - public int getIndex() { - return index; + public int getRwLockDataStructureResourceIndex() { + return rwLockDataStructureResourceIndex; } - public void setIndex(int index) { - this.index = index; + public void setRwLockDataStructureResourceIndex(int rwLockDataStructureResourceIndex) { + this.rwLockDataStructureResourceIndex = rwLockDataStructureResourceIndex; } @Override public String getName() { - return spec.getResourceId(); + return resourceSpec.getResourceId(); } public boolean supportsLazyEnlistment() { - return supportsLazyEnlistment_; + return supportsLazyEnlistment; } public boolean supportsLazyAssociation() { - return supportsLazyAssoc_; + return supportsLazyAssociation; } @Override diff --git a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/ResourceSpec.java b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/ResourceSpec.java index 565b186f221..8ea4df9cf51 100644 --- a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/ResourceSpec.java +++ b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/ResourceSpec.java @@ -183,15 +183,16 @@ public Object getConnectionToAssociate() { @Override public String toString() { - StringBuffer sb = new StringBuffer("ResourceSpec :- "); - sb.append("\nconnectionPoolName : ").append(poolInfo); - sb.append("\nisXA_ : ").append(isXA_); - sb.append("\nresoureId : ").append(resourceId); - sb.append("\nresoureIdType : ").append(resourceIdType); - sb.append("\npmResource : ").append(pmResource); - sb.append("\nnonTxResource : ").append(nonTxResource); - sb.append("\nlazyEnlistable : ").append(lazyEnlistable_); - sb.append("\nlazyAssociatable : ").append(lazyAssociatable_); + StringBuffer sb = new StringBuffer(""); return sb.toString(); } } diff --git a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/ResourceState.java b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/ResourceState.java index 724f4ae58ba..2ff073ef280 100644 --- a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/ResourceState.java +++ b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/ResourceState.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Contributors to the Eclipse Foundation + * Copyright (c) 2022, 2024 Contributors to the Eclipse Foundation. * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the @@ -18,8 +18,27 @@ package com.sun.enterprise.resource; public class ResourceState { + + /** + * Indicates that a resource has been enlisted in a transaction. + * + * @see jakarta.transaction.Transaction#enlistResource(XAResource) + */ private boolean enlisted; + + /** + * The busy state reflects the moment the resource is taken from the connection pool. It should be set to {@code true} + * once it is taken from the pool, and it should be set to {@code false} once it is returned to the pool. + *

+ * Setting to {@code true} happens at the moment the resource is taken from the pool (getResourceFromPool).
+ * Setting to {@code false} happens at the moment the connection is returned to the pool (resourceClosed). + */ private boolean busy; + + /** + * Timestamp represents the time of resource creation, or the time the resource usage was complete and is handed back to + * the pool. The timestamp value is used in the remove idle and invalid resources logic of the resource pool resizer. + */ private long timestamp; public boolean isEnlisted() { @@ -58,8 +77,17 @@ public ResourceState() { touchTimestamp(); } + /** + * Resets all fields, representing the constructor call situation. + */ + public void reset() { + touchTimestamp(); + setBusy(false); + setEnlisted(false); + } + @Override public String toString() { - return "Enlisted :" + enlisted + " Busy :" + busy; + return ""; } } diff --git a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/allocator/AbstractConnectorAllocator.java b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/allocator/AbstractConnectorAllocator.java index b88dae71b1b..2519da4160f 100644 --- a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/allocator/AbstractConnectorAllocator.java +++ b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/allocator/AbstractConnectorAllocator.java @@ -93,7 +93,7 @@ public Set getInvalidConnections(Set connectionSet) throws ResourceException { @Override public boolean isConnectionValid(ResourceHandle h) { HashSet conn = new HashSet<>(); - conn.add((ManagedConnection) h.getResource()); + conn.add(h.getResource()); Set invalids = null; try { invalids = getInvalidConnections(conn); @@ -135,7 +135,7 @@ public boolean isTransactional() { @Override public void cleanup(ResourceHandle h) throws PoolingException { try { - ManagedConnection mc = (ManagedConnection) h.getResource(); + ManagedConnection mc = h.getResource(); mc.cleanup(); } catch (Exception ex) { _logger.log(Level.WARNING, "managed_con.cleanup-failed", ex); @@ -146,7 +146,7 @@ public void cleanup(ResourceHandle h) throws PoolingException { @Override public boolean matchConnection(ResourceHandle h) { Set set = new HashSet<>(); - set.add((ManagedConnection) h.getResource()); + set.add(h.getResource()); try { ManagedConnection mc = mcf.matchManagedConnections(set, subject, reqInfo); return mc != null; @@ -157,9 +157,8 @@ public boolean matchConnection(ResourceHandle h) { @Override public void closeUserConnection(ResourceHandle resource) throws PoolingException { - try { - ManagedConnection mc = (ManagedConnection) resource.getResource(); + ManagedConnection mc = resource.getResource(); mc.cleanup(); } catch (ResourceException ex) { throw new PoolingException(ex); @@ -177,8 +176,8 @@ public Object getSharedConnection(ResourceHandle h) throw new UnsupportedOperationException(); } - protected ResourceHandle createResourceHandle(Object resource, ResourceSpec spec, - ResourceAllocator alloc, ClientSecurityInfo info) { + protected ResourceHandle createResourceHandle(ManagedConnection resource, ResourceSpec spec, + ResourceAllocator alloc) { ConnectorConstants.PoolType pt = ConnectorConstants.PoolType.STANDARD_POOL; try { @@ -187,9 +186,9 @@ protected ResourceHandle createResourceHandle(Object resource, ResourceSpec spec _logger.log(Level.WARNING,"unable_to_determine_pool_type", spec.getPoolInfo()); } if (pt == ConnectorConstants.PoolType.ASSOCIATE_WITH_THREAD_POOL) { - return new AssocWithThreadResourceHandle(resource, spec, alloc, info); + return new AssocWithThreadResourceHandle(resource, spec, alloc); } else { - return new ResourceHandle(resource, spec, alloc, info); + return new ResourceHandle(resource, spec, alloc); } } diff --git a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/allocator/ConnectorAllocator.java b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/allocator/ConnectorAllocator.java index bef99a9e274..446142095b0 100644 --- a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/allocator/ConnectorAllocator.java +++ b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/allocator/ConnectorAllocator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023 Contributors to the Eclipse Foundation + * Copyright (c) 2022, 2024 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 @@ -34,8 +34,12 @@ import javax.security.auth.Subject; import javax.transaction.xa.XAResource; - /** + * XATransaction Connector Allocator, used for transaction level: + * {@link com.sun.appserv.connectors.internal.api.ConnectorConstants#XA_TRANSACTION_INT}. + *

+ * TODO: Rename class to reflect XATransaction + * * @author Tony Ng */ public class ConnectorAllocator extends AbstractConnectorAllocator { @@ -143,7 +147,7 @@ public ConnectorAllocator(PoolManager poolMgr, public ResourceHandle createResource() throws PoolingException { try { ManagedConnection mc = mcf.createManagedConnection(subject, reqInfo); - ResourceHandle resource = createResourceHandle(mc, spec, this, info); + ResourceHandle resource = createResourceHandle(mc, spec, this); ConnectionEventListener l = new ConnectionListenerImpl(resource); mc.addConnectionEventListener(l); return resource; @@ -156,7 +160,7 @@ public ResourceHandle createResource() throws PoolingException { public void fillInResourceObjects(ResourceHandle resource) throws PoolingException { try { - ManagedConnection mc = (ManagedConnection) resource.getResource(); + ManagedConnection mc = resource.getResource(); Object con = mc.getConnection(subject, reqInfo); resource.incrementCount(); XAResource xares = mc.getXAResource(); @@ -177,7 +181,7 @@ public void destroyResource(ResourceHandle resource) } try { - ManagedConnection mc = (ManagedConnection) resource.getResource(); + ManagedConnection mc = resource.getResource(); mc.destroy(); } catch (Exception ex) { throw new PoolingException(ex); diff --git a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/allocator/LocalTxConnectorAllocator.java b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/allocator/LocalTxConnectorAllocator.java index be7220a682c..ad47a63b9cc 100644 --- a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/allocator/LocalTxConnectorAllocator.java +++ b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/allocator/LocalTxConnectorAllocator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023 Contributors to the Eclipse Foundation + * Copyright (c) 2021, 2024 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 @@ -41,6 +41,9 @@ import javax.transaction.xa.XAResource; /** + * LocalTransaction Connector Allocator, used for transaction level: + * {@link com.sun.appserv.connectors.internal.api.ConnectorConstants#LOCAL_TRANSACTION_INT}. + * * @author Tony Ng */ public class LocalTxConnectorAllocator extends AbstractConnectorAllocator { @@ -70,7 +73,7 @@ public ResourceHandle createResource() throws PoolingException { try { ManagedConnection mc = mcf.createManagedConnection(subject, reqInfo); - ResourceHandle resource = createResourceHandle(mc, spec, this, info); + ResourceHandle resource = createResourceHandle(mc, spec, this); ConnectionEventListener l = new LocalTxConnectionEventListener(resource); mc.addConnectionEventListener(l); resource.setListener(l); @@ -88,10 +91,8 @@ public ResourceHandle createResource() throws PoolingException { public void fillInResourceObjects(ResourceHandle resource) throws PoolingException { try { - ManagedConnection mc = (ManagedConnection) resource.getResource(); - + ManagedConnection mc = resource.getResource(); Object con = mc.getConnection(subject, reqInfo); - ConnectorXAResource xares = (ConnectorXAResource) resource.getXAResource(); xares.setUserHandle(con); resource.fillInResourceObjects(con, xares); @@ -104,7 +105,7 @@ public void fillInResourceObjects(ResourceHandle resource) public void destroyResource(ResourceHandle resource) throws PoolingException { try { - ManagedConnection mc = (ManagedConnection) resource.getResource(); + ManagedConnection mc = resource.getResource(); XAResource xares = resource.getXAResource(); forceTransactionCompletion(xares); mc.destroy(); diff --git a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/allocator/NoTxConnectorAllocator.java b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/allocator/NoTxConnectorAllocator.java index 8dfd7600a46..c6de51b6d3b 100644 --- a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/allocator/NoTxConnectorAllocator.java +++ b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/allocator/NoTxConnectorAllocator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023 Contributors to the Eclipse Foundation + * Copyright (c) 2022, 2024 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 @@ -34,6 +34,9 @@ import javax.security.auth.Subject; /** + * NoTransaction Connector Allocator, used for transaction level: + * {@link com.sun.appserv.connectors.internal.api.ConnectorConstants#NO_TRANSACTION_INT}. + * * @author Tony Ng */ public class NoTxConnectorAllocator extends AbstractConnectorAllocator { @@ -104,12 +107,11 @@ public NoTxConnectorAllocator(PoolManager poolMgr, super(poolMgr, mcf, spec, subject, reqInfo, info, desc); } - @Override public ResourceHandle createResource() throws PoolingException { try { ManagedConnection mc = mcf.createManagedConnection(subject, reqInfo); - ResourceHandle resource = createResourceHandle(mc, spec, this, info); + ResourceHandle resource = createResourceHandle(mc, spec, this); ConnectionEventListener l = new ConnectionListenerImpl(resource); mc.addConnectionEventListener(l); return resource; @@ -119,10 +121,9 @@ public ResourceHandle createResource() throws PoolingException { } @Override - public void fillInResourceObjects(ResourceHandle resource) - throws PoolingException { + public void fillInResourceObjects(ResourceHandle resource) throws PoolingException { try { - ManagedConnection mc = (ManagedConnection) resource.getResource(); + ManagedConnection mc = resource.getResource(); Object con = mc.getConnection(subject, reqInfo); resource.fillInResourceObjects(con, null); } catch (ResourceException ex) { @@ -131,11 +132,9 @@ public void fillInResourceObjects(ResourceHandle resource) } @Override - public void destroyResource(ResourceHandle resource) - throws PoolingException { - + public void destroyResource(ResourceHandle resource) throws PoolingException { try { - ManagedConnection mc = (ManagedConnection) resource.getResource(); + ManagedConnection mc = resource.getResource(); mc.destroy(); } catch (Exception ex) { throw new PoolingException(ex); diff --git a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/allocator/ResourceAllocator.java b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/allocator/ResourceAllocator.java index 015c09dab2a..774c48f51d2 100644 --- a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/allocator/ResourceAllocator.java +++ b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/allocator/ResourceAllocator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Contributors to the Eclipse Foundation + * Copyright (c) 2022, 2024 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 @@ -38,10 +38,28 @@ public interface ResourceAllocator { void destroyResource(ResourceHandle resource) throws PoolingException; - boolean matchConnection(ResourceHandle h); + /** + * Represents the "match-connections" configuration value of a connection pool.
+ * If true, enables connection matching. You can set to false if connections are homogeneous. + *

+ * Jakarta documentation: + * {@link jakarta.resource.spi.ManagedConnectionFactory#matchManagedConnections(Set, Subject, ConnectionRequestInfo)} + * mentions: "criteria used for matching is specific to a resource adapter and is not prescribed by the Connector + * specification." + * + * @param handle The resource handle to be matched. + * @return True if matching applies to this handle, otherwise false + */ + boolean matchConnection(ResourceHandle handle); boolean isTransactional(); + /** + * Forces the cleanup of the ManagedConnection associated to the given resource. + * + * @param resource the resource referencing a ManagedConnection + * @throws PoolingException in case the cleanup failed + */ void cleanup(ResourceHandle resource) throws PoolingException; boolean shareableWithinComponent(); diff --git a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/listener/LocalTxConnectionEventListener.java b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/listener/LocalTxConnectionEventListener.java index 36c03a57491..a68fa6fa729 100644 --- a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/listener/LocalTxConnectionEventListener.java +++ b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/listener/LocalTxConnectionEventListener.java @@ -20,18 +20,23 @@ import com.sun.enterprise.connectors.ConnectorRuntime; import com.sun.enterprise.resource.ResourceHandle; import com.sun.enterprise.resource.pool.PoolManager; +import com.sun.logging.LogDomains; import jakarta.resource.spi.ConnectionEvent; import jakarta.resource.spi.ManagedConnection; import java.util.IdentityHashMap; import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; /** * @author Binod P.G */ public class LocalTxConnectionEventListener extends ConnectionEventListener { + private static final Logger logger = LogDomains.getLogger(ResourceHandle.class, LogDomains.RSR_LOGGER); + /** * A shortcut to the singleton PoolManager instance. Field could also be removed. */ @@ -56,14 +61,33 @@ public class LocalTxConnectionEventListener extends ConnectionEventListener { public LocalTxConnectionEventListener(ResourceHandle resource) { this.resource = resource; + + if (logger.isLoggable(Level.FINE)) { + logger.log(Level.FINE, "LocalTxConnectionEventListener constructor, resource=" + resource + ", this=" + this); + } } @Override public synchronized void connectionClosed(ConnectionEvent evt) { + if (logger.isLoggable(Level.FINE)) { + logger.log(Level.FINE, "LocalTxConnectionEventListener.connectionClosed START, resource=" + resource + ", this=" + this); + for (Object key : associatedHandles.keySet()) { + ResourceHandle associatedHandle = associatedHandles.get(key); + logger.log(Level.FINE, + "LocalTxConnectionEventListener.connectionClosed associatedHandles: key=" + key + ", handle=" + associatedHandle); + logger.log(Level.FINE, + "LocalTxConnectionEventListener.connectionClosed associatedHandles: resource=" + associatedHandle.getResource()); + } + } + Object connectionHandle = evt.getConnectionHandle(); ResourceHandle handle = associatedHandles.getOrDefault(connectionHandle, resource); // ManagedConnection instance is still valid and put back in the pool: do not remove the event listener. poolManager.resourceClosed(handle); + + if (logger.isLoggable(Level.FINE)) { + logger.log(Level.FINE, "LocalTxConnectionEventListener.connectionClosed END, resource=" + resource + ", handle=" + handle + ", this=" + this); + } } @Override @@ -117,6 +141,9 @@ public void localTransactionRolledback(ConnectionEvent evt) { * @param resourceHandle the original Handle */ public synchronized void associateHandle(Object userHandle, ResourceHandle resourceHandle) { + if (logger.isLoggable(Level.FINE)) { + logger.log(Level.FINE, "LocalTxConnectionEventListener associateHandle, userHandle=" + userHandle + ", resourceHandle=" + resourceHandle + ", this=" + this); + } associatedHandles.put(userHandle, resourceHandle); } @@ -128,6 +155,9 @@ public synchronized void associateHandle(Object userHandle, ResourceHandle resou * can also indicate that the map previously associated null with userHandle. */ public synchronized ResourceHandle removeAssociation(Object userHandle) { + if (logger.isLoggable(Level.FINE)) { + logger.log(Level.FINE, "LocalTxConnectionEventListener removeAssociation, userHandle=" + userHandle + ", this=" + this); + } return associatedHandles.remove(userHandle); } @@ -136,6 +166,8 @@ public synchronized ResourceHandle removeAssociation(Object userHandle) { * @return The clone of the associatedHandles map. */ public synchronized Map getAssociatedHandlesAndClearMap() { + logger.log(Level.FINE, "LocalTxConnectionEventListener getAssociatedHandlesAndClearMap, this=" + this); + // Clone the associatedHandles, because we will clear the list in this method IdentityHashMap result = (IdentityHashMap) associatedHandles.clone(); diff --git a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/AbstractPoolManager.java b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/AbstractPoolManager.java index 151df17f5bf..9171c6664c5 100644 --- a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/AbstractPoolManager.java +++ b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/AbstractPoolManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Contributors to the Eclipse Foundation + * Copyright (c) 2022, 2024 Contributors to the Eclipse Foundation * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the @@ -17,33 +17,31 @@ package com.sun.enterprise.resource.pool; -import com.sun.enterprise.resource.ResourceSpec; - -import java.util.concurrent.ConcurrentHashMap; - -import org.glassfish.resourcebase.resources.api.PoolInfo; - /** * Abstract Pool manager for unimplemented features. Throws UnsupportedOperationException when invoked. */ public abstract class AbstractPoolManager implements PoolManager { - @Override - public void emptyResourcePool(ResourceSpec spec) { - throw new UnsupportedOperationException(); - } - - @Override - public void killAllPools() { - throw new UnsupportedOperationException(); - } - - @Override - public void setSelfManaged(PoolInfo poolInfo, boolean flag) { - throw new UnsupportedOperationException(); - } - - public ConcurrentHashMap getMonitoredPoolTable() { - throw new UnsupportedOperationException(); - } +// Not used in the code +// @Override +// public void emptyResourcePool(ResourceSpec spec) { +// throw new UnsupportedOperationException(); +// } + +// Not used in the code +// @Override +// public void killAllPools() { +// throw new UnsupportedOperationException(); +// } + +// Not used in the code +// @Override +// public void setSelfManaged(PoolInfo poolInfo, boolean flag) { +// throw new UnsupportedOperationException(); +// } + +// Not used in the code +// public ConcurrentHashMap getMonitoredPoolTable() { +// throw new UnsupportedOperationException(); +// } } diff --git a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/AssocWithThreadResourcePool.java b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/AssocWithThreadResourcePool.java index 3fd9d5265bf..26c30c9c1b9 100644 --- a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/AssocWithThreadResourcePool.java +++ b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/AssocWithThreadResourcePool.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Contributors to the Eclipse Foundation + * Copyright (c) 2022, 2024 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 @@ -49,7 +49,7 @@ public AssocWithThreadResourcePool(PoolInfo poolInfo, Hashtable env) throws Pool @Override protected void initializePoolDataStructure() throws PoolingException { dataStructure = DataStructureFactory.getDataStructure("com.sun.enterprise.resource.pool.datastructure.ListDataStructure", - dataStructureParameters, maxPoolSize, this, resourceSelectionStrategyClass); + dataStructureParameters, maxPoolSize, this); } /** diff --git a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/ConnectionPool.java b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/ConnectionPool.java index 6a99a0f12cb..4e6ce188fc6 100644 --- a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/ConnectionPool.java +++ b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/ConnectionPool.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024 Contributors to the Eclipse Foundation + * Copyright (c) 2022, 2024 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 @@ -123,15 +123,14 @@ public class ConnectionPool implements ResourcePool, ConnectionLeakListener, Res * Default: true */ protected boolean matchConnections; + /** * Represents the "is-connection-validation-required" configuration value.
* Specifies whether connections have to be validated before being given to the application. If a resource’s validation * fails, it is destroyed, and a new resource is created and returned.
* Default: false - * - * TODO: rename to 'connectionValidationRequired' */ - protected boolean validation; + protected boolean connectionValidationRequired; /** * Represents the "prefer-validate-over-recreate property" configuration value.
@@ -142,11 +141,20 @@ public class ConnectionPool implements ResourcePool, ConnectionLeakListener, Res */ protected boolean preferValidateOverRecreate; - // hold on to the resizer task so we can cancel/reschedule it. + /** + * True if the pool is initialized + */ + protected volatile boolean poolInitialized; + + /** + * Reference to a resizer task to be able to cancel/reschedule it. + */ protected Resizer resizerTask; - protected volatile boolean poolInitialized; - protected Timer timer; + /** + * Timer for the resizerTask. Runs every {@link #idletime} milliseconds. + */ + protected Timer resizerTaskTimer; // advanced pool config properties /** @@ -173,6 +181,7 @@ public class ConnectionPool implements ResourcePool, ConnectionLeakListener, Res * Represents the "validate-atmost-once-period-in-seconds" configuration, but in millis.
* Specifies the time interval within which a connection is validated at most once. Minimizes the number of validation * calls. A value of zero allows unlimited validation calls.
+ * This value is used when {@link #connectionValidationRequired} is set to true.
* Default: 10.000ms. */ protected long validateAtmostPeriodInMilliSeconds_; @@ -185,21 +194,16 @@ public class ConnectionPool implements ResourcePool, ConnectionLeakListener, Res */ protected int maxConnectionUsage_; - // To validate a Sun RA Pool Connection if it has not been validated - // in the past x sec. (x=idle-timeout) - // The property will be set from system property - - // com.sun.enterprise.connectors.ValidateAtmostEveryIdleSecs=true /** - * Represents the "validate-atmost-once-period-in-seconds" configuration.
- * Specifies the time interval within which a connection is validated at most once. Minimizes the number of validation - * calls. A value of zero allows unlimited validation calls.
- * Default: 0 + * To validate a Sun RA Pool Connection if it has not been validated in the past x sec. (x=idle-timeout). The property + * will be set from system property: com.sun.enterprise.connectors.ValidateAtmostEveryIdleSecs=true
+ * Default: false */ private boolean validateAtmostEveryIdleSecs; - // TODO document - protected String resourceSelectionStrategyClass; - + /** + * Listener for pool monitoring statistics. + */ protected PoolLifeCycleListener poolLifeCycleListener; // Gateway used to control the concurrency within the round-trip of resource access. @@ -208,7 +212,11 @@ public class ConnectionPool implements ResourcePool, ConnectionLeakListener, Res protected ConnectionLeakDetector leakDetector; + /** + * The datastructure containing the pooled resources. + */ protected DataStructure dataStructure; + protected String dataStructureType; protected String dataStructureParameters; @@ -224,8 +232,15 @@ public class ConnectionPool implements ResourcePool, ConnectionLeakListener, Res // NOTE: This resource allocator may not be the same as the allocator passed in to getResource() protected ResourceAllocator allocator; + // TODO: selfManaged_ does not seem to be set to true in glassfish code. Remove it from ResourcePool interface. + // Glassfish 2.1 had a management-rule / self-management part, but it no longer exists in domain.xml. + // See for example package "selfmanagement" and logger "SELF_MANAGEMENT_LOGGER" in the codeline. private boolean selfManaged_; + /** + * Blocked state is true during connection pool recreation. Once blocked is set to true only resources from the pool can + * be reused which are already part of the current transaction. + */ private boolean blocked; /** @@ -257,8 +272,7 @@ protected void initializePoolDataStructure() throws PoolingException { dataStructure = DataStructureFactory.getDataStructure( dataStructureType, - dataStructureParameters, maxPoolSize, this, - resourceSelectionStrategyClass); + dataStructureParameters, maxPoolSize, this); } protected void initializeResourceSelectionStrategy() { @@ -285,13 +299,12 @@ private void setPoolConfiguration(Hashtable env) throws PoolingException { failAllConnections = poolResource.isFailAllConnections(); - validation = poolResource.isIsConnectionValidationRequired(); + connectionValidationRequired = poolResource.isIsConnectionValidationRequired(); validateAtmostEveryIdleSecs = poolResource.isValidateAtmostEveryIdleSecs(); dataStructureType = poolResource.getPoolDataStructureType(); dataStructureParameters = poolResource.getDataStructureParameters(); poolWaitQueueClass = poolResource.getPoolWaitQueue(); - resourceSelectionStrategyClass = poolResource.getResourceSelectionStrategyClass(); resourceGatewayClass = poolResource.getResourceGatewayClass(); reconfigWaitTime = poolResource.getDynamicReconfigWaitTimeout(); @@ -340,9 +353,11 @@ protected synchronized void initPool(ResourceAllocator allocator) throws Pooling } /** - * Schedules the resizer timer task. If a task is currently scheduled, it would be canceled and a new one is scheduled. + * Schedules the resizer timer task. If a task is currently scheduled, it would be cancelled and a new one is scheduled. + *

+ * package protected for unit tests */ - private void scheduleResizerTask() { + protected void scheduleResizerTask() { if (resizerTask != null) { // cancel the current task resizerTask.cancel(); @@ -351,11 +366,11 @@ private void scheduleResizerTask() { resizerTask = initializeResizer(); - if (timer == null) { - timer = ConnectorRuntime.getRuntime().getTimer(); + if (resizerTaskTimer == null) { + resizerTaskTimer = ConnectorRuntime.getRuntime().getTimer(); } - timer.scheduleAtFixedRate(resizerTask, idletime, idletime); + resizerTaskTimer.scheduleAtFixedRate(resizerTask, idletime, idletime); LOG.log(FINE, "Scheduled resizer task with the idle time {0} ms", idletime); } @@ -364,12 +379,12 @@ protected Resizer initializeResizer() { } /** - * add a resource with status busy and not enlisted + * Add a resource to the pool with status busy and not enlisted. * - * @param alloc ResourceAllocator + * @param alloc the ResourceAllocator to be used * @throws PoolingException when unable to add a resource */ - public void addResource(ResourceAllocator alloc) throws PoolingException { + private void addResource(ResourceAllocator alloc) throws PoolingException { int numResCreated = dataStructure.addResource(alloc, 1); if (numResCreated > 0) { for (int i = 0; i < numResCreated; i++) { @@ -383,7 +398,7 @@ public void addResource(ResourceAllocator alloc) throws PoolingException { } /** - * marks resource as free. This method should be used instead of directly calling + * Marks resource as free. This method should be used instead of directly calling * resoureHandle.getResourceState().setBusy(false) OR getResourceState(resourceHandle).setBusy(false) as this method * handles stopping of connection leak tracing If connection leak tracing is enabled, takes care of stopping connection * leak tracing @@ -396,7 +411,7 @@ protected void setResourceStateToFree(ResourceHandle resourceHandle) { } /** - * marks resource as busy. This method should be used instead of directly calling + * Marks resource as busy. This method should be used instead of directly calling * resoureHandle.getResourceState().setBusy(true) OR getResourceState(resourceHandle).setBusy(true) as this method * handles starting of connection leak tracing If connection leak tracing is enabled, takes care of starting connection * leak tracing @@ -409,10 +424,10 @@ protected void setResourceStateToBusy(ResourceHandle resourceHandle) { } /** - * returns resource from the pool. + * Returns a resource from the connection pool. * * @return a free pooled resource object matching the ResourceSpec - * @throws PoolingException - if any error occurrs - or the pool has reached its max size and the + * @throws PoolingException - if any error occurs - or the pool has reached its max size and the * max-connection-wait-time-in-millis has expired. */ @Override @@ -553,7 +568,7 @@ public ResourceHandle getResource(ResourceSpec spec, ResourceAllocator alloc, Tr * Overridden in AssocWithThreadResourcePool to fetch the resource cached in the ThreadLocal In ConnectionPool this * simply returns null. * - * @param spec ResourceSpec + * @param spec the ResourceSpec used to locate the correct resource pool * @param alloc ResourceAllocator to create a resource * @param tran Transaction * @return ResourceHandle resource from ThreadLocal @@ -598,7 +613,7 @@ protected ResourceHandle internalGetResource(ResourceSpec resourceSpec, Resource * * @param transaction Current Transaction * @param resourceAllocator ResourceAllocator - * @param resourceSpec ResourceSpec + * @param resourceSpec the ResourceSpec used to locate the correct resource pool * @return result ResourceHandle */ private ResourceHandle getResourceFromTransaction(Transaction transaction, ResourceAllocator resourceAllocator, ResourceSpec resourceSpec) { @@ -662,6 +677,11 @@ private ResourceHandle getResourceFromTransaction(Transaction transaction, Resou } } + // TODO: This 'if (state.isFree())' logic suggests the state can already be Busy. + // Document why it can already be busy. + // Is this because you can have a transaction within a transaction and reuse the same resource? + // But in that case: shouldn't the state be already set to 'busy' by the previous code + // in the same thread and still remain busy? if (state.isFree()) { setResourceStateToBusy(resourceHandle); } @@ -686,7 +706,7 @@ private ResourceHandle getResourceFromTransaction(Transaction transaction, Resou /** * To provide an unenlisted, valid, matched resource from pool. * - * @param resourceSpec ResourceSpec + * @param resourceSpec the ResourceSpec used to locate the correct resource pool * @param resourceAllocator ResourceAllocator * @param transaction Transaction * @return ResourceHandle resource from pool @@ -706,11 +726,11 @@ protected ResourceHandle getUnenlistedResource(ResourceSpec resourceSpec, Resour protected boolean isConnectionValid(ResourceHandle resourceHandle, ResourceAllocator resourceAllocator) { boolean connectionValid = true; - if (validation || validateAtmostEveryIdleSecs) { + if (connectionValidationRequired || validateAtmostEveryIdleSecs) { long validationPeriod; // validation period is idle timeout if validateAtmostEveryIdleSecs is set to true // else it is validateAtmostPeriodInMilliSeconds_ - if (validation) { + if (connectionValidationRequired) { validationPeriod = validateAtmostPeriodInMilliSeconds_; } else { validationPeriod = idletime; @@ -747,6 +767,12 @@ protected boolean isConnectionValid(ResourceHandle resourceHandle, ResourceAlloc * @return boolean representing the match status of the connection */ protected boolean matchConnection(ResourceHandle resource, ResourceAllocator alloc) { + // TODO: Explain what matching is in detail. + // TODO: Explain that if matchConnections is disabled in the connectionpool why 'true' is still returned and not false?! + // Old documentation mentions: "match-connections / default: true / If true, enables connection matching. You + // can set to false if connections are homogeneous." Jakarta documentation: + // jakarta.resource.spi.ManagedConnectionFactory.matchManagedConnections(Set, Subject, ConnectionRequestInfo) mentions: + // "criteria used for matching is specific to a resource adapter and is not prescribed by the Connector specification." boolean matched = true; if (matchConnections) { matched = alloc.matchConnection(resource); @@ -767,6 +793,7 @@ protected boolean matchConnection(ResourceHandle resource, ResourceAllocator all * return a new resource. returns null if the pool new resources cannot be created.
* * @param resourceAllocator ResourceAllocator + * @param resourceSpec the ResourceSpec used to locate the correct resource pool * @return ResourceHandle resource from pool * @throws PoolingException if unable to create a new resource */ @@ -823,9 +850,10 @@ protected ResourceHandle getResourceFromPool(ResourceAllocator resourceAllocator } if (resourceFromPool != null) { - // Set correct state + // Set state to Busy setResourceStateToBusy(resourceFromPool); } else { + // Set state to Busy via resizePoolAndGetNewResource call resourceFromPool = resizePoolAndGetNewResource(resourceAllocator); } } finally { @@ -893,7 +921,10 @@ private ResourceHandle getMatchedResourceFromPool(ResourceAllocator alloc) { while ((handle = dataStructure.getResource()) != null) { if (matchConnection(handle, alloc)) { matchedResourceFromPool = handle; + // TODO: ensure the state is not already isBusy here setResourceStateToBusy(matchedResourceFromPool); + + // Break from the while loop and do not add the handle to the activeResources list. break; } activeResources.add(handle); @@ -903,6 +934,7 @@ private ResourceHandle getMatchedResourceFromPool(ResourceAllocator alloc) { for (ResourceHandle activeResource : activeResources) { dataStructure.returnResource(activeResource); } + // No need to clear the list, clear() call is probably here to try to help the garbage collector. activeResources.clear(); } @@ -910,7 +942,7 @@ private ResourceHandle getMatchedResourceFromPool(ResourceAllocator alloc) { } /** - * Try to purge resources by size <= quantity
+ * Try to purge resources by size <= quantity * * @param quantity maximum no. of resources to remove.
* @return resourceCount No. of resources actually removed.
@@ -939,14 +971,16 @@ private int purgeResources(int quantity) { * replacement is required since the steadypoolsize might equal maxpoolsize and in that case if we were not to remove a * resource from the pool, our resource would be above maxPoolSize * - * @param resourceAllocator ResourceAllocator to create resource - * @param resourceSpec ResourceSpec + * @param resourceAllocator the resource allocator to be used to create the new resource + * @param resourceSpec the ResourceSpec used to locate the correct resource pool * @return newly created resource * @throws PoolingException when unable to create a resource */ protected ResourceHandle createSingleResourceAndAdjustPool(ResourceAllocator resourceAllocator, ResourceSpec resourceSpec) throws PoolingException { + // TODO document in getResource when a return value can be null ResourceHandle handle = dataStructure.getResource(); if (handle != null) { + // TODO document why it is removed / add unit test to show the behavior described in the javadoc of the method dataStructure.removeResource(handle); } @@ -955,9 +989,9 @@ protected ResourceHandle createSingleResourceAndAdjustPool(ResourceAllocator res /** * Method to be used to create resource, instead of calling ResourceAllocator.createConfigBean(). This method handles - * the connection creation retrial in case of failure + * the connection creation retry in case of failure. * - * @param resourceAllocator ResourceAllocator + * @param resourceAllocator the resource allocator to be used to create the new resource * @return ResourceHandle newly created resource * @throws PoolingException when unable create a resource */ @@ -972,7 +1006,7 @@ protected ResourceHandle createSingleResource(ResourceAllocator resourceAllocato LOG.log(FINE, () -> "Time taken to create a single resource: " + resourceHandle.getResourceSpec().getResourceId() + " and adding to the pool: " + (now - startTime) + " ms."); - if (validation || validateAtmostEveryIdleSecs) { + if (connectionValidationRequired || validateAtmostEveryIdleSecs) { resourceHandle.setLastValidated(now); } return resourceHandle; @@ -992,9 +1026,9 @@ protected ResourceHandle createSingleResource(ResourceAllocator resourceAllocato } /** - * Create specified number of resources. + * Create specified number of resources using the given resource allocater. * - * @param alloc ResourceAllocator + * @param alloc the resource allocator to be used to create the new resource * @param size number of resources to create. * @throws PoolingException When unable to create a resource */ @@ -1045,9 +1079,6 @@ public void deleteResource(ResourceHandle resourceHandle) { } } - /** - * this method is called to indicate that the resource is not used by a bean/application anymore - */ @Override public void resourceClosed(ResourceHandle handle) throws IllegalStateException { LOG.log(FINE, "Resource was closed, processing handle: {0}", handle); @@ -1068,6 +1099,8 @@ public void resourceClosed(ResourceHandle handle) throws IllegalStateException { if (state.isUnenlisted() || (poolTxHelper.isNonXAResource(handle) && poolTxHelper.isLocalTransactionInProgress() && poolTxHelper.isLocalResourceEligibleForReuse(handle))) { + // Note: the call to isLocalResourceEligibleForReuse can change the enlisted state of the + // handle to false if the resource is eligible for reuse. freeUnenlistedResource(handle); } @@ -1108,11 +1141,13 @@ protected void freeUnenlistedResource(ResourceHandle h) { } protected void freeResource(ResourceHandle resourceHandle) { + LOG.log(FINE, "freeResource handle: {0}", resourceHandle); try { getResourceFromPoolAndFreeResourceMethodsLock.lock(); if (cleanupResource(resourceHandle)) { // Only when resource handle usage count is more than maxConnUsage if (maxConnectionUsage_ > 0 && resourceHandle.getUsageCount() >= maxConnectionUsage_) { + // Remove the resource handle from the pool and update the monitoring data performMaxConnectionUsageOperation(resourceHandle); } else { // Put it back to the free collection. @@ -1130,19 +1165,24 @@ protected void freeResource(ResourceHandle resourceHandle) { } finally { getResourceFromPoolAndFreeResourceMethodsLock.unlock(); } - } - protected boolean cleanupResource(ResourceHandle handle) { + /** + * Asks the resource allocator to cleanup the resource + * + * @param resource the resource to be cleaned up + * @return true if cleanup was successful, otherwise false + */ + protected boolean cleanupResource(ResourceHandle resource) { boolean cleanupSuccessful = true; // cleanup resource try { - ResourceAllocator alloc = handle.getResourceAllocator(); - alloc.cleanup(handle); + ResourceAllocator alloc = resource.getResourceAllocator(); + alloc.cleanup(resource); } catch (PoolingException ex) { LOG.log(WARNING, "Cleanup of a resource from pool [" + poolInfo.getName() + "] failed.", ex); cleanupSuccessful = false; - resourceErrorOccurred(handle); + resourceErrorOccurred(resource); } return cleanupSuccessful; } @@ -1151,6 +1191,8 @@ protected boolean cleanupResource(ResourceHandle handle) { public void resourceErrorOccurred(ResourceHandle resourceHandle) throws IllegalStateException { LOG.log(FINE, "Resource error occured: {0}", resourceHandle); if (failAllConnections) { + // TODO: leakDetector is not updated and isBusy state of this resource is not updated correctly: possible bug. + // leakDetector should be updated in the doFailAllConnectionsProcessing method. The resource can be updated here. doFailAllConnectionsProcessing(); return; } @@ -1169,7 +1211,8 @@ public void resourceErrorOccurred(ResourceHandle resourceHandle) throws IllegalS throw new IllegalStateException(); } - // mark as not busy + // Mark as not busy. Even if it is removed from the Pool datastructure, + // it is good to clean it up, at least to clean up the leakDetector. setResourceStateToFree(resourceHandle); state.touchTimestamp(); @@ -1200,6 +1243,9 @@ private void doFailAllConnectionsProcessing() { } emptyPool(); + // TODO: leakDetector might have been used and it is not cleaned up. + // should call leakDetector.reset + try { createResources(allocator, steadyPoolSize); LOG.log(FINE, "Successfully created new resources."); @@ -1210,30 +1256,34 @@ private void doFailAllConnectionsProcessing() { scheduleResizerTask(); } - /** - * this method is called when a resource is enlisted in - * - * @param tran Transaction - * @param resource ResourceHandle - */ @Override public void resourceEnlisted(Transaction tran, ResourceHandle resource) throws IllegalStateException { poolTxHelper.resourceEnlisted(tran, resource); } /** - * this method is called when transaction tran is completed + * This method is called when transaction tran is completed. * * @param tran Transaction * @param status status of transaction */ @Override public void transactionCompleted(Transaction tran, int status) throws IllegalStateException { + // transactionCompleted will update all relevant resource handles to be no longer enlisted List delistedResources = poolTxHelper.transactionCompleted(tran, status, poolInfo); + for (ResourceHandle resource : delistedResources) { - // Application might not have closed the connection. + // Application might not have closed the connection, free the resource only if it is not in use anymore. if (isResourceUnused(resource)) { freeResource(resource); + // Resource is now returned to the pool and another thread can use it, cannot log it anymore. + } else { + // TODO: Why would the application not close a busy connection if the transaction completed and the resource handle is + // delisted from the transaction? Is this done to leave the resource as used in the connection pool and let it + // time out and be cleaned up by the timer? + // The poolTxHelper.transactionCompleted already altered the enlisted state to be no longer enlisted in any transaction. + // So this resource is no longer part of an outer transaction for example. + // Would expect a warning here in case the resource handle state still marked as busy. } } } @@ -1246,11 +1296,7 @@ protected boolean isResourceUnused(ResourceHandle h) { public ResourceHandle createResource(ResourceAllocator alloc) throws PoolingException { // NOTE : Pool should not call this method directly, it should be called only by pool-datastructure ResourceHandle result = createSingleResource(alloc); - - ResourceState state = new ResourceState(); - state.setBusy(false); - state.setEnlisted(false); - result.setResourceState(state); + result.getResourceState().reset(); if (poolLifeCycleListener != null) { poolLifeCycleListener.connectionCreated(); @@ -1305,6 +1351,7 @@ private void incrementNumConnFailedValidation() { } } + // TODO: this method name should be createAndGetNewResource, but it is only used once, to it could be removed private ResourceHandle getNewResource(ResourceAllocator alloc) throws PoolingException { addResource(alloc); return dataStructure.getResource(); @@ -1323,6 +1370,9 @@ public void emptyPool() { @Override public void emptyFreeConnectionsInPool() { LOG.log(FINE, "Emptying free connections in the pool {0}", poolInfo.getName()); + + // TODO this is not completely thread safe, between getResource and removeResource + // the dataStructure can be altered by other threads ResourceHandle h; while ((h = dataStructure.getResource()) != null) { dataStructure.removeResource(h); @@ -1342,7 +1392,7 @@ public String toString() { sb.append(" matching="); sb.append((matchConnections ? "on" : "off")); sb.append(" validation="); - sb.append((validation ? "on" : "off")); + sb.append((connectionValidationRequired ? "on" : "off")); return sb.toString(); } @@ -1367,11 +1417,6 @@ public long getReconfigWaitTime() { return reconfigWaitTime; } - /** - * Reinitialize connections established in the connection pool and bring the pool to steady pool size. - * - * @throws com.sun.appserv.connectors.internal.api.PoolingException - */ @Override public synchronized boolean flushConnectionPool() throws PoolingException { LOG.log(FINE, "Flushing Connection Pool {0}", poolInfo); @@ -1389,13 +1434,6 @@ public synchronized boolean flushConnectionPool() throws PoolingException { return true; } - /** - * Reconfigure the Pool's properties. The reconfigConnectorConnectionPool method in the ConnectorRuntime will use this - * method (through PoolManager) if it needs to just change pool properties and not recreate the pool - * - * @param poolResource - the ConnectorConnectionPool JavaBean that holds the new pool properties - * @throws PoolingException if the pool resizing fails - */ @Override public synchronized void reconfigurePool(ConnectorConnectionPool poolResource) throws PoolingException { int _idleTime = Integer.parseInt(poolResource.getIdleTimeoutInSeconds()) * 1000; @@ -1419,7 +1457,7 @@ public synchronized void reconfigurePool(ConnectorConnectionPool poolResource) t maxWaitTime = 0; } - validation = poolResource.isIsConnectionValidationRequired(); + connectionValidationRequired = poolResource.isIsConnectionValidationRequired(); failAllConnections = poolResource.isFailAllConnections(); setAdvancedPoolConfiguration(poolResource); @@ -1523,8 +1561,13 @@ private void killExtraResources(int numToKill) { scheduleResizerTask(); } - /* - * Increase the number of steady resources in the pool if we detect that the steadyPoolSize has been increased + /** + * Increase the number of steady resources in the pool if we detect that the steadyPoolSize has been increased.
+ * Note: if the newSteadyPoolSize is smaller than the current pool size, no changes are made directly. The resizer task + * is updated in all cases and will resize the pool in the future. + * + * @param newSteadyPoolSize The new steady pool size + * @throws PoolingException when unable to add new resources to the pool */ private void increaseSteadyPoolSize(int newSteadyPoolSize) throws PoolingException { cancelResizerTask(); @@ -1534,27 +1577,16 @@ private void increaseSteadyPoolSize(int newSteadyPoolSize) throws PoolingExcepti scheduleResizerTask(); } - /** - * @param alloc ResourceAllocator - * @throws PoolingException when unable to create a resource - */ + // TODO is this private method needed? Why not call addResource directly? private void createResourceAndAddToPool(ResourceAllocator alloc) throws PoolingException { addResource(alloc); } - /** - * Switch on matching of connections in the pool. - */ @Override public void switchOnMatching() { matchConnections = true; } - /** - * query the name of this pool. Required by monitoring - * - * @return the name of this pool - */ @Override public final PoolInfo getPoolInfo() { return poolInfo; @@ -1567,8 +1599,8 @@ public synchronized void cancelResizerTask() { resizerTask.cancel(); } resizerTask = null; - if (timer != null) { - timer.purge(); + if (resizerTaskTimer != null) { + resizerTaskTimer.purge(); } } @@ -1666,12 +1698,6 @@ public void reclaimConnection(ResourceHandle handle) { notifyWaitingThreads(); } - /** - * Get Connection Pool status by computing the free/used values of the connections in the pool. Computations are based - * on whether the pool is initialized or not when this method is invoked. - * - * @return PoolStatus object - */ @Override public PoolStatus getPoolStatus() { PoolStatus poolStatus = new PoolStatus(this.poolInfo); diff --git a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/PoolManager.java b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/PoolManager.java index ddd4c0fe7e1..0d8a1b194d1 100644 --- a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/PoolManager.java +++ b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/PoolManager.java @@ -46,62 +46,142 @@ public interface PoolManager extends TransactedPoolManager { // transaction support levels - int NO_TRANSACTION = 0; - int LOCAL_TRANSACTION = 1; - int XA_TRANSACTION = 2; + // int NO_TRANSACTION = 0; + // int LOCAL_TRANSACTION = 1; + // int XA_TRANSACTION = 2; // Authentication mechanism levels - int BASIC_PASSWORD = 0; - int KERBV5 = 1; + // int BASIC_PASSWORD = 0; + // int KERBV5 = 1; // Credential Interest levels - String PASSWORD_CREDENTIAL = "jakarta.resource.spi.security.PasswordCredential"; - String GENERIC_CREDENTIAL = "jakarta.resource.spi.security.GenericCredential"; + // String PASSWORD_CREDENTIAL = "jakarta.resource.spi.security.PasswordCredential"; + // String GENERIC_CREDENTIAL = "jakarta.resource.spi.security.GenericCredential"; /** * Flush Connection pool by reinitializing the connections established in the pool. * - * @param poolInfo - * @throws com.sun.appserv.connectors.internal.api.PoolingException + * @param poolInfo the pool identifier for which the status needs to be flushed. + * @throws com.sun.appserv.connectors.internal.api.PoolingException in case the given pool is not initialized. */ boolean flushConnectionPool(PoolInfo poolInfo) throws PoolingException; /** - * @return connection pool status. + * Returns the pool status for the pool identified by the given poolInfo. + * + * @param poolInfo the pool identifier for which the status needs to be returned. + * @return pool status information if the pool is found based on the provided poolInfo, otherwise null is returned. */ PoolStatus getPoolStatus(PoolInfo poolInfo); ResourceHandle getResourceFromPool(ResourceSpec spec, ResourceAllocator alloc, ClientSecurityInfo info, - Transaction transaction) throws PoolingException, RetryableUnavailableException; + Transaction transaction) throws PoolingException, RetryableUnavailableException; - void createEmptyConnectionPool(PoolInfo poolInfo, PoolType pt, Hashtable env) throws PoolingException; + /** + * Creates an empty connection pool with the given pool info and pool type. + * + * @param poolInfo the pool identifier of the new pool + * @param poolType the type of the connection pool + * @param env hashtable used to find the connection pool ConnectorConnectionPool information to set parameters like + * maximum number of connections + * @throws PoolingException when unable to create/initialize pool + */ + void createEmptyConnectionPool(PoolInfo poolInfo, PoolType poolType, Hashtable env) throws PoolingException; + /** + * Returns the resource back to the pool IF errorOccurred is false. If errorOccurred is true the resource is removed + * from the pool. + *

+ * Note: The resource object may not be used anymore by the calling code after this call. + * + * @param resourceHandle the resource handle. + * @param errorOccurred true if an error occurred and the resource should not be returned to the pool, otherwise use + * false + *

+ * TODO The method name is misleading
+ * TODO Why is NoTxConnectorAllocator the only one calling this interface method and the rest of the calls are using + * resourceClosed / resourceErrorOccurred and resourceAbortOccurred ? Is the difference between resourceClosed and + * putbackResourceToPool that resourceClosed informs the transaction and putbackResourceToPool does not? + */ void putbackResourceToPool(ResourceHandle resourceHandle, boolean errorOccurred); - void putbackBadResourceToPool(ResourceHandle resourceHandle); - + /** + * Notifies the pool the resource is not used by a bean/application anymore.
+ * The resource is returned to the pool and the state of the resource is no longer busy.
+ * The resource is NOT delisted / unenlisted from the current transaction. + *

+ * Note: The resource object may not be used anymore by the calling code after this call. + * + * @param resourceHandle the resource handle + * @param poolInfo the pool information of the pool where the resource is expected to be removed from. If the given pool + * info is not found no exception is thrown. + *

+ * TODO Why is this method public available and only used internal in PoolManagerImpl and externally in + * LazyEnlistableResourceManagerImpl. Why can't LazyEnlistableResourceManagerImpl just call resourceClosed? If poolInfo + * = resourceHandle.getResourceSpec().getPoolInfo() then LazyEnlistableResourceManagerImpl could just as well call + * resourceClosed and then this method can be removed from the interface. + */ void putbackDirectToPool(ResourceHandle resourceHandle, PoolInfo poolInfo); + /** + * Closes the resource handle and returns it back to the connection pool.
+ * The resource is returned to the pool and the state of the resource is no longer busy.
+ * The resource is also delisted / unenlisted from the current transaction. + *

+ * Note: The resource object may not be used anymore by the calling code after this call. + * + * @param resourceHandle the resource handle to be closed and returned to the pool. + */ void resourceClosed(ResourceHandle resourceHandle); + // TODO Why is this method public available? Why is it not called via resourceErrorOccurred or resourceAbortOccurred? void badResourceClosed(ResourceHandle resourceHandle); void resourceErrorOccurred(ResourceHandle resourceHandle); void resourceAbortOccurred(ResourceHandle resourceHandle); + /** + * Inform all the connection pools using this transaction that the transaction has completed. This method is called by + * the EJB Transaction Manager.
+ * All resource handles associated to the given transaction are delisted.
+ * All resource handles associated to the given transaction handed back to the connection pool. + * + * @param transaction the transaction that is completed + * @param status the status of the transaction + */ void transactionCompleted(Transaction transaction, int status); - void emptyResourcePool(ResourceSpec spec); + // Not used in the code + // void emptyResourcePool(ResourceSpec spec); + /** + * Kills the pool for the given PoolInfo.
+ * Note: if the pool is not found the method ends with success. + * + * @param poolInfo the pool identifier for which the pool needs to be killed. + */ void killPool(PoolInfo poolInfo); + /** + * Reconfigures the connection pool to apply the given properties.
+ * Note: if the pool is not found the method ends with success. + * + * @param ccp the object containing the new connection pool properties that will be applied to the existing pool + * @throws PoolingException if reconfiguration of the pool failed + */ void reconfigPoolProperties(ConnectorConnectionPool ccp) throws PoolingException; + /** + * Switch on matching in the pool. + * + * @param poolInfo the pool identifier for which the pool matching needs to be switched on. + */ boolean switchOnMatching(PoolInfo poolInfo); /** - * Obtain a transactional resource such as JDBC connection + * Obtain a transactional resource such as JDBC connection from the connection pool. The caller is blocked until a + * resource is acquired or the max-wait-time of the connection pool expires. * * @param spec Specification for the resource * @param alloc Allocator for the resource @@ -113,17 +193,39 @@ ResourceHandle getResourceFromPool(ResourceSpec spec, ResourceAllocator alloc, C ResourceReferenceDescriptor getResourceReference(SimpleJndiName jndiName, SimpleJndiName logicalName); - void killAllPools(); - + /** + * Kills all free connections in the registered connection pools. The pools are not killed. + */ void killFreeConnectionsInPools(); + /** + * Returns the ResourcePool for the given PoolInfo + * + * @param poolInfo the pool identifier + * @return the ResourcePool if found, otherwise null + */ ResourcePool getPool(PoolInfo poolInfo); - void setSelfManaged(PoolInfo poolInfo, boolean flag); - - void lazyEnlist(ManagedConnection mc) throws ResourceException; + /** + * This method gets called by the LazyEnlistableConnectionManagerImpl when a connection needs enlistment, i.e on use of + * a Statement etc. Based on the given managagedConneciton the transaction manager is found and used to find the + * transaction. If no transaction was found this method returns without an error. + * + * @param managedConnection the managedConnection that needs to be enlisted in the transaction + * @throws ResourceException in case enlistment to the transaction failed. + */ + void lazyEnlist(ManagedConnection managedConnection) throws ResourceException; + /** + * Registers a PoolLifeCycle listener in the pool manager. The listener is used to keep track of statistics of the pool + * life cycle: pool created and pool destroyed events. + * + * @param poolListener the listener instance + */ void registerPoolLifeCycleListener(PoolLifeCycle poolListener); + /** + * Unregisters the PoolLifeCycle listener in the pool manager. + */ void unregisterPoolLifeCycleListener(); } diff --git a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/PoolManagerImpl.java b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/PoolManagerImpl.java index c34a612fb00..6b7accd1239 100644 --- a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/PoolManagerImpl.java +++ b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/PoolManagerImpl.java @@ -120,26 +120,30 @@ public void createEmptyConnectionPool(PoolInfo poolInfo, PoolType pooltype, Hash /** * Create and initialize pool if not created already. + *

+ * package default for unit test * - * @param poolInfo Name of the pool to be created - * @param poolType - PoolType - * @return ResourcePool - newly created pool + * @param poolInfo pool identifier of the pool to be created + * @param poolType the type of pool to be created + * @param env the jndi information to find the ConnectorConnectionPool configuration used to configure the pool * @throws PoolingException when unable to create/initialize pool */ - private ResourcePool createAndInitPool(final PoolInfo poolInfo, PoolType poolType, Hashtable env) throws PoolingException { + void createAndInitPool(final PoolInfo poolInfo, PoolType poolType, Hashtable env) throws PoolingException { ResourcePool pool = getPool(poolInfo); if (pool == null) { pool = ResourcePoolFactoryImpl.newInstance(poolInfo, poolType, env); addPool(pool); LOG.log(INFO, "Created connection pool and added it to PoolManager: {0}", pool); } - return pool; } // invoked by DataSource objects to obtain a connection @Override public Object getResource(ResourceSpec resourceSpec, ResourceAllocator resourceAllocator, ClientSecurityInfo clientSecurityInfo) throws PoolingException, RetryableUnavailableException { - + if (LOG.isLoggable(Level.FINE)) { + LOG.log(Level.FINE, "PoolManagerImpl.getResource START, resourceSpec=" + resourceSpec + "\nresourceAllocator=" + + resourceAllocator + "\nclientSecurityInfo=" + clientSecurityInfo); + } Transaction transaction = null; boolean transactional = resourceAllocator.isTransactional(); @@ -148,7 +152,9 @@ public Object getResource(ResourceSpec resourceSpec, ResourceAllocator resourceA } ResourceHandle resourceHandle = getResourceFromPool(resourceSpec, resourceAllocator, clientSecurityInfo, transaction); - + if (LOG.isLoggable(Level.FINE)) { + LOG.log(Level.FINE, "PoolManagerImpl.getResource handle=" + resourceHandle + ", poolStatus" + getPoolStatus(resourceSpec.getPoolInfo())); + } if (!resourceHandle.supportsLazyAssociation()) { resourceSpec.setLazyAssociatable(false); } @@ -158,7 +164,7 @@ public Object getResource(ResourceSpec resourceSpec, ResourceAllocator resourceA // we need to associate a new connection with it try { Object connection = resourceSpec.getConnectionToAssociate(); - ManagedConnection managedConnection = (ManagedConnection) resourceHandle.getResource(); + ManagedConnection managedConnection = resourceHandle.getResource(); managedConnection.associateConnection(connection); } catch (ResourceException e) { putbackDirectToPool(resourceHandle, resourceSpec.getPoolInfo()); @@ -172,8 +178,16 @@ public Object getResource(ResourceSpec resourceSpec, ResourceAllocator resourceA resourceSpec.setLazyEnlistable(false); } + // Overwrite the resourceSpec value in the resourceHandle. + // TODO: explain why this is done and why resourceAllocator in the resourceHandle is not overwritten with this + // resourceAllocator. resourceHandle.setResourceSpec(resourceSpec); + if (LOG.isLoggable(Level.FINE)) { + LOG.log(Level.FINE, "PoolManagerImpl.getResource, transaction=" + transaction + " resourceHandle=" + resourceHandle + " isUnenlisted=" + + resourceHandle.getResourceState().isUnenlisted()); + } + try { if (resourceHandle.getResourceState().isUnenlisted()) { // The spec being used here is the spec with the updated lazy enlistment info. @@ -184,6 +198,10 @@ public Object getResource(ResourceSpec resourceSpec, ResourceAllocator resourceA // LazyEnlistableResourceManager getResourceManager(resourceSpec).enlistResource(resourceHandle); + } else { + if (LOG.isLoggable(Level.FINE)) { + LOG.log(Level.FINE, "PoolManagerImpl.getResource - DO NOT ENLIST because it is already Enlisted, resource=" + resourceHandle); + } } } catch (Exception e) { // In the rare cases where enlistResource throws exception, we should return the resource to the pool @@ -196,7 +214,11 @@ public Object getResource(ResourceSpec resourceSpec, ResourceAllocator resourceA throw new PoolingException(e); } - return resourceHandle.getUserConnection(); + Object result = resourceHandle.getUserConnection(); + if (LOG.isLoggable(Level.FINE)) { + LOG.log(Level.FINE, "PoolManagerImpl.getResource END, resourceHandle=" + resourceHandle); + } + return result; } @Override @@ -221,11 +243,6 @@ public ResourceHandle getResourceFromPool(ResourceSpec resourceSpec, ResourceAll return getPool(resourceSpec.getPoolInfo()).getResource(resourceSpec, resourceAllocator, transaction); } - /** - * Switch on matching in the pool. - * - * @param poolInfo Name of the pool - */ @Override public boolean switchOnMatching(PoolInfo poolInfo) { ResourcePool pool = getPool(poolInfo); @@ -238,7 +255,8 @@ public boolean switchOnMatching(PoolInfo poolInfo) { return true; } - private void addPool(ResourcePool pool) { + /* package protected for unit test */ + protected void addPool(ResourcePool pool) { LOG.log(FINE, "Adding pool {0} to pooltable", pool.getPoolInfo()); poolTable.put(pool.getPoolInfo(), pool); } @@ -271,7 +289,6 @@ private void addSyncListener(Transaction transaction) { } } - // called by EJB Transaction Manager @Override public void transactionCompleted(Transaction transaction, int status) throws IllegalStateException { Set pools = ((JavaEETransaction) transaction).getAllParticipatingPools(); @@ -308,10 +325,6 @@ public void resourceEnlisted(Transaction transaction, com.sun.appserv.connectors } } - /** - * This method gets called by the LazyEnlistableConnectionManagerImpl when a connection needs enlistment, i.e on use of - * a Statement etc. - */ @Override public void lazyEnlist(ManagedConnection mc) throws ResourceException { lazyEnlistableResourceManager.lazyEnlist(mc); @@ -348,8 +361,18 @@ public void unregisterResource(com.sun.appserv.connectors.internal.api.ResourceH @Override public void resourceClosed(ResourceHandle resource) { + if (LOG.isLoggable(Level.FINE)) { + LOG.log(Level.FINE, "PoolManagerImpl.resourceClosed START, resource=" + resource); + } + getResourceManager(resource.getResourceSpec()).delistResource(resource, TMSUCCESS); putbackResourceToPool(resource, false); + + if (LOG.isLoggable(Level.FINE)) { + // Warning resource state logged here might not be valid, could be part of another thread already + // but logging the resource id is useful. + LOG.log(Level.FINE, "PoolManagerImpl.resourceClosed END, resource=" + resource); + } } @Override @@ -360,23 +383,42 @@ public void badResourceClosed(ResourceHandle resource) { @Override public void resourceErrorOccurred(ResourceHandle resource) { + // TODO: Why is delistResource not called like done in resourceAbortOccurred and in resourceClosed? + // Shouldn't delistResource be called with TMFAIL ? + // Added example in unit test PoolManagerImplTest that shows the delist is not called. + + // TODO: Why is resourceHandle.setConnectionErrorOccurred() not performed? + // Added example in unit test PoolManagerImplTest that shows connection error is not updated + putbackResourceToPool(resource, true); } @Override public void resourceAbortOccurred(ResourceHandle resource) { + // TODO: Why is TMSUCCESS used and not TMFAIL? Document the meaning of Abort. + //

+ // com.sun.gjc.spi.ManagedConnectionImpl.transactionCompleted() + // performs: badConnectionEventListener.connectionAbortOccurred -> jdbc.markedForRemoval_conAborted + //

+ // com.sun.gjc.spi.jdbc40.ConnectionHolder40.abort(Executor) documents: "Abort operation to mark the connection + // internally as a bad connection for removal and to close the connection. This ensures that at the end of the + // transaction, the connection is destroyed. A running thread holding a connection will run to completion before the + // connection is destroyed" getResourceManager(resource.getResourceSpec()).delistResource(resource, TMSUCCESS); + + // TODO: Why is resourceHandle.setConnectionErrorOccurred() not performed? + // Added example in unit test PoolManagerImplTest that shows connection error is not updated putbackResourceToPool(resource, true); } - @Override - public void putbackBadResourceToPool(ResourceHandle resourceHandle) { + private void putbackBadResourceToPool(ResourceHandle resourceHandle) { // Notify pool PoolInfo poolInfo = resourceHandle.getResourceSpec().getPoolInfo(); if (poolInfo != null) { ResourcePool pool = poolTable.get(poolInfo); if (pool != null) { synchronized (pool) { + // TODO: why is resourceClosed called AND resourceErrorOccurred? pool.resourceClosed(resourceHandle); resourceHandle.setConnectionErrorOccurred(); pool.resourceErrorOccurred(resourceHandle); @@ -387,18 +429,26 @@ public void putbackBadResourceToPool(ResourceHandle resourceHandle) { @Override public void putbackResourceToPool(ResourceHandle resourceHandle, boolean errorOccurred) { + if (LOG.isLoggable(Level.FINE)) { + LOG.log(Level.FINE, "PoolManagerImpl.putbackResourceToPool START, resource=" + resourceHandle + ", errorOccurred=" + errorOccurred); + } // Notify pool PoolInfo poolInfo = resourceHandle.getResourceSpec().getPoolInfo(); if (poolInfo != null) { ResourcePool pool = poolTable.get(poolInfo); if (pool != null) { if (errorOccurred) { + // TODO: this code is different from putbackBadResourceToPool logic, explain why + // TODO: why is resourceHandle.setConnectionErrorOccurred(); not called? pool.resourceErrorOccurred(resourceHandle); } else { pool.resourceClosed(resourceHandle); } } } + if (LOG.isLoggable(Level.FINE)) { + LOG.log(Level.FINE, "PoolManagerImpl.putbackResourceToPool END, resource=" + resourceHandle + ", errorOccurred=" + errorOccurred); + } } @Override diff --git a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/PoolStatus.java b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/PoolStatus.java index 6bf484a3d8d..011029c6bcb 100644 --- a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/PoolStatus.java +++ b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/PoolStatus.java @@ -62,4 +62,9 @@ public void setPoolInfo(PoolInfo poolInfo) { public PoolStatus(PoolInfo poolInfo) { this.poolInfo = poolInfo; } + + @Override + public String toString() { + return "PoolStatus [poolInfo=" + poolInfo + ", numConnFree=" + numConnFree + ", numConnUsed=" + numConnUsed + "]"; + } } diff --git a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/PoolTxHelper.java b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/PoolTxHelper.java index 4b66f2ca461..074d1235839 100644 --- a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/PoolTxHelper.java +++ b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/PoolTxHelper.java @@ -55,7 +55,8 @@ public PoolTxHelper(PoolInfo poolInfo) { * Check whether the local resource can be put back to pool If true, unenlist the resource * * @param h ResourceHandle to be verified - * @return boolean + * @return true if the resource handle is eligible for reuse, otherwise false. NOTE in case of true, this method alters + * the handle state to enlisted=false */ public boolean isLocalResourceEligibleForReuse(ResourceHandle h) { boolean result = false; @@ -70,9 +71,17 @@ public boolean isLocalResourceEligibleForReuse(ResourceHandle h) { } return result; } + h.getResourceState().setEnlisted(false); + if (_logger.isLoggable(Level.FINE)) { + _logger.log(Level.FINE, "Pool: isLocalResourceEligibleForReuse, eligible=true, enlisted changed to true for handle=" + h); + } result = true; } + + if (_logger.isLoggable(Level.FINE)) { + _logger.log(Level.FINE, "Pool: isLocalResourceEligibleForReuse, eligible=false, handle=" + h); + } return result; } @@ -153,7 +162,6 @@ public boolean isNonXAResource(ResourceHandle resource) { * @return boolean indicating whether thegiven non-xa resource is in transaction */ private boolean isNonXAResourceInTransaction(JavaEETransaction tran, ResourceHandle resource) { - return resource.equals(tran.getNonXAResource()); } @@ -175,6 +183,9 @@ public boolean isNonXAResourceAndFree(JavaEETransaction tran, ResourceHandle res * @param resource Resource to be enlisted in the transaction */ public void resourceEnlisted(Transaction tran, ResourceHandle resource) { + if (_logger.isLoggable(Level.FINE)) { + _logger.log(Level.FINE, "Pool: resourceEnlisted START, tran=" + tran + ", resource=" + resource + ", poolInfo=" + poolInfo); + } try { JavaEETransaction j2eetran = (JavaEETransaction) tran; Set set = j2eetran.getResources(poolInfo); @@ -192,7 +203,7 @@ public void resourceEnlisted(Transaction tran, ResourceHandle resource) { ResourceState state = resource.getResourceState(); state.setEnlisted(true); if (_logger.isLoggable(Level.FINE)) { - _logger.log(Level.FINE, "Pool [ " + poolInfo + " ]: resourceEnlisted: " + resource); + _logger.log(Level.FINE, "Pool: resourceEnlisted END, tran=" + tran + ", resource=" + resource); } } @@ -205,6 +216,10 @@ public void resourceEnlisted(Transaction tran, ResourceHandle resource) { * @return delisted resources */ public List transactionCompleted(Transaction tran, int status, PoolInfo poolInfo) { + if (_logger.isLoggable(Level.FINE)) { + _logger.log(Level.FINE, "Pool: transactionCompleted START, tran= " + tran + ", poolInfo=" + poolInfo); + } + JavaEETransaction j2eetran; List delistedResources = new ArrayList<>(); try { @@ -233,6 +248,10 @@ public List transactionCompleted(Transaction tran, int status, P _logger.log(Level.FINE, "Pool: transactionCompleted: " + resource); } } + + if (_logger.isLoggable(Level.FINE)) { + _logger.log(Level.FINE, "Pool: transactionCompleted END, tran= " + tran); + } return delistedResources; } } diff --git a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/ResourcePool.java b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/ResourcePool.java index 36bb4a50dcf..f9d79d3d896 100644 --- a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/ResourcePool.java +++ b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/ResourcePool.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Contributors to the Eclipse Foundation + * Copyright (c) 2022, 2024 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 @@ -38,31 +38,85 @@ public interface ResourcePool { // Modify getResource() to throw PoolingException ResourceHandle getResource(ResourceSpec spec, ResourceAllocator alloc, Transaction transaction) throws PoolingException, RetryableUnavailableException; + /** + * Indicate that the resource is not used by a bean/application anymore. + * + * @param resource The resource that is not used anymore. After the call the resource is also marked as 'not busy' / + * 'free'. + */ void resourceClosed(ResourceHandle resource); + /** + * Updates the resource to be marked as not busy / free, remove the resource from the connection pool and inform any + * waiting threads that a resource has become available. + * + * @param resource the resource that will be removed from the connection pool + */ void resourceErrorOccurred(ResourceHandle resource); + /** + * This method is called when a resource is enlisted in a transaction. + * + * @param tran the Transaction to enlist the resource in + * @param resource the ResourceHandle that will be enlisted in the given transaction + */ void resourceEnlisted(Transaction tran, ResourceHandle resource); - // Get status of pool + /** + * Get the Pool status by computing the free/used values of the connections in the pool. Computations are based on + * whether the pool is initialized or not when this method is invoked. + * + * @return the PoolStatus object + */ PoolStatus getPoolStatus(); + /** + * Called when a transaction is completed.
+ * All resource handles associated to the given transaction are delisted.
+ * All resource handles associated to the given transaction handed back to the connection pool. + * + * @param tran The transaction + * @param status The jakarta.transaction.Status value of the transaction. + */ void transactionCompleted(Transaction tran, int status); + /** + * Resize the pool by removing idle and invalid resources.
+ * Only when forced is true the pool size is scaled down with the pool resize quantity. + * + * @param forced when force is true, scale down the pool with the pool resize quantity. + */ void resizePool(boolean forced); - // forcefully destroy all connections in the pool even if - // connections have transactions in progress + /** + * Forcefully destroy all connections in the pool even if connections have transactions in progress + */ void emptyPool(); - // reconfig the pool's properties + /** + * Reconfigure the Pool's properties. The reconfigConnectorConnectionPool method in the ConnectorRuntime will use this + * method (through PoolManager) if it needs to just change pool properties and not recreate the pool + * + * @param poolResource - the ConnectorConnectionPool JavaBean that holds the new pool properties + * @throws PoolingException if the pool resizing fails + */ void reconfigurePool(ConnectorConnectionPool ccp) throws PoolingException; - // cancel the resizer task in the pool + /** + * Cancel the resizer task in the pool if it exists. + */ void cancelResizerTask(); + /** + * Switch on matching of connections in the pool. + */ void switchOnMatching(); + /** + * Get the PoolInfo unique identifier of this pool. + * + * @return the PoolInfo unique identifier of this pool + */ PoolInfo getPoolInfo(); void emptyFreeConnectionsInPool(); @@ -117,22 +171,23 @@ public interface ResourcePool { void setSelfManaged(boolean selfManaged); /** - * set pool life cycle listener + * Set the pool life cycle listener * - * @param listener + * @param listener the new PoolLifeCycleListener */ void setPoolLifeCycleListener(PoolLifeCycleListener listener); /** - * remove pool life cycle listener + * Remove the pool life cycle listener */ void removePoolLifeCycleListener(); /** - * Flush Connection pool by reinitializing the connections established in the pool. + * Flush Connection pool by removing all resources established in the connection pool and bring the pool to steady pool + * size. * * @return boolean indicating whether flush operation was successful or not - * @throws com.sun.appserv.connectors.internal.api.PoolingException + * @throws PoolingException in case the pool was not initialized */ boolean flushConnectionPool() throws PoolingException; @@ -160,7 +215,7 @@ public interface ResourcePool { /** * returns the reconfig-wait-time * - * @return long + * @return the reconfig-wait-time */ long getReconfigWaitTime(); } diff --git a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/UnpooledResource.java b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/UnpooledResource.java index a02d9a13eb2..bb9767c5f50 100644 --- a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/UnpooledResource.java +++ b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/UnpooledResource.java @@ -20,7 +20,6 @@ import com.sun.appserv.connectors.internal.api.PoolingException; import com.sun.enterprise.resource.ResourceHandle; import com.sun.enterprise.resource.ResourceSpec; -import com.sun.enterprise.resource.ResourceState; import com.sun.enterprise.resource.allocator.ResourceAllocator; import jakarta.transaction.Transaction; @@ -81,10 +80,10 @@ protected ResourceHandle getUnenlistedResource(ResourceSpec spec, ResourceAlloca this.poolSize.decrement(); throw ex; } + handle.getResourceState().reset(); - ResourceState state = new ResourceState(); - handle.setResourceState(state); - state.setEnlisted(false); + // TODO: document that all get(Unenlisted)Resource methods must return state busy resource + // TODO: rename variables, they currently have 2 or 3 names by default: handle, resource and resourceHandle setResourceStateToBusy(handle); return handle; } diff --git a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/datastructure/DataStructure.java b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/datastructure/DataStructure.java index 83245b0d4fa..58861730bfa 100644 --- a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/datastructure/DataStructure.java +++ b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/datastructure/DataStructure.java @@ -43,11 +43,11 @@ public interface DataStructure { void setMaxSize(int maxSize); /** - * creates a new resource and adds to the datastructure. + * Create a new resource using the given resource-allocator and add it to the datastructure. * - * @param allocator ResourceAllocator - * @param count Number (units) of resources to create - * @return int number of resources added. + * @param allocator the resource-allocator to be used + * @param count the number (units) of resources to create + * @return the number of resources added * @throws PoolingException when unable to create a resource */ int addResource(ResourceAllocator allocator, int count) throws PoolingException; @@ -93,11 +93,11 @@ public interface DataStructure { int getResourcesSize(); /** - * Get all resources in the datastructure Note : do not use this for normal usages as it can potentially represent all - * resources (including the ones in use). This is used under special circumstances where there is a need to process all - * resources. + * Get all resources in the datastructure
+ * Note: do not use this for normal usages as it can potentially represent all resources (including the ones in use). + * This is used under special circumstances where there is a need to process all resources. * - * @return List + * @return the list of resources in the data structure. */ List getAllResources(); } diff --git a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/datastructure/DataStructureFactory.java b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/datastructure/DataStructureFactory.java index 870fd74b445..20a3c7f01ae 100644 --- a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/datastructure/DataStructureFactory.java +++ b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/datastructure/DataStructureFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Contributors to the Eclipse Foundation + * Copyright (c) 2022, 2024 Contributors to the Eclipse Foundation. * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the @@ -39,33 +39,33 @@ public class DataStructureFactory { // TODO synchronize datastructure creation ? protected final static Logger _logger = LogDomains.getLogger(DataStructureFactory.class, LogDomains.RSR_LOGGER); - public static DataStructure getDataStructure(String className, String parameters, int maxPoolSize, ResourceHandler handler, String strategyClass) throws PoolingException { + public static DataStructure getDataStructure(String className, String parameters, int maxPoolSize, ResourceHandler handler) throws PoolingException { DataStructure dataStructure; if (className != null) { if (className.equals(ListDataStructure.class.getName())) { - dataStructure = new ListDataStructure(parameters, maxPoolSize, handler, strategyClass); + dataStructure = new ListDataStructure(parameters, maxPoolSize, handler); } else if (className.equals(RWLockDataStructure.class.getName())) { - dataStructure = new RWLockDataStructure(parameters, maxPoolSize, handler, strategyClass); + dataStructure = new RWLockDataStructure(parameters, maxPoolSize, handler); } else { - dataStructure = initializeCustomDataStructureInPrivilegedMode(className, parameters, maxPoolSize, handler, strategyClass); + dataStructure = initializeCustomDataStructureInPrivilegedMode(className, parameters, maxPoolSize, handler); } } else { debug("Initializing RWLock DataStructure"); - dataStructure = new RWLockDataStructure(parameters, maxPoolSize, handler, strategyClass); + dataStructure = new RWLockDataStructure(parameters, maxPoolSize, handler); } return dataStructure; } - private static DataStructure initializeCustomDataStructureInPrivilegedMode(final String className, final String parameters, final int maxPoolSize, final ResourceHandler handler, final String strategyClass) throws PoolingException { + private static DataStructure initializeCustomDataStructureInPrivilegedMode(final String className, final String parameters, final int maxPoolSize, final ResourceHandler handler) throws PoolingException { Object result = AccessController.doPrivileged(new PrivilegedAction() { @Override public Object run() { Object result = null; try { - result = initializeDataStructure(className, parameters, maxPoolSize, handler, strategyClass); + result = initializeDataStructure(className, parameters, maxPoolSize, handler); } catch (Exception e) { _logger.log(WARNING, "pool.datastructure.init.failure", className); _logger.log(WARNING, "pool.datastructure.init.failure.exception", e); @@ -80,12 +80,12 @@ public Object run() { } } - private static DataStructure initializeDataStructure(String className, String parameters, int maxPoolSize, ResourceHandler handler, String strategyClass) throws Exception { + private static DataStructure initializeDataStructure(String className, String parameters, int maxPoolSize, ResourceHandler handler) throws Exception { DataStructure ds; - Object[] constructorParameters = new Object[] { parameters, maxPoolSize, handler, strategyClass }; + Object[] constructorParameters = new Object[] { parameters, maxPoolSize, handler }; Class class1 = Thread.currentThread().getContextClassLoader().loadClass(className); - Constructor constructor = class1.getConstructor(String.class, int.class, ResourceHandler.class, String.class); + Constructor constructor = class1.getConstructor(String.class, int.class, ResourceHandler.class); ds = (DataStructure) constructor.newInstance(constructorParameters); _logger.log(FINEST, "Using Pool Data Structure : ", className); diff --git a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/datastructure/ListDataStructure.java b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/datastructure/ListDataStructure.java index 7747abd8e69..a44d6177ed3 100644 --- a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/datastructure/ListDataStructure.java +++ b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/datastructure/ListDataStructure.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023 Contributors to the Eclipse Foundation + * Copyright (c) 2022, 2024 Contributors to the Eclipse Foundation * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the @@ -44,16 +44,14 @@ public class ListDataStructure implements DataStructure { private ResourceHandler handler; private ResourceSelectionStrategy strategy; - public ListDataStructure(String parameters, int maxSize, ResourceHandler handler, String strategyClass) { + public ListDataStructure(String parameters, int maxSize, ResourceHandler handler) { resources = new ArrayList<>((maxSize > 1000) ? 1000 : maxSize); free = new ArrayList<>((maxSize > 1000) ? 1000 : maxSize); this.handler = handler; - initializeStrategy(strategyClass); dynSemaphore = new DynamicSemaphore(); setMaxSize(maxSize); } - /** * Set maxSize based on the new max pool size set on the connection pool * during a reconfiguration. @@ -86,10 +84,6 @@ public synchronized void setMaxSize(int newMaxSize) { this.maxSize = newMaxSize; } - private void initializeStrategy(String strategyClass) { - //TODO - } - /** * creates a new resource and adds to the datastructure. * diff --git a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/datastructure/RWLockDataStructure.java b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/datastructure/RWLockDataStructure.java index cde98b9007e..22fb2b3a09a 100644 --- a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/datastructure/RWLockDataStructure.java +++ b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/datastructure/RWLockDataStructure.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2023 Contributors to the Eclipse Foundation + * Copyright (c) 2022, 2024 Contributors to the Eclipse Foundation * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the @@ -54,7 +54,7 @@ public class RWLockDataStructure implements DataStructure { private volatile int maxSize; - public RWLockDataStructure(String parameters, int maxSize, ResourceHandler handler, String strategyClass) { + public RWLockDataStructure(String parameters, int maxSize, ResourceHandler handler) { this.availableResources = new DataStructureSemaphore(maxSize); this.useMask = new BitSet(maxSize); this.resources = new ResourceHandle[maxSize]; @@ -64,7 +64,6 @@ public RWLockDataStructure(String parameters, int maxSize, ResourceHandler handl LOG.log(Level.FINEST, "pool.datastructure.rwlockds.init"); } - @Override public int addResource(ResourceAllocator allocator, int count) throws PoolingException { int numResAdded = 0; @@ -96,7 +95,7 @@ public int addResource(ResourceAllocator allocator, int count) throws PoolingExc newResources = Arrays.copyOf(resources, currentMaxSize); } - resource.setIndex(size); + resource.setRwLockDataStructureResourceIndex(size); stamp = lock.tryConvertToWriteLock(stamp); if (stamp == 0L) { @@ -167,7 +166,7 @@ public void removeResource(ResourceHandle resource) { } int currentSize = size; - int removeIndex = resource.getIndex(); + int removeIndex = resource.getRwLockDataStructureResourceIndex(); if (!lock.validate(stamp)) { continue; } @@ -196,7 +195,7 @@ public void removeResource(ResourceHandle resource) { if (removeIndex < lastIndex) { // Move last resource in place of removed ResourceHandle lastResource = resources[lastIndex]; - lastResource.setIndex(removeIndex); + lastResource.setRwLockDataStructureResourceIndex(removeIndex); resources[removeIndex] = lastResource; useMask.set(removeIndex, useMask.get(lastIndex)); } @@ -228,7 +227,7 @@ public void returnResource(ResourceHandle resource) { } int currentSize = size; - int returnIndex = resource.getIndex(); + int returnIndex = resource.getRwLockDataStructureResourceIndex(); if (!lock.validate(stamp)) { continue; } diff --git a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/resizer/AssocWithThreadPoolResizer.java b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/resizer/AssocWithThreadPoolResizer.java index 1d8b5755a02..e286ffc203b 100644 --- a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/resizer/AssocWithThreadPoolResizer.java +++ b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/resizer/AssocWithThreadPoolResizer.java @@ -186,7 +186,7 @@ private int removeInvalidResources(Set freeConnectionsToValidate for (ResourceHandle handle : freeConnectionsToValidate) { if (handle != null) { Set connectionsToTest = new HashSet<>(); - connectionsToTest.add((ManagedConnection) handle.getResource()); + connectionsToTest.add(handle.getResource()); Set invalidConnections = handler.getInvalidConnections(connectionsToTest); if (invalidConnections != null && !invalidConnections.isEmpty()) { invalidConnectionsCount = validateAndRemoveResource(handle, invalidConnections); diff --git a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/resizer/Resizer.java b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/resizer/Resizer.java index 1a9a9308952..b9046e20247 100644 --- a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/resizer/Resizer.java +++ b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/pool/resizer/Resizer.java @@ -83,9 +83,10 @@ public void run() { } /** - * Resize the pool + * Resize the pool by removing idle and invalid resources.
+ * Only when forced is true the pool size is scaled down with the pool resize quantity. * - * @param forced when force is true, scale down the pool. + * @param forced when force is true, scale down the pool with the pool resize quantity. */ public void resizePool(boolean forced) { @@ -125,13 +126,14 @@ private void ensureSteadyPool() { } /** - * Scale down pool by a size <= pool-resize-quantity + * Scale down pool by a size <= pool-resize-quantity but only if forced is true * - * @param forced scale-down only when forced - * @param scaleDownQuantity no. of resources to remove + * @param scaleDownQuantity the number of resources to remove + * @param forced scale-down only when forced value is true + * + * TODO: move forced parameter out of this method and move it to the calling code */ protected void scaleDownPool(int scaleDownQuantity, boolean forced) { - if (pool.getResizeQuantity() > 0 && forced) { scaleDownQuantity = (scaleDownQuantity <= (dataStructure.getResourcesSize() - pool.getSteadyPoolSize())) ? scaleDownQuantity : 0; @@ -232,7 +234,7 @@ private void removeInvalidResources(Set freeConnectionsToValidate) { // validate if the connection is one in the freeConnectionsToValidate if (freeConnectionsToValidate.contains(handle.toString())) { Set connectionsToTest = new HashSet<>(); - connectionsToTest.add((ManagedConnection) handle.getResource()); + connectionsToTest.add(handle.getResource()); Set invalidConnections = handler.getInvalidConnections(connectionsToTest); if (invalidConnections != null && !invalidConnections.isEmpty()) { invalidConnectionsCount = validateAndRemoveResource(handle, invalidConnections); diff --git a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/rm/LazyEnlistableResourceManagerImpl.java b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/rm/LazyEnlistableResourceManagerImpl.java index 58954983780..c09e5935d95 100755 --- a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/rm/LazyEnlistableResourceManagerImpl.java +++ b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/rm/LazyEnlistableResourceManagerImpl.java @@ -33,6 +33,7 @@ import java.util.logging.Logger; import org.glassfish.api.invocation.ComponentInvocation; +import org.glassfish.resourcebase.resources.api.PoolInfo; import static com.sun.logging.LogDomains.RSR_LOGGER; import static java.util.logging.Level.WARNING; @@ -96,7 +97,7 @@ public void lazyEnlist(ManagedConnection managedConnection) throws ResourceExcep ListIterator it = l.listIterator(); while (it.hasNext()) { ResourceHandle hand = (ResourceHandle) it.next(); - ManagedConnection toEnlist = (ManagedConnection) hand.getResource(); + ManagedConnection toEnlist = hand.getResource(); if (managedConnection.equals(toEnlist)) { resourceHandle = hand; break; @@ -114,7 +115,7 @@ public void lazyEnlist(ManagedConnection managedConnection) throws ResourceExcep // The other case might or might not work if (resourceHandle != null && resourceHandle.getResourceState().isUnenlisted()) { try { - // Enable the suspended lazyenlistment so as to enlist the resource. + // Enable enlistment to be able to enlist the resource. resourceHandle.setEnlistmentSuspended(false); transactionManager.enlistResource(transaction, resourceHandle); @@ -123,10 +124,11 @@ public void lazyEnlist(ManagedConnection managedConnection) throws ResourceExcep } catch (Exception e) { // In the rare cases where enlistResource throws exception, we // should return the resource to the pool - ConnectorRuntime.getRuntime().getPoolManager().putbackDirectToPool(resourceHandle, - resourceHandle.getResourceSpec().getPoolInfo()); + // TODO: Why not call resourceClosed instead of putbackDirectToPool? + PoolInfo poolInfo = resourceHandle.getResourceSpec().getPoolInfo(); + ConnectorRuntime.getRuntime().getPoolManager().putbackDirectToPool(resourceHandle, poolInfo); - LOG.log(WARNING, "poolmgr.err_enlisting_res_in_getconn", resourceHandle.getResourceSpec().getPoolInfo()); + LOG.log(WARNING, "poolmgr.err_enlisting_res_in_getconn", poolInfo); LOG.fine("rm.enlistResource threw Exception. Returning resource to pool"); // And rethrow the exception diff --git a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/rm/ResourceManager.java b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/rm/ResourceManager.java index 8911987ad2b..b93f7ef22c4 100755 --- a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/rm/ResourceManager.java +++ b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/rm/ResourceManager.java @@ -71,7 +71,7 @@ public interface ResourceManager { * Delist the resource from the transaction. * * @param resource Resource to be delisted. - * @param xaresFlag XA Flag + * @param xaresFlag flag indicating transaction success. This can be XAResource.TMSUCCESS or XAResource.TMFAIL */ void delistResource(ResourceHandle resource, int xaresFlag); @@ -79,7 +79,7 @@ public interface ResourceManager { * Unregister the resource from a transaction's list. * * @param resource Resource to be unregistered. - * @param xaresFlag XA Flag + * @param xaresFlag flag indicating transaction success. This can be XAResource.TMSUCCESS or XAResource.TMFAIL */ void unregisterResource(ResourceHandle resource, int xaresFlag); } diff --git a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/rm/ResourceManagerImpl.java b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/rm/ResourceManagerImpl.java index b9fd0b269ca..03aa5623eb8 100755 --- a/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/rm/ResourceManagerImpl.java +++ b/appserver/connectors/connectors-runtime/src/main/java/com/sun/enterprise/resource/rm/ResourceManagerImpl.java @@ -100,6 +100,11 @@ public void enlistResource(ResourceHandle resourceHandle) throws PoolingExceptio */ @Override public void registerResource(ResourceHandle handle) throws PoolingException { + if (LOG.isLoggable(Level.FINE)) { + LOG.log(Level.FINE, "ResourceManagerImpl.registerResource START: handle=" + handle + ", resource=" + + handle.getResource() + ", transactional=" + handle.isTransactional()); + } + try { JavaEETransactionManager transactionManager = getTransactionManager(); @@ -107,6 +112,10 @@ public void registerResource(ResourceHandle handle) throws PoolingException { if (handle.isTransactional()) { ComponentInvocation componentInvocation = getCurrentInvocation(); + if (LOG.isLoggable(Level.FINE)) { + LOG.log(Level.FINE, "ResourceManagerImpl.registerResource: componentInvocation=" + componentInvocation); + } + Transaction transaction = null; if (componentInvocation == null) { @@ -115,12 +124,18 @@ public void registerResource(ResourceHandle handle) throws PoolingException { // method/ in that, you return the transaction from the TxManager try { transaction = transactionManager.getTransaction(); + if (LOG.isLoggable(Level.FINE)) { + LOG.log(Level.FINE, "ResourceManagerImpl.registerResource: A transaction=" + transaction); + } } catch (Exception e) { transaction = null; LOG.log(INFO, e.getMessage()); } } else { transaction = (Transaction) componentInvocation.getTransaction(); + if (LOG.isLoggable(Level.FINE)) { + LOG.log(Level.FINE, "ResourceManagerImpl.registerResource: B transaction=" + transaction); + } transactionManager.registerComponentResource(handle); } @@ -129,7 +144,7 @@ public void registerResource(ResourceHandle handle) throws PoolingException { transactionManager.enlistResource(transaction, handle); } catch (Exception ex) { if (LOG.isLoggable(Level.FINE)) { - LOG.fine("Exception whle trying to enlist resource " + ex.getMessage()); + LOG.fine("Exception while trying to enlist resource " + ex.getMessage()); } // If transactional, remove the connection handle from the @@ -149,6 +164,11 @@ public void registerResource(ResourceHandle handle) throws PoolingException { LOG.log(SEVERE, "poolmgr.component_register_exception", ex); throw new PoolingException(ex.toString(), ex); } + + if (LOG.isLoggable(Level.FINE)) { + LOG.log(Level.FINE, "ResourceManagerImpl.registerResource END: handle=" + handle + ", resource=" + + handle.getResource() + ", transactional=" + handle.isTransactional()); + } } // Overridden by the LazyEnlistableResourceManager to be a No-Op @@ -210,7 +230,8 @@ public void delistResource(ResourceHandle resource, int xaresFlag) { } /** - * Unregister the ResourceHandle from the transaction + * Unregister the ResourceHandle from the transaction TODO: document what the resource state should be: + * enlisted? busy? TODO: move documentation to the interface * * @param resource ResourceHandle object * @param xaresFlag flag indicating transaction success. This can be XAResource.TMSUCCESS or XAResource.TMFAIL @@ -218,6 +239,12 @@ public void delistResource(ResourceHandle resource, int xaresFlag) { */ @Override public void unregisterResource(ResourceHandle resource, int xaresFlag) { + if (LOG.isLoggable(Level.FINE)) { + LOG.log(Level.FINE, + "ResourceManagerImpl.unregisterResource START: handle=" + resource + ", resource=" + + resource.getResource() + + ", transactional=" + resource.isTransactional()); + } JavaEETransactionManager transactionManager = getTransactionManager(); Transaction transaction = null; @@ -245,6 +272,9 @@ public void unregisterResource(ResourceHandle resource, int xaresFlag) { } if (transaction != null && resource.isEnlisted()) { + // TODO: delistResource seems to return always true, or throws an exception + // which is ok, but return type could be removed! Since it is not used here to see + // if something went wrong. transactionManager.delistResource(transaction, resource, xaresFlag); } } @@ -258,6 +288,12 @@ public void unregisterResource(ResourceHandle resource, int xaresFlag) { // UnregisterResource is called outside of component context // likely to be container-forced destroy. Do nothing } + + if (LOG.isLoggable(Level.FINE)) { + LOG.log(Level.FINE, "ResourceManagerImpl.unregisterResource END: handle=" + resource + ", resource=" + + resource.getResource() + + ", transactional=" + resource.isTransactional()); + } } private static ComponentInvocation getCurrentInvocation() { diff --git a/appserver/connectors/connectors-runtime/src/test/java/com/sun/enterprise/resource/listener/LocalTxConnectionEventListenerTest.java b/appserver/connectors/connectors-runtime/src/test/java/com/sun/enterprise/resource/listener/LocalTxConnectionEventListenerTest.java index 2ded2bbc40b..b0133445f59 100644 --- a/appserver/connectors/connectors-runtime/src/test/java/com/sun/enterprise/resource/listener/LocalTxConnectionEventListenerTest.java +++ b/appserver/connectors/connectors-runtime/src/test/java/com/sun/enterprise/resource/listener/LocalTxConnectionEventListenerTest.java @@ -82,6 +82,6 @@ public void getAssociatedHandlesAndClearMapTest() throws ResourceException { private ResourceHandle createResourceHandle(int i) throws ResourceException { ManagedConnection managedConnection = createNiceMock(ManagedConnection.class); replay(); - return new ResourceHandle(managedConnection, new ResourceSpec(new SimpleJndiName("testResource" + i), 0), null, null); + return new ResourceHandle(managedConnection, new ResourceSpec(new SimpleJndiName("testResource" + i), 0), null); } } diff --git a/appserver/connectors/connectors-runtime/src/test/java/com/sun/enterprise/resource/pool/ConnectionPoolTest.java b/appserver/connectors/connectors-runtime/src/test/java/com/sun/enterprise/resource/pool/ConnectionPoolTest.java index 6fea4229fb3..eab29934937 100644 --- a/appserver/connectors/connectors-runtime/src/test/java/com/sun/enterprise/resource/pool/ConnectionPoolTest.java +++ b/appserver/connectors/connectors-runtime/src/test/java/com/sun/enterprise/resource/pool/ConnectionPoolTest.java @@ -31,7 +31,6 @@ import com.sun.enterprise.transaction.api.JavaEETransaction; import com.sun.logging.LogDomains; -import jakarta.resource.ResourceException; import jakarta.resource.spi.ManagedConnection; import jakarta.resource.spi.ManagedConnectionFactory; import jakarta.resource.spi.RetryableUnavailableException; @@ -94,7 +93,7 @@ public class ConnectionPoolTest { // they can probably also be made as a unit test here / or in a similar unit test @BeforeEach - public void createAndPopulateMocks() throws PoolingException, ResourceException { + public void createAndPopulateMocks() throws Exception { List mocks = new ArrayList<>(); // Mock ManagedConnection @@ -124,18 +123,18 @@ public void createAndPopulateMocks() throws PoolingException, ResourceException // Make sure ConnectorRuntime singleton is initialized MyConnectorRuntime connectorRuntime = new MyConnectorRuntime(); - ProcessEnvironment processEnvironment = new ProcessEnvironment(); - connectorRuntime.setProcessEnvironment(processEnvironment); connectorRuntime.postConstruct(); } private void createConnectionPool(int maxPoolSize, int maxWaitTimeInMillis, int poolResizeQuantity) throws PoolingException { PoolInfo poolInfo = ConnectionPoolTest.getPoolInfo(); - MyConnectionPool.myMaxPoolSize = maxPoolSize; - MyConnectionPool.maxWaitTimeInMillis = maxWaitTimeInMillis; - MyConnectionPool.poolResizeQuantity = poolResizeQuantity; - connectionPool = new MyConnectionPool(poolInfo); + Hashtable env = new Hashtable<>(); + env.put("maxPoolSize", Integer.valueOf(maxPoolSize)); + env.put("maxWaitTimeInMillis", Integer.valueOf(maxWaitTimeInMillis)); + env.put("poolResizeQuantity", Integer.valueOf(poolResizeQuantity)); + + connectionPool = new MyConnectionPool(poolInfo, env); assertEquals(0, connectionPool.getSteadyPoolSize()); assertEquals(maxPoolSize, connectionPool.getMaxPoolSize()); @@ -187,7 +186,7 @@ void basicConnectionPoolTest() throws Exception { // Test issue #24843: make the state of resource1 not busy anymore (it should not happen but it happens in rare cases), // resource should still be closed without throwing an exception. - resource1.getResourceState().setBusy(false); + connectionPool.setResourceStateToFree(resource1); connectionPool.resourceClosed(resource1); assertResourceIsNotBusy(resource1); @@ -444,16 +443,15 @@ private void cleanupConnectionPool() { // Clean the pool connectionPool.emptyPool(); assertResourcesSize(0); + + PoolStatus poolStatus = connectionPool.getPoolStatus(); + assertEquals(0, poolStatus.getNumConnFree()); } public static class MyConnectionPool extends ConnectionPool { - public static int myMaxPoolSize; - public static int maxWaitTimeInMillis; - public static int poolResizeQuantity; - - public MyConnectionPool(PoolInfo poolInfo) throws PoolingException { - super(ConnectionPoolTest.getPoolInfo(), new Hashtable<>()); + public MyConnectionPool(PoolInfo poolInfo, Hashtable env) throws PoolingException { + super(ConnectionPoolTest.getPoolInfo(), env); } @Override @@ -463,6 +461,13 @@ protected ConnectorConnectionPool getPoolConfigurationFromJndi(Hashtable env) th ConnectorConnectionPool connectorConnectionPool = ConnectionPoolObjectsUtils .createDefaultConnectorPoolObject(poolInfo, null); + int myMaxPoolSize = (int) env.get("maxPoolSize"); + int maxWaitTimeInMillis = (int) env.get("maxWaitTimeInMillis"); + int poolResizeQuantity = (int) env.get("poolResizeQuantity"); + + assertTrue(myMaxPoolSize > 0); + assertTrue(poolResizeQuantity > 0); + // Override some defaults connectorConnectionPool.setSteadyPoolSize("0"); connectorConnectionPool.setMaxPoolSize("" + myMaxPoolSize); @@ -471,12 +476,18 @@ protected ConnectorConnectionPool getPoolConfigurationFromJndi(Hashtable env) th return connectorConnectionPool; } + + protected void scheduleResizerTask() { + // Do not schedule any resize tasks + } } public class MyConnectorRuntime extends ConnectorRuntime { + private ProcessEnvironment processEnvironment = new ProcessEnvironment(); - public void setProcessEnvironment(ProcessEnvironment processEnvironment) { - this.processEnvironment = processEnvironment; + public MyConnectorRuntime() throws Exception { + // Force 'injection' of private field processEnvironment + InjectionUtil.injectPrivateField(ConnectorRuntime.class, this, "processEnvironment", processEnvironment); } @Override @@ -496,7 +507,7 @@ public DelegatingClassLoader getConnectorClassLoader() { } } - private static PoolInfo getPoolInfo() { + public static PoolInfo getPoolInfo() { SimpleJndiName jndiName = new SimpleJndiName("myPool"); return new PoolInfo(jndiName); } diff --git a/appserver/connectors/connectors-runtime/src/test/java/com/sun/enterprise/resource/pool/InjectionUtil.java b/appserver/connectors/connectors-runtime/src/test/java/com/sun/enterprise/resource/pool/InjectionUtil.java new file mode 100644 index 00000000000..854d80d4b49 --- /dev/null +++ b/appserver/connectors/connectors-runtime/src/test/java/com/sun/enterprise/resource/pool/InjectionUtil.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024 Eclipse Foundation 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.resource.pool; + +import java.lang.reflect.Field; + +public class InjectionUtil { + + /** + * Use injection to fill in private fields in (final) classes. E.g. fields annotated with jakarta.inject.Inject. + * + * @param clazz the class to be altered + * @param clazzInstance the instance of the class that needs to be altered + * @param fieldName the name of the field in the class + * @param fieldValue the new value for the field + * @throws Exception if the injection of the value failed + */ + public static void injectPrivateField(Class clazz, Object clazzInstance, String fieldName, Object fieldValue) throws Exception { + Field declaredField = clazz.getDeclaredField(fieldName); + declaredField.setAccessible(true); + declaredField.set(clazzInstance, fieldValue); + } +} diff --git a/appserver/connectors/connectors-runtime/src/test/java/com/sun/enterprise/resource/pool/PoolManagerImplTest.java b/appserver/connectors/connectors-runtime/src/test/java/com/sun/enterprise/resource/pool/PoolManagerImplTest.java new file mode 100644 index 00000000000..02da1351ab7 --- /dev/null +++ b/appserver/connectors/connectors-runtime/src/test/java/com/sun/enterprise/resource/pool/PoolManagerImplTest.java @@ -0,0 +1,515 @@ +/* + * Copyright (c) 2024 Eclipse Foundation 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.resource.pool; + +import com.sun.appserv.connectors.internal.api.ConnectorConstants.PoolType; +import com.sun.appserv.connectors.internal.api.ConnectorRuntimeException; +import com.sun.appserv.connectors.internal.api.PoolingException; +import com.sun.enterprise.connectors.ConnectorRuntime; +import com.sun.enterprise.resource.ClientSecurityInfo; +import com.sun.enterprise.resource.ResourceHandle; +import com.sun.enterprise.resource.ResourceSpec; +import com.sun.enterprise.resource.allocator.ConnectorAllocator; +import com.sun.enterprise.resource.allocator.LocalTxConnectorAllocator; +import com.sun.enterprise.resource.allocator.NoTxConnectorAllocator; +import com.sun.enterprise.resource.allocator.ResourceAllocator; +import com.sun.enterprise.resource.pool.mock.JavaEETransactionManagerMock; +import com.sun.enterprise.resource.pool.mock.JavaEETransactionMock; +import com.sun.enterprise.transaction.api.JavaEETransaction; +import com.sun.enterprise.transaction.api.JavaEETransactionManager; +import com.sun.enterprise.transaction.spi.TransactionalResource; + +import jakarta.resource.spi.ManagedConnection; +import jakarta.resource.spi.ManagedConnectionFactory; +import jakarta.transaction.SystemException; +import jakarta.transaction.Transaction; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.glassfish.api.admin.ProcessEnvironment; +import org.glassfish.api.invocation.InvocationManager; +import org.glassfish.api.invocation.InvocationManagerImpl; +import org.glassfish.api.naming.GlassfishNamingManager; +import org.glassfish.api.naming.SimpleJndiName; +import org.glassfish.resourcebase.resources.api.PoolInfo; +import org.glassfish.resourcebase.resources.naming.ResourceNamingService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static com.sun.enterprise.resource.pool.ConnectionPoolTest.getPoolInfo; +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.createNiceMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.isNull; +import static org.easymock.EasyMock.notNull; +import static org.easymock.EasyMock.replay; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +public class PoolManagerImplTest { + + // Mocked instances + private GlassfishNamingManager glassfishNamingManager; + private ManagedConnection managedConnection; + private ManagedConnectionFactory managedConnectionFactory; + + // Regular fields + private ClientSecurityInfo clientSecurityInfo = null; + private PoolManagerImpl poolManagerImpl = new MyPoolManagerImpl(); + private PoolInfo poolInfo = getPoolInfo(); + private JavaEETransaction javaEETransaction = new MyJavaEETransaction(); + private MyJavaEETransactionManager javaEETransactionManager = new MyJavaEETransactionManager(); + private PoolType poolType; + + @BeforeEach + public void createAndPopulateMocks() throws Exception { + List mocks = new ArrayList<>(); + + // Mock GlassfishNamingManager + glassfishNamingManager = createNiceMock(GlassfishNamingManager.class); + mocks.add(glassfishNamingManager); + + // Mock ManagedConnection + managedConnection = createNiceMock(ManagedConnection.class); + expect(managedConnection.getConnection(isNull(), isNull())) + .andReturn(new MyDatabaseConnection()).anyTimes(); + mocks.add(managedConnection); + + // Mock ManagedConnectionFactory + ManagedConnectionFactory localConnectionFactory = createMock(ManagedConnectionFactory.class); + expect(localConnectionFactory.createManagedConnection(isNull(), isNull())) + .andReturn(managedConnection) + .atLeastOnce(); + // Must return a not null object in matchManagedConnections to ensure matching in the ConnectionPool is 'true' + expect(localConnectionFactory.matchManagedConnections(notNull(), isNull(), isNull())) + .andReturn(managedConnection) + .atLeastOnce(); + managedConnectionFactory = localConnectionFactory; + mocks.add(managedConnectionFactory); + + replay(mocks.toArray()); + + // Make sure ConnectorRuntime singleton is initialized + MyConnectorRuntime connectorRuntime = new MyConnectorRuntime(); + connectorRuntime.postConstruct(); + } + + /** + * Test to show the getResource behavior + */ + @Test + void getResourceTest() throws Exception { + // Create Standard pool + poolType = PoolType.STANDARD_POOL; + poolManagerImpl.createEmptyConnectionPool(poolInfo, poolType, new Hashtable<>()); + + ResourceSpec resourceSpec = createTestResourceSpec(poolInfo); + + // Test getting a single resource, this will return a 'userConnection' / 'connection handle' object representing the + // physical connection, e.g. a database connection. + + // Test using the No transaction allocator + ResourceAllocator noTxAllocator = new NoTxConnectorAllocator(null, managedConnectionFactory, resourceSpec, null, + null, null, null); + Object resource = poolManagerImpl.getResource(resourceSpec, noTxAllocator, clientSecurityInfo); + assertTrue(resource instanceof MyDatabaseConnection); + + // Test using the Local transaction allocator + ResourceAllocator localTxAllocator = new LocalTxConnectorAllocator(null, managedConnectionFactory, resourceSpec, null, + null, null, null, false); + resource = poolManagerImpl.getResource(resourceSpec, localTxAllocator, clientSecurityInfo); + assertTrue(resource instanceof MyDatabaseConnection); + + // Test using the XA transaction allocator + ResourceAllocator xAllocator = new ConnectorAllocator(null, managedConnectionFactory, resourceSpec, null, + null, null, null, false); + resource = poolManagerImpl.getResource(resourceSpec, xAllocator, clientSecurityInfo); + assertTrue(resource instanceof MyDatabaseConnection); + + // Resources from the pool should be occupied + assertPoolStatusNumberOfConnectionsUsed(3); + + // Get resource does not return a ResourceHandle, so we cannot return the resources to the pool. + // For now just flush all resources from the pool. + poolManagerImpl.flushConnectionPool(poolInfo); + + assertPoolStatusNumberOfConnectionsUsed(0); + + // Kill the pool + poolManagerImpl.killPool(poolInfo); + assertNull(poolManagerImpl.getPoolStatus(poolInfo)); + } + + /** + * Test to show the getResourceFromPool and resourceClosed behavior in relation to the ResourceHandle enlisted and busy + * states, while using the LocalTxConnectorAllocator. + */ + @Test + public void resourceClosedTest() throws Exception { + // Create Standard pool + poolType = PoolType.STANDARD_POOL; + poolManagerImpl.createEmptyConnectionPool(poolInfo, poolType, new Hashtable<>()); + + assertPoolStatusNumberOfConnectionsUsed(0); + assertPoolStatusNumberOfConnectionsFree(0); + + ResourceSpec resourceSpec = new ResourceSpec(new SimpleJndiName("myResourceSpec"), ResourceSpec.JNDI_NAME); + resourceSpec.setPoolInfo(poolInfo); + + // Test using the Local transaction allocator + ResourceAllocator localTxAllocator = new LocalTxConnectorAllocator(null, managedConnectionFactory, resourceSpec, null, + null, null, null, false); + ResourceHandle resource = poolManagerImpl.getResourceFromPool(resourceSpec, localTxAllocator, clientSecurityInfo, javaEETransaction); + assertNotNull(resource); + assertTrue(resource.getUserConnection() instanceof MyDatabaseConnection); + + // State should be marked busy directly after a getResource call + // The resource is not (yet) enlisted in a transaction + assertResourceIsBusy(resource); + assertResourceIsNotEnlisted(resource); + + // One resource from the pool should be added to the pool and should be occupied + assertPoolStatusNumberOfConnectionsUsed(1); + assertPoolStatusNumberOfConnectionsFree(0); + + // Enlist the resource in the transaction + assertResourceIsNotPartOfTransaction(javaEETransaction, resource); + resource.enlistedInTransaction(javaEETransaction); + assertResourceIsBusy(resource); + assertResourceIsEnlisted(resource); + assertResourceIsPartOfTransaction(javaEETransaction, resource); + + // Return the resource to the pool + poolManagerImpl.resourceClosed(resource); + // NOTE: in a multi threaded test the resource cannot be tested after this point! + + // When resource is returned to the pool the state is no longer busy + // But the resource is still enlisted in the transaction + assertResourceIsNotBusy(resource); + assertResourceIsEnlisted(resource); + assertResourceHasNoConnectionErrorOccured(resource); + assertResourceIsPartOfTransaction(javaEETransaction, resource); + + // Resource is still in use + assertPoolStatusNumberOfConnectionsUsed(1); + assertPoolStatusNumberOfConnectionsFree(0); + + // Stop the transaction to get the resource delisted / unenlisted from the transaction + // Mimic com.sun.enterprise.transaction.JavaEETransactionImpl.commit() call + poolManagerImpl.transactionCompleted(javaEETransaction, jakarta.transaction.Status.STATUS_COMMITTED); + + // State should remain not busy + // And the resource no longer enlisted in the transaction + assertResourceIsNotBusy(resource); + assertResourceIsNotEnlisted(resource); + assertResourceHasNoConnectionErrorOccured(resource); + assertResourceIsNotPartOfTransaction(javaEETransaction, resource); + + // Connection should no longer be in use + assertPoolStatusNumberOfConnectionsUsed(0); + assertPoolStatusNumberOfConnectionsFree(1); + + // Kill the pool + poolManagerImpl.killPool(poolInfo); + assertNull(poolManagerImpl.getPoolStatus(poolInfo)); + } + + /** + * Test to show the getResourceFromPool and resourceErrorOccurred behavior in relation to the ResourceHandle enlisted + * and busy states, while using the LocalTxConnectorAllocator. + */ + @Test + public void resourceErrorOccurredTest() throws Exception { + resourceErrorOrAbortedOccurredTest(false); + } + + /** + * Test to show the getResourceFromPool and resourceAbortOccurred behavior in relation to the ResourceHandle enlisted + * and busy states, while using the LocalTxConnectorAllocator. + */ + @Test + public void resourceAbortOccurredTest() throws Exception { + resourceErrorOrAbortedOccurredTest(true); + } + + private void resourceErrorOrAbortedOccurredTest(boolean resourceAbortedOccured) throws Exception { + // Create Standard pool + poolType = PoolType.STANDARD_POOL; + poolManagerImpl.createEmptyConnectionPool(poolInfo, poolType, new Hashtable<>()); + + assertPoolStatusNumberOfConnectionsUsed(0); + assertPoolStatusNumberOfConnectionsFree(0); + + ResourceSpec resourceSpec = new ResourceSpec(new SimpleJndiName("myResourceSpec"), ResourceSpec.JNDI_NAME); + resourceSpec.setPoolInfo(poolInfo); + + // Test using the Local transaction allocator + ResourceAllocator localTxAllocator = new LocalTxConnectorAllocator(null, managedConnectionFactory, resourceSpec, null, + null, null, null, false); + ResourceHandle resource = poolManagerImpl.getResourceFromPool(resourceSpec, localTxAllocator, clientSecurityInfo, javaEETransaction); + assertNotNull(resource); + assertTrue(resource.getUserConnection() instanceof MyDatabaseConnection); + + // State should be marked busy directly after a getResource call + // The resource is not (yet) enlisted in a transaction + assertResourceIsBusy(resource); + assertResourceIsNotEnlisted(resource); + + // One resource from the pool should be added to the pool and should be occupied + assertPoolStatusNumberOfConnectionsUsed(1); + assertPoolStatusNumberOfConnectionsFree(0); + + // Enlist the resource in the transaction + assertResourceIsNotPartOfTransaction(javaEETransaction, resource); + resource.enlistedInTransaction(javaEETransaction); + assertResourceIsBusy(resource); + assertResourceIsEnlisted(resource); + assertResourceIsPartOfTransaction(javaEETransaction, resource); + + if (resourceAbortedOccured) { + // Return the resource to the pool using resourceAbortOccurred + poolManagerImpl.resourceAbortOccurred(resource); + // Related transaction delist should be called for the resource + assertTrue(javaEETransactionManager.isDelistIsCalled(resource)); + + // TODO: connection error occurred flag on the resource is only set via badConnectionClosed + // why isn't it also called for resourceAbortedOccured? Bug?! + assertResourceHasNoConnectionErrorOccured(resource); + } else { + // Return the resource to the pool using resourceErrorOccurred + poolManagerImpl.resourceErrorOccurred(resource); + // Related transaction delist should be called for the resource, shouldn't it? + // resourceErrorOccurred does not seem to remove the resource from the transaction, possible bug? + // TODO: should be assertTrue + assertFalse(javaEETransactionManager.isDelistIsCalled(resource)); + + // TODO: connection error occurred flag on the resource is only set via badConnectionClosed + // why isn't it also called for resourceErrorOccurred? Bug?! + assertResourceHasNoConnectionErrorOccured(resource); + } + // NOTE: in a multi threaded test the resource cannot be tested after this point! + + // When resource is returned to the pool the state is no longer busy + // But the resource is still enlisted in the transaction + assertResourceIsNotBusy(resource); + assertResourceIsEnlisted(resource); + assertResourceIsPartOfTransaction(javaEETransaction, resource); + + // In case of putbackResourceToPool we would expect: Resource is still in use + // assertPoolStatusNumberOfConnectionsUsed(1); + // In case of putbackBadResourceToPool the resource is no longer listed as in use + assertPoolStatusNumberOfConnectionsUsed(0); + // And the bad resource should not have been returned to the free pool + assertPoolStatusNumberOfConnectionsFree(0); + + // Stop the transaction to get the resource delisted / unenlisted from the transaction + // Mimic com.sun.enterprise.transaction.JavaEETransactionImpl.commit() call + poolManagerImpl.transactionCompleted(javaEETransaction, jakarta.transaction.Status.STATUS_MARKED_ROLLBACK); + + // State should remain not busy + // And the resource no longer enlisted in the transaction + assertResourceIsNotBusy(resource); + assertResourceIsNotEnlisted(resource); + assertResourceIsNotPartOfTransaction(javaEETransaction, resource); + + // Connection should no longer be in use, and number of connections free should remain at 0 + assertPoolStatusNumberOfConnectionsUsed(0); + assertPoolStatusNumberOfConnectionsFree(0); + + // Kill the pool + poolManagerImpl.killPool(poolInfo); + assertNull(poolManagerImpl.getPoolStatus(poolInfo)); + + } + + private void assertResourceIsBusy(ResourceHandle resource) { + assertTrue(resource.getResourceState().isBusy()); + } + + private void assertResourceIsNotBusy(ResourceHandle resource) { + assertFalse(resource.getResourceState().isBusy()); + } + + private void assertResourceIsEnlisted(ResourceHandle resource) { + assertTrue(resource.isEnlisted()); + } + + private void assertResourceIsNotEnlisted(ResourceHandle resource) { + assertFalse(resource.isEnlisted()); + } + + private void assertResourceHasNoConnectionErrorOccured(ResourceHandle resource) { + assertFalse(resource.hasConnectionErrorOccurred()); + } + + private void assertPoolStatusNumberOfConnectionsUsed(int expectedNumber) { + PoolStatus poolStatus = poolManagerImpl.getPoolStatus(poolInfo); + assertEquals(expectedNumber, poolStatus.getNumConnUsed()); + } + + private void assertPoolStatusNumberOfConnectionsFree(int expectedNumber) { + PoolStatus poolStatus = poolManagerImpl.getPoolStatus(poolInfo); + assertEquals(expectedNumber, poolStatus.getNumConnFree()); + } + + private static ResourceSpec createTestResourceSpec(PoolInfo thePoolInfo) { + ResourceSpec resourceSpec = new ResourceSpec(new SimpleJndiName("myResourceSpec"), ResourceSpec.JNDI_NAME); + resourceSpec.setPoolInfo(thePoolInfo); + return resourceSpec; + } + + private void assertResourceIsPartOfTransaction(JavaEETransaction transaction, ResourceHandle expectedResource) { + for (Object resource : transaction.getResources(poolInfo)) { + if (resource instanceof ResourceHandle) { + ResourceHandle foundResource = (ResourceHandle) resource; + if (foundResource.equals(expectedResource)) { + return; + } + } + } + fail(); + } + + private void assertResourceIsNotPartOfTransaction(JavaEETransaction transaction, ResourceHandle expectedResource) { + Set resources = transaction.getResources(poolInfo); + if (resources != null) { + for (Object resource : resources) { + if (resource instanceof ResourceHandle) { + ResourceHandle foundResource = (ResourceHandle) resource; + if (foundResource.equals(expectedResource)) { + fail(); + } + } + } + } + } + + private class MyDatabaseConnection { + } + + private class MyConnectorRuntime extends ConnectorRuntime { + private InvocationManager manager = new InvocationManagerImpl(); + private ProcessEnvironment processEnvironment = new ProcessEnvironment(); + private ResourceNamingService resourceNamingService = new ResourceNamingService(); + + public MyConnectorRuntime() throws Exception { + // Force 'injection', unluckily ResourceNamingService is marked as final + // otherwise we could mock it, or subclass it for this unit test. + InjectionUtil.injectPrivateField(ResourceNamingService.class, resourceNamingService, "namingManager", glassfishNamingManager); + + // Force 'injection' of private field processEnvironment + InjectionUtil.injectPrivateField(ConnectorRuntime.class, this, "processEnvironment", processEnvironment); + } + + @Override + public JavaEETransactionManager getTransactionManager() { + return javaEETransactionManager; + } + + @Override + public InvocationManager getInvocationManager() { + return manager; + } + + @Override + public ResourceNamingService getResourceNamingService() { + return resourceNamingService ; + } + + @Override + public PoolManager getPoolManager() { + return poolManagerImpl; + } + + @Override + public PoolType getPoolType(PoolInfo poolInfo) throws ConnectorRuntimeException { + // Overriden to avoid ResourceNamingService jndi lookup calls in unit test + return poolType; + } + } + + // We cannot depend on the real JavaEETransactionManagerSimplified implementation due to dependency limitations + private class MyJavaEETransactionManager extends JavaEETransactionManagerMock { + + Map delistIsCalled = new HashMap<>(); + + @Override + public Transaction getTransaction() throws SystemException { + // Assuming only 1 transaction used in each unit test, return it + return javaEETransaction; + } + + @Override + public boolean delistResource(Transaction tran, TransactionalResource resource, int flag) throws IllegalStateException, SystemException { + // Store state for unit test validation + delistIsCalled.put(resource, Boolean.TRUE); + + // Return delist success + return true; + } + + public boolean isDelistIsCalled(TransactionalResource resource) { + return delistIsCalled.getOrDefault(resource, Boolean.FALSE); + } + } + + // We cannot depend on the real JavaEETransactionImpl due to dependency limitations + private class MyJavaEETransaction extends JavaEETransactionMock { + + private HashMap resourceTable = new HashMap<>(); + + @Override + public Set getAllParticipatingPools() { + return resourceTable.keySet(); + } + + @Override + public Set getResources(Object poolInfo) { + return resourceTable.get(poolInfo); + } + + @Override + public void setResources(Set resources, Object poolInfo) { + resourceTable.put(poolInfo, resources); + } + } + + private class MyPoolManagerImpl extends PoolManagerImpl { + + // Override createAndInitPool to be able to use our MyConnectionPool implementation to be able to override + // getPoolConfigurationFromJndi and scheduleResizerTask in the ConnectionPool class. + @Override + void createAndInitPool(PoolInfo poolInfo, PoolType poolType, Hashtable env) throws PoolingException { + // Abuse env hashTable to get fields into the getPoolConfigurationFromJndi method + env.put("maxPoolSize", Integer.valueOf(10)); + env.put("maxWaitTimeInMillis", Integer.valueOf(500)); + env.put("poolResizeQuantity", Integer.valueOf(1)); + + ConnectionPoolTest.MyConnectionPool pool = new ConnectionPoolTest.MyConnectionPool(poolInfo, env); + addPool(pool); + } + } +} diff --git a/appserver/connectors/connectors-runtime/src/test/java/com/sun/enterprise/resource/pool/datastructure/RWLockDataStructureTest.java b/appserver/connectors/connectors-runtime/src/test/java/com/sun/enterprise/resource/pool/datastructure/RWLockDataStructureTest.java index 7c13e8ce5c3..74591428a7a 100644 --- a/appserver/connectors/connectors-runtime/src/test/java/com/sun/enterprise/resource/pool/datastructure/RWLockDataStructureTest.java +++ b/appserver/connectors/connectors-runtime/src/test/java/com/sun/enterprise/resource/pool/datastructure/RWLockDataStructureTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Eclipse Foundation and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2024 Eclipse Foundation 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 @@ -17,12 +17,13 @@ package com.sun.enterprise.resource.pool.datastructure; import com.sun.appserv.connectors.internal.api.PoolingException; -import com.sun.enterprise.resource.ClientSecurityInfo; import com.sun.enterprise.resource.ResourceHandle; import com.sun.enterprise.resource.ResourceSpec; import com.sun.enterprise.resource.allocator.ResourceAllocator; import com.sun.enterprise.resource.pool.ResourceHandler; +import jakarta.resource.spi.ManagedConnection; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -80,9 +81,9 @@ public void createAndPopulateMocks() throws PoolingException { // We use constructor to generate ResourceHandle mock // because we depend on an internal state of this object. createMockBuilder(ResourceHandle.class) - .withConstructor(Object.class, ResourceSpec.class, ResourceAllocator.class, ClientSecurityInfo.class) + .withConstructor(ManagedConnection.class, ResourceSpec.class, ResourceAllocator.class) // Actual constructor arguments does not matter - .withArgs(null, null, null, null) + .withArgs(null, null, null) .createNiceMock()); } @@ -107,7 +108,7 @@ public void testAddResource() throws Exception { int resourceCount = RESOURCE_COUNT / 2; int taskCount = TASK_COUNT / 2; - DataStructure dataStructure = new RWLockDataStructure(null, resourceCount, handler, null); + DataStructure dataStructure = new RWLockDataStructure(null, resourceCount, handler); List> tasks = new ArrayList<>(taskCount); for (int i = 0; i < taskCount; i++) { @@ -158,8 +159,8 @@ public void testAddResourceWithException() throws Exception { allocator = createNiceMock(ResourceAllocator.class); ResourceHandle resource = createMockBuilder(ResourceHandle.class) - .withConstructor(Object.class, ResourceSpec.class, ResourceAllocator.class, ClientSecurityInfo.class) - .withArgs(null, null, null, null) + .withConstructor(ManagedConnection.class, ResourceSpec.class, ResourceAllocator.class) + .withArgs(null, null, null) .createNiceMock(); expect(handler.createResource(allocator)).andThrow(new PoolingException()); @@ -169,7 +170,7 @@ public void testAddResourceWithException() throws Exception { replay(resource, handler, allocator); - DataStructure dataStructure = new RWLockDataStructure(null, 1, handler, null); + DataStructure dataStructure = new RWLockDataStructure(null, 1, handler); assertAll( () -> assertThrows(PoolingException.class, () -> dataStructure.addResource(allocator, 1)), @@ -188,7 +189,7 @@ public void testAddResourceWithException() throws Exception { @Timeout(value = 10, threadMode = ThreadMode.SEPARATE_THREAD) public void testGetResource() throws Exception { - DataStructure dataStructure = new RWLockDataStructure(null, RESOURCE_COUNT, handler, null); + DataStructure dataStructure = new RWLockDataStructure(null, RESOURCE_COUNT, handler); assertThat("Add Resources", dataStructure.addResource(allocator, RESOURCE_COUNT), equalTo(RESOURCE_COUNT)); @@ -223,7 +224,7 @@ public void testGetResource() throws Exception { @Timeout(value = 10, threadMode = ThreadMode.SEPARATE_THREAD) public void testReturnResource() throws Exception { - DataStructure dataStructure = new RWLockDataStructure(null, RESOURCE_COUNT, handler, null); + DataStructure dataStructure = new RWLockDataStructure(null, RESOURCE_COUNT, handler); assertThat("Add Resources", dataStructure.addResource(allocator, RESOURCE_COUNT), equalTo(RESOURCE_COUNT)); assertThat("Free List Size", dataStructure.getFreeListSize(), equalTo(RESOURCE_COUNT)); @@ -261,7 +262,7 @@ public void testReturnResource() throws Exception { @Timeout(value = 10, threadMode = ThreadMode.SEPARATE_THREAD) public void testRemoveResource() throws Exception { - DataStructure dataStructure = new RWLockDataStructure(null, RESOURCE_COUNT, handler, null); + DataStructure dataStructure = new RWLockDataStructure(null, RESOURCE_COUNT, handler); assertThat("Add Resources", dataStructure.addResource(allocator, RESOURCE_COUNT), equalTo(RESOURCE_COUNT)); @@ -295,7 +296,7 @@ public void testRemoveResource() throws Exception { @Test public void testRemoveAll() throws PoolingException { - DataStructure dataStructure = new RWLockDataStructure(null, RESOURCE_COUNT, handler, null); + DataStructure dataStructure = new RWLockDataStructure(null, RESOURCE_COUNT, handler); dataStructure.addResource(allocator, RESOURCE_COUNT); assertThat("Resources Size", dataStructure.getResourcesSize(), equalTo(RESOURCE_COUNT)); @@ -313,7 +314,7 @@ public void testRemoveAll() throws PoolingException { @Timeout(value = 10, threadMode = ThreadMode.SEPARATE_THREAD) public void testRaceConditions() throws Exception { - RWLockDataStructure dataStructure = new RWLockDataStructure(null, RESOURCE_COUNT, handler, null); + RWLockDataStructure dataStructure = new RWLockDataStructure(null, RESOURCE_COUNT, handler); for (int i = 0; i < RESOURCE_COUNT; i++) { // requires handler.createResource(allocator) diff --git a/appserver/connectors/connectors-runtime/src/test/java/com/sun/enterprise/resource/pool/mock/JavaEETransactionManagerMock.java b/appserver/connectors/connectors-runtime/src/test/java/com/sun/enterprise/resource/pool/mock/JavaEETransactionManagerMock.java new file mode 100644 index 00000000000..4cfcc6895b1 --- /dev/null +++ b/appserver/connectors/connectors-runtime/src/test/java/com/sun/enterprise/resource/pool/mock/JavaEETransactionManagerMock.java @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2024 Eclipse Foundation 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.resource.pool.mock; + +import com.sun.enterprise.transaction.api.JavaEETransaction; +import com.sun.enterprise.transaction.api.XAResourceWrapper; +import com.sun.enterprise.transaction.spi.JavaEETransactionManagerDelegate; +import com.sun.enterprise.transaction.spi.TransactionalResource; + +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.Synchronization; +import jakarta.transaction.SystemException; +import jakarta.transaction.Transaction; + +import java.rmi.RemoteException; +import java.util.ArrayList; +import java.util.List; + +import javax.transaction.xa.XAResource; +import javax.transaction.xa.Xid; + +import org.glassfish.api.invocation.ComponentInvocation; +import org.glassfish.api.invocation.InvocationException; +import org.glassfish.api.invocation.ResourceHandler; + +/** + * Mock class without any implementation + */ +public class JavaEETransactionManagerMock implements com.sun.enterprise.transaction.api.JavaEETransactionManager { + + @Override + public void begin() throws NotSupportedException, SystemException { + } + + @Override + public void commit() + throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, IllegalStateException, SystemException { + } + + @Override + public int getStatus() throws SystemException { + return 0; + } + + @Override + public Transaction getTransaction() throws SystemException { + return null; + } + + @Override + public void resume(Transaction tobj) throws InvalidTransactionException, IllegalStateException, SystemException { + } + + @Override + public void rollback() throws IllegalStateException, SecurityException, SystemException { + } + + @Override + public void setRollbackOnly() throws IllegalStateException, SystemException { + } + + @Override + public void setTransactionTimeout(int seconds) throws SystemException { + } + + @Override + public Transaction suspend() throws SystemException { + return null; + } + + @Override + public void registerSynchronization(Synchronization sync) throws RollbackException, IllegalStateException, SystemException { + } + + @Override + public boolean enlistResource(Transaction tran, TransactionalResource h) throws RollbackException, IllegalStateException, SystemException { + return false; + } + + @Override + public boolean delistResource(Transaction tran, TransactionalResource h, int flag) throws IllegalStateException, SystemException { + return false; + } + + @Override + public void enlistComponentResources() throws RemoteException { + } + + @Override + public void delistComponentResources(boolean suspend) throws RemoteException { + } + + @Override + public void componentDestroyed(Object instance, ComponentInvocation inv) { + } + + @Override + public void componentDestroyed(Object instance) { + } + + @Override + public void componentDestroyed(ResourceHandler rh) { + } + + @Override + public void preInvoke(ComponentInvocation prev) throws InvocationException { + } + + @Override + public void postInvoke(ComponentInvocation curr, ComponentInvocation prev) throws InvocationException { + } + + @Override + public void setDefaultTransactionTimeout(int seconds) { + } + + @Override + public void cleanTxnTimeout() { + } + + @Override + public List getExistingResourceList(Object instance, ComponentInvocation inv) { + return null; + } + + @Override + public void registerComponentResource(TransactionalResource h) { + } + + @Override + public void unregisterComponentResource(TransactionalResource h) { + } + + @Override + public void recover(XAResource[] resourceList) { + } + + @Override + public void initRecovery(boolean force) { + } + + @Override + public void shutdown() { + } + + @Override + public void begin(int timeout) throws NotSupportedException, SystemException { + } + + @Override + public boolean isNullTransaction() { + return false; + } + + @Override + public void checkTransactionExport(boolean isLocal) { + } + + @Override + public void checkTransactionImport() { + } + + @Override + public boolean isTimedOut() { + return false; + } + + @Override + public ArrayList getActiveTransactions() { + return null; + } + + @Override + public void forceRollback(String txnId) throws IllegalStateException, SystemException { + } + + @Override + public void setMonitoringEnabled(boolean enabled) { + } + + @Override + public void freeze() { + } + + @Override + public void unfreeze() { + } + + @Override + public boolean isFrozen() { + return false; + } + + @Override + public void recreate(Xid xid, long timeout) throws WorkException { + } + + @Override + public void release(Xid xid) throws WorkException { + } + + @Override + public XATerminator getXATerminator() { + return null; + } + + @Override + public void setDelegate(JavaEETransactionManagerDelegate delegate) { + } + + @Override + public JavaEETransaction getCurrentTransaction() { + return null; + } + + @Override + public void setCurrentTransaction(JavaEETransaction tx) { + } + + @Override + public XAResourceWrapper getXAResourceWrapper(String clName) { + return null; + } + + @Override + public void handlePropertyUpdate(String name, Object value) { + } + + @Override + public boolean recoverIncompleteTx(boolean delegated, String logPath, XAResource[] xaresArray) throws Exception { + return false; + } + + @Override + public List getResourceList(Object instance, ComponentInvocation inv) { + return null; + } + + @Override + public void clearThreadTx() { + } + + @Override + public String getTxLogLocation() { + return null; + } + + @Override + public void registerRecoveryResourceHandler(XAResource xaResource) { + } + + @Override + public int getPurgeCancelledTtransactionsAfter() { + return 0; + } + + @Override + public void setPurgeCancelledTtransactionsAfter(int value) { + } +} diff --git a/appserver/connectors/connectors-runtime/src/test/java/com/sun/enterprise/resource/pool/mock/JavaEETransactionMock.java b/appserver/connectors/connectors-runtime/src/test/java/com/sun/enterprise/resource/pool/mock/JavaEETransactionMock.java new file mode 100644 index 00000000000..9e05437b280 --- /dev/null +++ b/appserver/connectors/connectors-runtime/src/test/java/com/sun/enterprise/resource/pool/mock/JavaEETransactionMock.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2024 Eclipse Foundation 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.resource.pool.mock; + +import com.sun.enterprise.transaction.api.JavaEETransaction; +import com.sun.enterprise.transaction.api.SimpleResource; +import com.sun.enterprise.transaction.spi.TransactionalResource; + +import jakarta.persistence.EntityManagerFactory; +import jakarta.transaction.HeuristicMixedException; +import jakarta.transaction.HeuristicRollbackException; +import jakarta.transaction.RollbackException; +import jakarta.transaction.Synchronization; +import jakarta.transaction.SystemException; + +import java.util.Set; + +import javax.transaction.xa.XAResource; + +/** + * Mock class without any implementation + */ +public class JavaEETransactionMock implements JavaEETransaction { + + @Override + public void commit() + throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, IllegalStateException, SystemException { + } + + @Override + public boolean delistResource(XAResource xaRes, int flag) throws IllegalStateException, SystemException { + return false; + } + + @Override + public boolean enlistResource(XAResource xaRes) throws RollbackException, IllegalStateException, SystemException { + return false; + } + + @Override + public int getStatus() throws SystemException { + return 0; + } + + @Override + public void registerSynchronization(Synchronization sync) throws RollbackException, IllegalStateException, SystemException { + } + + @Override + public void rollback() throws IllegalStateException, SystemException { + } + + @Override + public void setRollbackOnly() throws IllegalStateException, SystemException { + } + + @Override + public SimpleResource getExtendedEntityManagerResource(EntityManagerFactory factory) { + return null; + } + + @Override + public SimpleResource getTxEntityManagerResource(EntityManagerFactory factory) { + return null; + } + + @Override + public void addTxEntityManagerMapping(EntityManagerFactory factory, SimpleResource em) { + } + + @Override + public void addExtendedEntityManagerMapping(EntityManagerFactory factory, SimpleResource em) { + } + + @Override + public void removeExtendedEntityManagerMapping(EntityManagerFactory factory) { + } + + @Override + public void setContainerData(T data) { + } + + @Override + public T getContainerData() { + return null; + } + + @Override + public Set getAllParticipatingPools() { + return null; + } + + @Override + public Set getResources(Object poolInfo) { + return null; + } + + @Override + public TransactionalResource getLAOResource() { + return null; + } + + @Override + public void setLAOResource(TransactionalResource h) { + } + + @Override + public TransactionalResource getNonXAResource() { + return null; + } + + @Override + public void setResources(Set resources, Object poolInfo) { + } + + @Override + public boolean isLocalTx() { + return false; + } + + @Override + public boolean isTimedOut() { + return false; + } +} diff --git a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EJBContainerTransactionManager.java b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EJBContainerTransactionManager.java index c0a2784f01f..a03a1d0fc08 100755 --- a/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EJBContainerTransactionManager.java +++ b/appserver/ejb/ejb-container/src/main/java/com/sun/ejb/containers/EJBContainerTransactionManager.java @@ -136,6 +136,10 @@ int findTxAttr(MethodDescriptor md) { * Handle transaction requirements, if any, before invoking bean method */ final void preInvokeTx(EjbInvocation inv) throws Exception { + if (_logger.isLoggable(Level.FINE)) { + _logger.fine("preInvokeTx START, inv=" + inv); + } + // Get existing Tx status: this tells us if the client // started a transaction which was propagated on this invocation. Integer preInvokeTxStatus = inv.getPreInvokeTxStatus(); @@ -162,6 +166,9 @@ final void preInvokeTx(EjbInvocation inv) throws Exception { throw new EJBException(ex); } } + if (_logger.isLoggable(Level.FINE)) { + _logger.fine("preInvokeTx END (1), inv=" + inv); + } return; } @@ -275,6 +282,10 @@ final void preInvokeTx(EjbInvocation inv) throws Exception { default: throw new EJBException("Bad transaction attribute"); } + + if (_logger.isLoggable(Level.FINE)) { + _logger.fine("preInvokeTx END (2), inv=" + inv); + } } /** @@ -282,6 +293,9 @@ final void preInvokeTx(EjbInvocation inv) throws Exception { * no-op in those containers that do not need this callback */ private void startNewTx(Transaction prevTx, EjbInvocation inv) throws Exception { + if (_logger.isLoggable(Level.FINE)) { + _logger.fine("startNewTx START, inv=" + inv + "\n prevTx=" + prevTx); + } container.checkUnfinishedTx(prevTx, inv); @@ -313,12 +327,19 @@ private void startNewTx(Transaction prevTx, EjbInvocation inv) throws Exception // a Synchronization object with the TM, the afterCompletion // will get called. container.afterBegin(context); + + if (_logger.isLoggable(Level.FINE)) { + _logger.fine("startNewTx END, inv=" + inv); + } } /** * Use caller transaction to execute a bean method */ protected void useClientTx(Transaction prevTx, EjbInvocation inv) { + if (_logger.isLoggable(Level.FINE)) { + _logger.fine("useClientTx START, inv=" + inv + "\n prevTx=" + prevTx); + } Transaction clientTx; int status=-1; int prevStatus=-1; @@ -410,12 +431,19 @@ protected void useClientTx(Transaction prevTx, EjbInvocation inv) { } } } + + if (_logger.isLoggable(Level.FINE)) { + _logger.fine("useClientTx END, inv=" + inv); + } } /** * Handle transaction requirements, if any, after invoking bean method */ protected void postInvokeTx(EjbInvocation inv) throws Exception { + if (_logger.isLoggable(Level.FINE)) { + _logger.fine("postInvokeTx START, inv=" + inv); + } Throwable exception = inv.exception; @@ -436,6 +464,9 @@ protected void postInvokeTx(EjbInvocation inv) throws Exception { inv.exception = ((BaseContainer.PreInvokeException)exception).exception; } + if (_logger.isLoggable(Level.FINE)) { + _logger.fine("postInvokeTx END (1), inv=" + inv); + } return; } @@ -533,6 +564,10 @@ protected void postInvokeTx(EjbInvocation inv) throws Exception { // XXX If any of the TM commit/rollback/suspend calls throws an // exception, should the transaction be rolled back if not already so ? + + if (_logger.isLoggable(Level.FINE)) { + _logger.fine("postInvokeTx END (2), inv=" + inv); + } } final UserTransaction getUserTransaction() { @@ -664,6 +699,10 @@ Throwable checkExceptionClientTx(EJBContextImpl context, Throwable exception) th // this is the counterpart of startNewTx private Throwable completeNewTx(EJBContextImpl context, Throwable exception, int status) throws Exception { + if (_logger.isLoggable(Level.FINE)) { + _logger.fine("completeNewTx START, context=" + context + ", status=" + status + ", exception=" + exception); + } + Throwable newException = exception; if (exception instanceof BaseContainer.PreInvokeException) { newException = ((BaseContainer.PreInvokeException) exception).exception; @@ -672,6 +711,10 @@ private Throwable completeNewTx(EJBContextImpl context, Throwable exception, int if (status == Status.STATUS_NO_TRANSACTION) { // no tx was started, probably an exception was thrown // before tm.begin() was called + + if (_logger.isLoggable(Level.FINE)) { + _logger.fine("completeNewTx END (1), context=" + context); + } return newException; } @@ -684,6 +727,10 @@ private Throwable completeNewTx(EJBContextImpl context, Throwable exception, int // EJB2.0 section 18.3.1, Table 15 // Rollback the Tx we started destroyBeanAndRollback(context, null); + + if (_logger.isLoggable(Level.FINE)) { + _logger.fine("completeNewTx END (2), context=" + context); + } return processSystemException(newException); } try { @@ -705,15 +752,25 @@ private Throwable completeNewTx(EJBContextImpl context, Throwable exception, int transactionManager.commit(); } } + + if (_logger.isLoggable(Level.FINE)) { + _logger.fine("completeNewTx END (3), context=" + context); + } return newException; } catch (RollbackException ex) { _logger.log(Level.FINE, "ejb.transaction_abort_exception", ex); // EJB2.0 section 18.3.6 + if (_logger.isLoggable(Level.FINE)) { + _logger.fine("completeNewTx END (4), context=" + context); + } return new EJBException("Transaction aborted", ex); } catch (Exception ex) { _logger.log(Level.FINE, "ejb.cmt_exception", ex); // Commit or rollback failed. // EJB2.0 section 18.3.6 + if (_logger.isLoggable(Level.FINE)) { + _logger.fine("completeNewTx END (5), context=" + context); + } return new EJBException("Unable to complete" + " container-managed transaction.", ex); } } diff --git a/appserver/jdbc/jdbc-ra/jdbc-core/src/main/java/com/sun/gjc/spi/ManagedConnectionImpl.java b/appserver/jdbc/jdbc-ra/jdbc-core/src/main/java/com/sun/gjc/spi/ManagedConnectionImpl.java index 4cae1c32d60..1bc418f6d44 100644 --- a/appserver/jdbc/jdbc-ra/jdbc-core/src/main/java/com/sun/gjc/spi/ManagedConnectionImpl.java +++ b/appserver/jdbc/jdbc-ra/jdbc-core/src/main/java/com/sun/gjc/spi/ManagedConnectionImpl.java @@ -714,7 +714,7 @@ void checkIfValid() throws ResourceException { * the connection handle and sends a CONNECTION_CLOSED event to all the * registered event listeners. * - * @param e Exception that may have occured while closing the connection handle + * @param e Exception that may have occurred while closing the connection handle * @param connHolder30Object ConnectionHolder30 that has been * closed * @throws SQLException in case closing the sql connection got out of diff --git a/appserver/jdbc/jdbc-ra/jdbc-core/src/main/java/com/sun/gjc/spi/base/ConnectionHolder.java b/appserver/jdbc/jdbc-ra/jdbc-core/src/main/java/com/sun/gjc/spi/base/ConnectionHolder.java index 92f5dbde5b2..cbf2a13e0a6 100644 --- a/appserver/jdbc/jdbc-ra/jdbc-core/src/main/java/com/sun/gjc/spi/base/ConnectionHolder.java +++ b/appserver/jdbc/jdbc-ra/jdbc-core/src/main/java/com/sun/gjc/spi/base/ConnectionHolder.java @@ -179,6 +179,10 @@ public void clearWarnings() throws SQLException { * @throws SQLException In case of a database error. */ public void close() throws SQLException { + if (_logger.isLoggable(Level.FINE)) { + _logger.log(Level.FINE, "ConnectionHolder.close() START managedConnectionImpl=" + managedConnectionImpl); + } + if (isClosed) { if (_logger.isLoggable(Level.FINE)) { _logger.log(Level.FINE, "jdbc.duplicate_close_connection", this); @@ -192,6 +196,10 @@ public void close() throws SQLException { // and has not been associated yet or has been disassociated managedConnectionImpl.connectionClosed(null, this); } + + if (_logger.isLoggable(Level.FINE)) { + _logger.log(Level.FINE, "ConnectionHolder.close() END managedConnectionImpl=" + managedConnectionImpl); + } } /** diff --git a/appserver/transaction/internal-api/src/main/java/com/sun/enterprise/transaction/api/JavaEETransaction.java b/appserver/transaction/internal-api/src/main/java/com/sun/enterprise/transaction/api/JavaEETransaction.java index 13ff58305ba..9f67c10d8b8 100644 --- a/appserver/transaction/internal-api/src/main/java/com/sun/enterprise/transaction/api/JavaEETransaction.java +++ b/appserver/transaction/internal-api/src/main/java/com/sun/enterprise/transaction/api/JavaEETransaction.java @@ -29,7 +29,7 @@ public interface JavaEETransaction extends Transaction { - public SimpleResource getExtendedEntityManagerResource(EntityManagerFactory factory); + public SimpleResource getExtendedEntityManagerResource(EntityManagerFactory factory); public SimpleResource getTxEntityManagerResource(EntityManagerFactory factory); @@ -45,6 +45,13 @@ public interface JavaEETransaction public Set getAllParticipatingPools(); + /** + * Returns the resources enlisted in this transaction.
+ * Note: this getter is also used to delist / unenlist resources from this transaction instance. + * + * @param poolInfo the pool identifier for which the resources need to be returned + * @return a Set of resources associated to this transaction. + */ public Set getResources(Object poolInfo); public TransactionalResource getLAOResource(); diff --git a/appserver/transaction/internal-api/src/main/java/com/sun/enterprise/transaction/api/JavaEETransactionManager.java b/appserver/transaction/internal-api/src/main/java/com/sun/enterprise/transaction/api/JavaEETransactionManager.java index a1550d7556f..3690b1fb34f 100644 --- a/appserver/transaction/internal-api/src/main/java/com/sun/enterprise/transaction/api/JavaEETransactionManager.java +++ b/appserver/transaction/internal-api/src/main/java/com/sun/enterprise/transaction/api/JavaEETransactionManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Contributors to the Eclipse Foundation + * Copyright (c) 2022, 2024 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 @@ -49,7 +49,7 @@ public interface JavaEETransactionManager extends TransactionManager { /** - * register a synchronization object with the transaction + * Register a synchronization object with the transaction * associated with the current thread * * @param sync the synchronization object @@ -68,12 +68,9 @@ void registerSynchronization(Synchronization sync) /** * Enlist the resource specified with the transaction * - * * @param tran The transaction object - * * @param h The resource handle object - * - * @return true if the resource was enlisted successfully; otherwise * false. + * @return true if the resource was enlisted successfully; otherwise false. * * @exception RollbackException Thrown to indicate that * the transaction has been marked for rollback only. @@ -94,10 +91,9 @@ boolean enlistResource(Transaction tran, * Delist the resource specified from the transaction * * @param tran The transaction object - * * @param h The resource handle object - * * @param flag One of the values of TMSUCCESS, TMSUSPEND, or TMFAIL. + * @return true if the resource was delisted successfully; otherwise false. * * @exception IllegalStateException Thrown if the transaction in the * target object is inactive. diff --git a/appserver/transaction/internal-api/src/main/java/com/sun/enterprise/transaction/spi/TransactionalResource.java b/appserver/transaction/internal-api/src/main/java/com/sun/enterprise/transaction/spi/TransactionalResource.java index a6206d8a783..c57c6af32a2 100644 --- a/appserver/transaction/internal-api/src/main/java/com/sun/enterprise/transaction/spi/TransactionalResource.java +++ b/appserver/transaction/internal-api/src/main/java/com/sun/enterprise/transaction/spi/TransactionalResource.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2022, 2024 Contributors to the Eclipse Foundation. * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the @@ -21,44 +22,91 @@ import javax.transaction.xa.XAResource; /** - * TransactionalResource interface to be implemented by the resource handlers - * to be able to communicate with used the transaction manager components + * TransactionalResource interface to be implemented by the resource handlers to be able to communicate with the + * transaction manager components. * * @author Marina Vatkina */ - public interface TransactionalResource { + /** + * Returns true if the resource is part of a transaction. + * + * @return true if the resource is part of a transaction. + */ public boolean isTransactional(); - //TODO V3 not needed as of now. + /** + * To check whether lazy enlistment is suspended or not.
+ * If {@code true}, transaction manager will not do enlist/lazy enlist. + * + * @return true if enlistment is suspended, otherwise false. + */ public boolean isEnlistmentSuspended(); + /** + * Returns the (optional) XAResource reference for this resource handle. + * + * @return the XAResource reference for this resource handle or null if no reference is set. + */ public XAResource getXAResource(); + /** + * Returns true if the ResourceHandle is supported in an XA transaction. + * + * @return true if the ResourceHandle is supported in an XA transaction, otherwise false. + */ public boolean supportsXA(); + /** + * Returns the component instance holding this resource handle. + * + * @return the component instance holding this resource handle. + */ public Object getComponentInstance(); + /** + * Sets the component instance holding this resource handle. + * + * @param instance the component instance holding this resource handle. + */ public void setComponentInstance(Object instance); + /** + * Closes the (optional) 'userConnection' / 'connection handle' (used by the application code to refer to the underlying + * physical connection). Example: the ManagedConnection represented by this ResourceHandle is closed / cleaned up. + * + * @throws PoolingException wrapping any 'userConnection' specific exception that might occur during the close call. + * @throws UnsupportedOperationException when the method is not implemented. + */ public void closeUserConnection() throws Exception; + /** + * Returns true if the resource handle is enlisted in a transaction. + * + * @return true if the resource handle is enlisted in a transaction. + */ public boolean isEnlisted(); + /** + * Returns true if the resource handle is sharable within the component. + * + * @return true if the resource handle is sharable within the component. + */ public boolean isShareable(); - public void destroyResource(); - /** - * @return the String that can identify this resource + * Returns the String that can identify this resource. + * + * @return the String that can identify this resource. */ public String getName(); /** * Indicates that a resource has been enlisted in the transaction. - * @param tran Transaction to which the resource is enlisted - * @throws IllegalStateException when unable to enlist the resource + * + * @param transaction the Transaction to which the resource is enlisted. + * @throws IllegalStateException when unable to enlist the resource. */ - void enlistedInTransaction(Transaction tran) throws IllegalStateException; + void enlistedInTransaction(Transaction transaction) throws IllegalStateException; } diff --git a/appserver/transaction/jta/src/main/java/com/sun/enterprise/transaction/JavaEETransactionImpl.java b/appserver/transaction/jta/src/main/java/com/sun/enterprise/transaction/JavaEETransactionImpl.java index 966f39aec10..4982e8199d6 100644 --- a/appserver/transaction/jta/src/main/java/com/sun/enterprise/transaction/JavaEETransactionImpl.java +++ b/appserver/transaction/jta/src/main/java/com/sun/enterprise/transaction/JavaEETransactionImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Contributors to the Eclipse Foundation + * Copyright (c) 2021, 2024 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 @@ -48,7 +48,7 @@ import javax.transaction.xa.Xid; /** - * This class implements the JTA Transaction API for the J2EE RI. It is a wrapper over the JTS Transaction object that + * This class implements the JTA Transaction API for the JEE RI. It is a wrapper over the JTS Transaction object that * provides optimized local transaction support when a transaction uses zero/one non-XA resource, and delegates to JTS * otherwise. This object can be in two states: local tx (jtsTx==null) or global (JTS) tx. If jtsTx!=null, all calls are * delegated to jtsTx. @@ -60,12 +60,12 @@ */ public final class JavaEETransactionImpl extends TimerTask implements JavaEETransaction { - static Logger _logger = LogDomains.getLogger(JavaEETransactionImpl.class, LogDomains.JTA_LOGGER); + private static Logger _logger = LogDomains.getLogger(JavaEETransactionImpl.class, LogDomains.JTA_LOGGER); // Sting Manager for Localization private static StringManager sm = StringManager.getManager(JavaEETransactionImpl.class); - JavaEETransactionManager javaEETM; + private JavaEETransactionManager javaEETM; // Local Tx ids are just numbers: they dont need to be unique across // processes or across multiple activations of this server process. @@ -78,7 +78,15 @@ public final class JavaEETransactionImpl extends TimerTask implements JavaEETran private long txId; private JavaEEXid xid; private TransactionInternal jtsTx; + + /** + * Non XA transaction resources + */ private TransactionalResource nonXAResource; + + /** + * use-last-agent-optimization resource + */ private TransactionalResource laoResource; private int localTxStatus; private Vector syncs = new Vector(); @@ -95,7 +103,11 @@ public final class JavaEETransactionImpl extends TimerTask implements JavaEETran // END: local transaction timeout private boolean imported = false; - private HashMap resourceTable; + /** + * Maps PoolInfo to resources + */ + private HashMap resourceTable; + private HashMap userResourceMap; // This cache contains the EntityContexts in this Tx @@ -646,7 +658,7 @@ public void registerSynchronization(Synchronization sync) throws RollbackExcepti // START OF IASRI 4660742 if (_logger.isLoggable(Level.FINE)) { _logger.log(Level.FINE, - "--In JavaEETransactionImpl.registerSynchronization, jtsTx=" + jtsTx + " nonXAResource=" + nonXAResource); + "--In JavaEETransactionImpl.registerSynchronization START, jtsTx=" + jtsTx + ", nonXAResource=" + nonXAResource + ", sync=" + sync); } // END OF IASRI 4660742 @@ -655,6 +667,11 @@ public void registerSynchronization(Synchronization sync) throws RollbackExcepti jtsTx.registerSynchronization(sync); else syncs.add(sync); + + if (_logger.isLoggable(Level.FINE)) { + _logger.log(Level.FINE, + "--In JavaEETransactionImpl.registerSynchronization END, jtsTx=" + jtsTx + ", nonXAResource=" + nonXAResource + ", sync=" + sync); + } } public void setRollbackOnly() throws IllegalStateException, SystemException { @@ -815,4 +832,4 @@ public int getRemainingTimeout() { } } -} +} \ No newline at end of file diff --git a/appserver/transaction/jta/src/main/java/com/sun/enterprise/transaction/JavaEETransactionManagerSimplified.java b/appserver/transaction/jta/src/main/java/com/sun/enterprise/transaction/JavaEETransactionManagerSimplified.java index 8a0f764fa0c..a922693bb59 100644 --- a/appserver/transaction/jta/src/main/java/com/sun/enterprise/transaction/JavaEETransactionManagerSimplified.java +++ b/appserver/transaction/jta/src/main/java/com/sun/enterprise/transaction/JavaEETransactionManagerSimplified.java @@ -1404,13 +1404,6 @@ private void handleResourceError(TransactionalResource h, Exception ex, Transact } catch (Exception ex2) { // Log.err.println(ex2); } - } else { - // destroy resource. RM Error. - try { - h.destroyResource(); - } catch (Exception ex2) { - // Log.err.println(ex2); - } } } diff --git a/appserver/transaction/jts/src/main/java/com/sun/enterprise/transaction/jts/JavaEETransactionManagerJTSDelegate.java b/appserver/transaction/jts/src/main/java/com/sun/enterprise/transaction/jts/JavaEETransactionManagerJTSDelegate.java index 8c378c0310b..6cc2702fb65 100644 --- a/appserver/transaction/jts/src/main/java/com/sun/enterprise/transaction/jts/JavaEETransactionManagerJTSDelegate.java +++ b/appserver/transaction/jts/src/main/java/com/sun/enterprise/transaction/jts/JavaEETransactionManagerJTSDelegate.java @@ -102,6 +102,9 @@ public class JavaEETransactionManagerJTSDelegate implements JavaEETransactionMan private Logger logger; + /** + * use-last-agent-optimization + */ private boolean lao = true; private volatile TransactionManager transactionManagerImpl; @@ -140,7 +143,7 @@ public void setUseLAO(boolean b) { @Override public void commitDistributedTransaction() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, IllegalStateException, SystemException { - logger.log(FINE, "TM: commit"); + logger.log(FINE, "JavaEETransactionManagerJTSDelegate.commitDistributedTransaction START"); validateTransactionManager(); TransactionManager transactionManager = transactionManagerLocal.get(); @@ -172,6 +175,7 @@ public void commitDistributedTransaction() throws RollbackException, HeuristicMi javaEETMS.setTransactionCompeting(false); } } + logger.log(FINE, "JavaEETransactionManagerJTSDelegate.commitDistributedTransaction END"); } /** @@ -179,7 +183,7 @@ public void commitDistributedTransaction() throws RollbackException, HeuristicMi */ @Override public void rollbackDistributedTransaction() throws IllegalStateException, SecurityException, SystemException { - logger.log(FINE, "TM: rollback"); + logger.log(FINE, "JavaEETransactionManagerJTSDelegate.rollbackDistributedTransaction START"); validateTransactionManager(); TransactionManager transactionManager = transactionManagerLocal.get(); @@ -201,6 +205,7 @@ public void rollbackDistributedTransaction() throws IllegalStateException, Secur } finally { javaEETMS.monitorTxCompleted(obj, false); } + logger.log(FINE, "JavaEETransactionManagerJTSDelegate.rollbackDistributedTransaction END"); } @Override @@ -216,7 +221,8 @@ public int getStatus() throws SystemException { } if (logger.isLoggable(FINE)) { - logger.log(FINE, "TM: status: " + JavaEETransactionManagerSimplified.getStatusAsString(status)); + logger.log(FINE, "JavaEETransactionManagerJTSDelegate.getStatus, status=" + + JavaEETransactionManagerSimplified.getStatusAsString(status)); } return status; @@ -226,10 +232,15 @@ public int getStatus() throws SystemException { public Transaction getTransaction() throws SystemException { JavaEETransaction javaEETransaction = javaEETransactionManager.getCurrentTransaction(); if (logger.isLoggable(FINE)) { - logger.log(FINE, "TM: getTransaction: tx=" + javaEETransaction + ", tm=" + transactionManagerLocal.get()); + logger.log(FINE, "JavaEETransactionManagerJTSDelegate.getTransaction START: tx=" + javaEETransaction + + ", tm=" + transactionManagerLocal.get()); } if (javaEETransaction != null) { + if (logger.isLoggable(FINE)) { + logger.log(FINE, "JavaEETransactionManagerJTSDelegate: getTransaction END (1): tx=" + javaEETransaction + + ", tm=" + transactionManagerLocal.get()); + } return javaEETransaction; } @@ -241,13 +252,18 @@ public Transaction getTransaction() throws SystemException { } if (jtsTx == null) { + if (logger.isLoggable(FINE)) { + logger.log(FINE, "JavaEETransactionManagerJTSDelegate.getTransaction END (2): tx=" + javaEETransaction + + ", tm=" + transactionManagerLocal.get()); + } return null; } // Check if this JTS Transaction was previously active in this JVM (possible for distributed loopbacks). javaEETransaction = (JavaEETransaction) globalTransactions.get(jtsTx); if (logger.isLoggable(FINE)) { - logger.log(FINE, "TM: getTransaction: tx=" + javaEETransaction + ", jtsTx=" + jtsTx); + logger.log(FINE, "JavaEETransactionManagerJTSDelegate.getTransaction: tx=" + javaEETransaction + ", jtsTx=" + + jtsTx); } if (javaEETransaction == null) { @@ -256,6 +272,11 @@ public Transaction getTransaction() throws SystemException { } javaEETransactionManager.setCurrentTransaction(javaEETransaction); // associate tx with thread + + if (logger.isLoggable(FINE)) { + logger.log(FINE, "JavaEETransactionManagerJTSDelegate.getTransaction END (3): tx=" + javaEETransaction + + ", tm=" + transactionManagerLocal.get()); + } return javaEETransaction; } @@ -313,10 +334,12 @@ public boolean enlistLAOResource(Transaction transaction, TransactionalResource @Override public void setRollbackOnlyDistributedTransaction() throws IllegalStateException, SystemException { - logger.log(FINE, "TM: setRollbackOnly"); + logger.log(FINE, "JavaEETransactionManagerJTSDelegate.setRollbackOnlyDistributedTransaction START"); validateTransactionManager(); transactionManagerLocal.get().setRollbackOnly(); + + logger.log(FINE, "JavaEETransactionManagerJTSDelegate.setRollbackOnlyDistributedTransaction END"); } @Override @@ -339,12 +362,14 @@ public Transaction suspend(JavaEETransaction tx) throws SystemException { @Override public void resume(Transaction tx) throws InvalidTransactionException, IllegalStateException, SystemException { - logger.log(FINE, "TM: resume"); + logger.log(FINE, "JavaEETransactionManagerJTSDelegate.resume START, tx=" + tx); if (transactionManagerImpl != null) { setTransactionManager(); transactionManagerLocal.get().resume(tx); } + + logger.log(FINE, "JavaEETransactionManagerJTSDelegate.resume END, tx=" + tx); } @Override @@ -420,7 +445,7 @@ public XATerminator getXATerminator() { } private Transaction suspendXA() throws SystemException { - logger.log(FINE, "TM: suspend"); + logger.log(FINE, "JavaEETransactionManagerJTSDelegate: suspendXA"); validateTransactionManager(); return transactionManagerLocal.get().suspend(); @@ -434,7 +459,8 @@ private void validateTransactionManager() throws IllegalStateException { private void setTransactionManager() { if (logger.isLoggable(FINE)) { - logger.log(FINE, "TM: setTransactionManager: tm=" + transactionManagerLocal.get()); + logger.log(FINE, + "JavaEETransactionManagerJTSDelegate: setTransactionManager: tm=" + transactionManagerLocal.get()); } if (transactionManagerImpl == null) { @@ -498,7 +524,7 @@ public void initTransactionProperties() { if (value != null && "false".equals(value)) { setUseLAO(false); if (logger.isLoggable(FINE)) { - logger.log(FINE, "TM: LAO is disabled"); + logger.log(FINE, "JavaEETransactionManagerJTSDelegate: LAO is disabled"); } } @@ -519,7 +545,8 @@ public void initTransactionProperties() { if (Boolean.parseBoolean(transactionService.getPropertyValue("delegated-recovery"))) { // Register GMS notification callback if (logger.isLoggable(FINE)) { - logger.log(FINE, "TM: Registering for GMS notification callback"); + logger.log(FINE, + "JavaEETransactionManagerJTSDelegate: Registering for GMS notification callback"); } int waitTime = 60; diff --git a/appserver/transaction/jts/src/test/java/com/sun/enterprise/transaction/jts/JavaEETransactionManagerTest.java b/appserver/transaction/jts/src/test/java/com/sun/enterprise/transaction/jts/JavaEETransactionManagerTest.java index 9499143bb31..6fb9e53067d 100644 --- a/appserver/transaction/jts/src/test/java/com/sun/enterprise/transaction/jts/JavaEETransactionManagerTest.java +++ b/appserver/transaction/jts/src/test/java/com/sun/enterprise/transaction/jts/JavaEETransactionManagerTest.java @@ -18,7 +18,6 @@ package com.sun.enterprise.transaction.jts; import com.sun.enterprise.config.serverbeans.ServerTags; -import com.sun.enterprise.resource.ClientSecurityInfo; import com.sun.enterprise.resource.ResourceHandle; import com.sun.enterprise.resource.ResourceSpec; import com.sun.enterprise.resource.allocator.ResourceAllocator; @@ -30,6 +29,7 @@ import com.sun.enterprise.transaction.UserTransactionImpl; import com.sun.enterprise.transaction.spi.JavaEETransactionManagerDelegate; +import jakarta.resource.spi.ManagedConnection; import jakarta.transaction.HeuristicMixedException; import jakarta.transaction.HeuristicRollbackException; import jakarta.transaction.InvalidTransactionException; @@ -1036,12 +1036,12 @@ private int getStatus(String name) { } static class TestResourceHandle extends ResourceHandle { - private final XAResource resource; + private final XAResource xaResource; private static PoolManagerImpl poolMgr = new PoolManagerImpl(); - public TestResourceHandle(XAResource resource) { - super(null,new ResourceSpec(new SimpleJndiName("testResource"),0) ,null,null); - this.resource = resource; + public TestResourceHandle(XAResource xaResource) { + super(null, new ResourceSpec(new SimpleJndiName("testResource"), 0), null); + this.xaResource = xaResource; } @Override @@ -1065,13 +1065,13 @@ public ResourceAllocator getResourceAllocator() { } @Override - public Object getResource() { - return resource; + public ManagedConnection getResource() { + return null; } @Override public XAResource getXAResource() { - return resource; + return xaResource; } @Override @@ -1079,11 +1079,6 @@ public Object getUserConnection() { return null; } - @Override - public ClientSecurityInfo getClientSecurityInfo() { - return null; - } - @Override public void fillInResourceObjects(Object userConnection, XAResource xares) { }