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 390e1144090..6a99a0f12cb 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 @@ -44,6 +44,7 @@ import java.util.List; import java.util.Set; import java.util.Timer; +import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Logger; import javax.naming.NamingException; @@ -67,20 +68,80 @@ public class ConnectionPool implements ResourcePool, ConnectionLeakListener, Res private static final Logger LOG = LogDomains.getLogger(ConnectionPool.class, LogDomains.RSR_LOGGER); // pool life-cycle config properties + /** + * Represents the "max-pool-size" configuration value.
+ * Specifies the maximum number of connections that can be created to satisfy client requests.
+ * Default: 32 + */ protected int maxPoolSize; + + /** + * Represents the "steady-pool-size" configuration value.
+ * Specifies the initial and minimum number of connections maintained in the pool.
+ * Default: 8 + */ protected int steadyPoolSize; - /** used by resizer to downsize the pool */ + + /** + * Represents the "pool-resize-quantity" configuration value.
+ * Specifies the number of idle connections to be destroyed if the existing number of connections is above the + * steady-pool-size (subject to the max-pool-size limit).
+ * This is enforced periodically at the idle-timeout-in-seconds interval. An idle connection is one that has not been + * used for a period of idle-timeout-in-seconds. When the pool size reaches steady-pool-size, connection removal + * stops.
+ * Default: 2 + */ protected int resizeQuantity; - /** The total time a thread is willing to wait for a resource object. */ + + /** + * Represents the "max-wait-time-in-millis" configuration value.
+ * Specifies the amount of time, in milliseconds, that the caller is willing to wait for a connection. If 0, the caller + * is blocked indefinitely until a resource is available or an error occurs.
+ * Default: 60.000ms + */ protected int maxWaitTime; - /** time (in ms) before destroying a free resource */ + + /** + * Represents the "idle-timeout-in-seconds" configuration value, but in millis.
+ * Specifies the maximum time that a connection can remain idle in the pool. After this amount of time, the pool can + * close this connection.
+ * Default: 300.000ms + */ protected long idletime; // pool config properties + /** + * Represents the "fail-all-connections" configuration value.
+ * If true, closes all connections in the pool if a single validation check fails.
+ * Default: false + */ protected boolean failAllConnections; + + /** + * Represents the "match-connections" configuration value.
+ * If true, enables connection matching. You can set to false if connections are homogeneous.
+ * 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; + + /** + * Represents the "prefer-validate-over-recreate property" configuration value.
+ * Specifies that validating idle connections is preferable to closing them. This property has no effect on non-idle + * connections. If set to true, idle connections are validated during pool resizing, and only those found to be invalid + * are destroyed and recreated. If false, all idle connections are destroyed and recreated during pool resizing.
+ * Default: false + */ protected boolean preferValidateOverRecreate; + // hold on to the resizer task so we can cancel/reschedule it. protected Resizer resizerTask; @@ -88,17 +149,55 @@ public class ConnectionPool implements ResourcePool, ConnectionLeakListener, Res protected Timer timer; // advanced pool config properties + /** + * Represents the "connection-creation-retry-attempts" configuration value.
+ * If the value connectionCreationRetryAttempts_ is > 0 then connectionCreationRetry_ is true, otherwise false. + */ protected boolean connectionCreationRetry_; + /** + * Represents the "connection-creation-retry-attempts" configuration.
+ * Specifies the number of attempts to create a new connection.
+ * Default: 0 + */ protected int connectionCreationRetryAttempts_; + + /** + * Represents the "connection-creation-retry-interval-in-seconds" configuration, but in millis.
+ * Specifies the time interval between attempts to create a connection when connection-creation-retry-attempts is + * greater than 0.
+ * Default: 10.000ms + */ protected long conCreationRetryInterval_; + + /** + * 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.
+ * Default: 10.000ms. + */ protected long validateAtmostPeriodInMilliSeconds_; + + /** + * Represents the "max-connection-usage-count" configuration.
+ * Specifies the number of times a connections is reused by the pool, after which it is closed. A zero value disables + * this feature.
+ * Default: 0 + */ protected int maxConnectionUsage_; - // To validate a Sun RA Pool Connection if it hasnot been validated + + // 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 + */ private boolean validateAtmostEveryIdleSecs; + // TODO document protected String resourceSelectionStrategyClass; protected PoolLifeCycleListener poolLifeCycleListener; @@ -129,6 +228,13 @@ public class ConnectionPool implements ResourcePool, ConnectionLeakListener, Res private boolean blocked; + /** + * Reentrant lock to ensure calls to {@link #getResourceFromPool(ResourceAllocator, ResourceSpec)} and + * {@link #freeResource(ResourceHandle)} are not executed at the same time to solve issue 24843, because one is getting + * resources from the pool and possibly resizing the pool while the other is returning resources to the pool. + */ + private final ReentrantLock getResourceFromPoolAndFreeResourceMethodsLock = new ReentrantLock(true); + public ConnectionPool(PoolInfo poolInfo, Hashtable env) throws PoolingException { this.poolInfo = poolInfo; setPoolConfiguration(env); @@ -675,50 +781,55 @@ protected ResourceHandle getResourceFromPool(ResourceAllocator resourceAllocator ResourceHandle resourceHandle; List freeResources = new ArrayList<>(); - try { - while ((resourceHandle = dataStructure.getResource()) != null) { - if (resourceHandle.hasConnectionErrorOccurred()) { - dataStructure.removeResource(resourceHandle); - continue; - } + try { + getResourceFromPoolAndFreeResourceMethodsLock.lock(); + try { + while ((resourceHandle = dataStructure.getResource()) != null) { + if (resourceHandle.hasConnectionErrorOccurred()) { + dataStructure.removeResource(resourceHandle); + continue; + } - if (matchConnection(resourceHandle, resourceAllocator)) { + if (matchConnection(resourceHandle, resourceAllocator)) { - boolean isValid = isConnectionValid(resourceHandle, resourceAllocator); - if (resourceHandle.hasConnectionErrorOccurred() || !isValid) { - if (failAllConnections) { - resourceFromPool = createSingleResourceAndAdjustPool(resourceAllocator, resourceSpec); - // No need to match since the resource is created with the allocator of caller. + boolean isValid = isConnectionValid(resourceHandle, resourceAllocator); + if (resourceHandle.hasConnectionErrorOccurred() || !isValid) { + if (failAllConnections) { + resourceFromPool = createSingleResourceAndAdjustPool(resourceAllocator, resourceSpec); + // No need to match since the resource is created with the allocator of caller. + break; + } + dataStructure.removeResource(resourceHandle); + // Resource is invalid, continue iteration. + continue; + } + if (resourceHandle.isShareable() == resourceAllocator.shareableWithinComponent()) { + // Got a matched, valid resource + resourceFromPool = resourceHandle; break; } - dataStructure.removeResource(resourceHandle); - // Resource is invalid, continue iteration. - continue; - } - if (resourceHandle.isShareable() == resourceAllocator.shareableWithinComponent()) { - // Got a matched, valid resource - resourceFromPool = resourceHandle; - break; + freeResources.add(resourceHandle); + } else { + freeResources.add(resourceHandle); } - freeResources.add(resourceHandle); - } else { - freeResources.add(resourceHandle); } + } finally { + // Return all unmatched, free resources + for (ResourceHandle freeResource : freeResources) { + dataStructure.returnResource(freeResource); + } + freeResources.clear(); } - } finally { - // Return all unmatched, free resources - for (ResourceHandle freeResource : freeResources) { - dataStructure.returnResource(freeResource); - } - freeResources.clear(); - } - if (resourceFromPool != null) { - // Set correct state - setResourceStateToBusy(resourceFromPool); - } else { - resourceFromPool = resizePoolAndGetNewResource(resourceAllocator); + if (resourceFromPool != null) { + // Set correct state + setResourceStateToBusy(resourceFromPool); + } else { + resourceFromPool = resizePoolAndGetNewResource(resourceAllocator); + } + } finally { + getResourceFromPoolAndFreeResourceMethodsLock.unlock(); } return resourceFromPool; @@ -734,26 +845,27 @@ protected ResourceHandle getResourceFromPool(ResourceAllocator resourceAllocator * @throws PoolingException when not able to create resources */ private ResourceHandle resizePoolAndGetNewResource(ResourceAllocator resourceAllocator) throws PoolingException { + // Must be called from the thread holding the lock to this pool. ResourceHandle newResource = null; int numOfConnsToCreate = 0; - if (dataStructure.getResourcesSize() < steadyPoolSize) { + int dataStructureSize = dataStructure.getResourcesSize(); + if (dataStructureSize < steadyPoolSize) { // May be all invalid resources are destroyed as // a result no free resource found and no. of resources is less than steady-pool-size - numOfConnsToCreate = steadyPoolSize - dataStructure.getResourcesSize(); - } else if (dataStructure.getResourcesSize() + resizeQuantity <= maxPoolSize) { - + numOfConnsToCreate = steadyPoolSize - dataStructureSize; + } else if (dataStructureSize + resizeQuantity <= maxPoolSize) { // Create and add resources of quantity "resizeQuantity" numOfConnsToCreate = resizeQuantity; - } else if (dataStructure.getResourcesSize() < maxPoolSize) { - + } else if (dataStructureSize < maxPoolSize) { // This else if "test condition" is not needed. Just to be safe. // still few more connections (less than "resizeQuantity" and to reach the count of maxPoolSize) // can be added - numOfConnsToCreate = maxPoolSize - dataStructure.getResourcesSize(); + numOfConnsToCreate = maxPoolSize - dataStructureSize; } + if (numOfConnsToCreate > 0) { createResources(resourceAllocator, numOfConnsToCreate); newResource = getMatchedResourceFromPool(resourceAllocator); @@ -962,7 +1074,9 @@ public void resourceClosed(ResourceHandle handle) throws IllegalStateException { if (poolLifeCycleListener != null && !handle.getDestroyByLeakTimeOut()) { poolLifeCycleListener.connectionReleased(handle.getId()); } - LOG.log(FINE, "Resource was freed after it`s closure: {0}", handle); + + // Note handle might already be altered by another thread before it is logged! + LOG.log(FINE, "Resource was freed after its closure: {0}", handle); } /** @@ -994,23 +1108,29 @@ protected void freeUnenlistedResource(ResourceHandle h) { } protected void freeResource(ResourceHandle resourceHandle) { - if (cleanupResource(resourceHandle)) { - // Only when resource handle usage count is more than maxConnUsage - if (maxConnectionUsage_ > 0 && resourceHandle.getUsageCount() >= maxConnectionUsage_) { - performMaxConnectionUsageOperation(resourceHandle); - } else { - // Put it back to the free collection. - dataStructure.returnResource(resourceHandle); - // update the monitoring data - if (poolLifeCycleListener != null && !resourceHandle.getDestroyByLeakTimeOut()) { - poolLifeCycleListener.decrementConnectionUsed(resourceHandle.getId()); - poolLifeCycleListener.incrementNumConnFree(false, steadyPoolSize); + try { + getResourceFromPoolAndFreeResourceMethodsLock.lock(); + if (cleanupResource(resourceHandle)) { + // Only when resource handle usage count is more than maxConnUsage + if (maxConnectionUsage_ > 0 && resourceHandle.getUsageCount() >= maxConnectionUsage_) { + performMaxConnectionUsageOperation(resourceHandle); + } else { + // Put it back to the free collection. + dataStructure.returnResource(resourceHandle); + // update the monitoring data + if (poolLifeCycleListener != null && !resourceHandle.getDestroyByLeakTimeOut()) { + poolLifeCycleListener.decrementConnectionUsed(resourceHandle.getId()); + poolLifeCycleListener.incrementNumConnFree(false, steadyPoolSize); + } } + // For both the cases of free.add and maxConUsageOperation, a free resource is added. + // Hence notify waiting threads. + notifyWaitingThreads(); } - // for both the cases of free.add and maxConUsageOperation, a free resource is added. - // Hence notify waiting threads - notifyWaitingThreads(); + } finally { + getResourceFromPoolAndFreeResourceMethodsLock.unlock(); } + } protected boolean cleanupResource(ResourceHandle handle) { @@ -1066,6 +1186,10 @@ public void resourceErrorOccurred(ResourceHandle resourceHandle) throws IllegalS // removed in the next iteration of getUnenlistedResource // or internalGetResource dataStructure.removeResource(resourceHandle); + + // Removing a resource, means a free resource is available for the pool. + // Hence notify waiting threads. + notifyWaitingThreads(); } private void doFailAllConnectionsProcessing() { 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 1b7daba9e1f..e45c5d96214 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 @@ -28,15 +28,22 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; import java.util.Hashtable; import java.util.List; +import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Level; +import java.util.logging.Logger; import org.easymock.IExpectationSetters; import org.glassfish.api.admin.ProcessEnvironment; @@ -46,7 +53,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; -import org.opentest4j.MultipleFailuresError; import com.sun.appserv.connectors.internal.api.ConnectorRuntimeException; import com.sun.appserv.connectors.internal.api.PoolingException; @@ -59,16 +65,23 @@ import com.sun.enterprise.resource.ResourceState; import com.sun.enterprise.resource.allocator.LocalTxConnectorAllocator; import com.sun.enterprise.resource.allocator.ResourceAllocator; -import com.sun.enterprise.resource.pool.datastructure.RWLockDataStructure; +import com.sun.enterprise.resource.pool.datastructure.DataStructure; 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; import jakarta.transaction.Transaction; public class ConnectionPoolTest { + private static final Logger LOG = LogDomains.getLogger(ConnectionPool.class, LogDomains.RSR_LOGGER); + + private static final String NO_AVAILABLE_RESOURCES_AND_WAIT_TIME = "No available resources and wait time "; + private static final String MS_EXPIRED = " ms expired."; + private ManagedConnection managedConnection; private ManagedConnectionFactory managedConnectionFactory; private JavaEETransaction javaEETransaction; @@ -77,6 +90,9 @@ public class ConnectionPoolTest { private ResourceSpec resourceSpec; + // TODO: Look at tests like: org.glassfish.jdbc.devtests.v3.test.MaxConnectionUsageTest and the others in the package, + // they can probably also be made as a unit test here / or in a similar unit test + @BeforeEach public void createAndPopulateMocks() throws PoolingException, ResourceException { List mocks = new ArrayList<>(); @@ -113,11 +129,14 @@ public void createAndPopulateMocks() throws PoolingException, ResourceException connectorRuntime.postConstruct(); } - private void createConnectionPool(int maxPoolSize) throws PoolingException { + 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); - assertEquals(1, connectionPool.getSteadyPoolSize()); + assertEquals(0, connectionPool.getSteadyPoolSize()); assertEquals(maxPoolSize, connectionPool.getMaxPoolSize()); resourceSpec = new ResourceSpec(new SimpleJndiName("myResourceSpec"), ResourceSpec.JNDI_NAME); @@ -129,14 +148,21 @@ private void createConnectionPool(int maxPoolSize) throws PoolingException { */ @Test void basicConnectionPoolTest() throws Exception { - createConnectionPool(2); + // Use the lowest maxWaitTimeInMillis, because this test knows the pool is exhausted on the 3rd call + // for a resource, and we want it to fail asap. Do not use 0, because it means wait indefinitely. + final int maxWaitTimeInMillis = 1; + + // Use poolResizeQuantity 1, to have the easiest understanding and predictability of the test + final int poolResizeQuantity = 1; + + createConnectionPool(2, maxWaitTimeInMillis, poolResizeQuantity); ResourceAllocator alloc = new LocalTxConnectorAllocator(null, managedConnectionFactory, resourceSpec, null, null, null, null, false); Transaction transaction = javaEETransaction; - // Test how many resources are available - // Expect 0 because the pool is not initialized yet. It will be initialized when the first resource is requested + // Test how many resources are available. Expect 0 because the pool is not initialized yet. It will be initialized when + // the first resource is requested and steady pool size is configured as 0. assertResourcesSize(0); // Test getting 3 resources from the pool of 2 @@ -150,9 +176,10 @@ void basicConnectionPoolTest() throws Exception { assertResourceIsBusy(resource2); assertResourcesSize(2); - assertThrows(PoolingException.class, () -> { + PoolingException assertThrows = assertThrows(PoolingException.class, () -> { connectionPool.getResource(resourceSpec, alloc, transaction); }); + assertEquals(NO_AVAILABLE_RESOURCES_AND_WAIT_TIME + maxWaitTimeInMillis + MS_EXPIRED, assertThrows.getMessage()); // Test returning 2 resources to the pool connectionPool.resourceClosed(resource2); @@ -167,96 +194,228 @@ void basicConnectionPoolTest() throws Exception { // Test how many resources are available assertResourcesSize(2); - connectionPool.emptyPool(); + cleanupConnectionPool(); + } + + /** + * Test the "pool-resize-quantity" option + */ + @Test + void poolResizeQuantityTest() throws Exception { + int maximumPoolSize = 5; + poolResizeQuantityTest(maximumPoolSize, 1); + poolResizeQuantityTest(maximumPoolSize, 2); + poolResizeQuantityTest(maximumPoolSize, 5); + + // Test bigger than maximum pool size + poolResizeQuantityTest(maximumPoolSize, 6); + } + + private void poolResizeQuantityTest(int maximumPoolSize, int poolResizeQuantity) throws PoolingException, RetryableUnavailableException { + final int maxWaitTimeInMillis = 1; + createConnectionPool(maximumPoolSize, maxWaitTimeInMillis, poolResizeQuantity); + + ResourceAllocator alloc = new LocalTxConnectorAllocator(null, managedConnectionFactory, resourceSpec, null, + null, null, null, false); + Transaction transaction = javaEETransaction; + + // Test how many resources are available + // Expect 0 because the pool is not initialized yet. It will be initialized when the first resource is requested and + // steady pool size is configured as 0 assertResourcesSize(0); + + // Get 1 resource, and the pool should increase from 0 to poolResizeQuantity, unless it hits the max connection pool + // size. + ResourceHandle resource = connectionPool.getResource(resourceSpec, alloc, transaction); + assertNotNull(resource); + + // Test the "pool-resize-quantity" logic. "poolResizeQuantity" number of new resource should have been added to the + // pool. + if (poolResizeQuantity <= maximumPoolSize) { + assertResourcesSize(poolResizeQuantity); + } else { + // Pool size should not exceed maximumPoolSize + assertResourcesSize(maximumPoolSize); + } + connectionPool.resourceClosed(resource); + + cleanupConnectionPool(); } + /** + * This test is about testing getResource and resourceClosed calls in a multi threaded situation. All calls should + * succeed if the pool size allows the total number of threads to complete within the maxWaitTimeInMillis value. + */ @Test @Timeout(value = 10) void basicConnectionPoolMultiThreadedTest() throws Exception { - int maxConnectionPoolSize = 30; - createConnectionPool(maxConnectionPoolSize); + // Use a low value to try and fill up the whole pool + int maxConnectionPoolSize = 5; + + // In this test the max wait time can be ignored because all threads succeed. + // So the "max-wait-time-in-millis" value is set to 0, to wait indefinitely. + final int maxWaitTimeInMillis = 0; + + // Use poolResizeQuantity 1, to have the easiest understanding and predictability of the test. + // It also means the highest amount of pool resize calls. + final int poolResizeQuantity = 1; + + createConnectionPool(maxConnectionPoolSize, maxWaitTimeInMillis, poolResizeQuantity); ResourceAllocator alloc = new LocalTxConnectorAllocator(null, managedConnectionFactory, resourceSpec, null, null, null, null, false); + // Keep track of resources and number of tasks executed + final Set usedResouceHandles = Collections.synchronizedSet(new HashSet<>()); + AtomicInteger numberOfThreadsFinished = new AtomicInteger(); + + // Make sure there are no resources in the pool before we start processing tasks + assertResourcesSize(0); + int taskCount = 100; List> tasks = new ArrayList<>(taskCount); for (int i = 0; i < taskCount; i++) { tasks.add(() -> { - while (true) { - try { - // Get resource - ResourceHandle resource = connectionPool.getResource(resourceSpec, alloc, javaEETransaction); - assertNotNull(resource); - assertResourceIsBusy(resource); - - // Keep resource a bit occupied, to ensure more than 1 resource from the - // pool is used at the same time - Thread.sleep(0, 10); - // Return resource - connectionPool.resourceClosed(resource); - assertResourceIsNotBusy(resource); - return null; - } catch (PoolingException e) { - // Try again a bit later, should get a connection at one point - Thread.sleep(0, 5); - } + // Get resource. PoolingException should only be possible if the maxWaitTimeInMillis is too low for the amount of tasks. + ResourceHandle resource = connectionPool.getResource(resourceSpec, alloc, javaEETransaction); + + assertNotNull(resource); + assertResourceIsBusy(resource); + + // Keep track of unique resources, we want to validate if resources are being reused + // which is the basic usage of the pool. + boolean isNewlyAdded = usedResouceHandles.add(resource); + if (isNewlyAdded) { + // Adding should only be ok for the first 'maxConnectionPoolSize' number of tasks that start + LOG.log(Level.FINE, "Adding resource: {0}", resource); + } else { + LOG.log(Level.FINE, "Reusing resource: {0}", resource); } + + // Keep resource a bit occupied, to ensure more than 1 resource from the + // pool is used at the same time and the max pool size is reached. + Thread.sleep(0, 10); + + // Return resource, this also eventually calls connectionPool.notifyWaitingThreads(), + // which allows waiting threads to continue when a resource is available. + connectionPool.resourceClosed(resource); + + // Cannot check the resource result here, it will already be re-used by another thread! + // assertResourceIsNotBusy(resource); + + numberOfThreadsFinished.incrementAndGet(); + return null; }); } runTheTasks(tasks); + + // Pool should be filled to maximum pool size. Resources can be idle for "idle-timeout-in-seconds" (which is 789 + // seconds) before they can be removed from the pool. assertResourcesSize(maxConnectionPoolSize); - connectionPool.emptyPool(); - assertResourcesSize(0); + + cleanupConnectionPool(); + + // Just another proof that all 1000 threads finished. + assertEquals(taskCount, numberOfThreadsFinished.get()); + + // No new resources are being used, check the remaining ones for the state. + for (ResourceHandle resource : usedResouceHandles) { + assertResourceIsNotBusy(resource); + } + + // Validate there are no more resources created, than the maximum pool size, because they should be reused + assertTrue(usedResouceHandles.size() <= maxConnectionPoolSize, "failed, size=" + usedResouceHandles.size()); } + /** + * This test is about testing getResource and resourceErrorOccurred calls in a multi threaded situation. All calls + * should be marked as failed and be removed from the pool. + */ @Test @Timeout(value = 10) void resourceErrorOccurredMultiThreadedTest() throws Exception { int maxConnectionPoolSize = 30; - createConnectionPool(maxConnectionPoolSize); + + // In this test the threads are not kept busy, but all are marked as failed. The value is set low, but might be + // dependent on the cpu speed. So it is not set too low. As rule of thumb: the value can be as low as the runtime of + // this test. The lower the 'maxConnectionPoolSize' value or the higher the number of 'taskCount' threads means this + // value needs to increase. + final int maxWaitTimeInMillis = 500; + + // Use poolResizeQuantity 1, to have the easiest understanding and predictability of the test + final int poolResizeQuantity = 1; + + createConnectionPool(maxConnectionPoolSize, maxWaitTimeInMillis, poolResizeQuantity); ResourceAllocator alloc = new LocalTxConnectorAllocator(null, managedConnectionFactory, resourceSpec, null, null, null, null, false); + // Keep track of resources and number of tasks executed + final Set usedResouceHandles = Collections.synchronizedSet(new HashSet<>()); + AtomicInteger numberOfThreadsFinished = new AtomicInteger(); + + // Make sure there are no resources in the pool before we start processing tasks + assertResourcesSize(0); + int taskCount = 100; List> tasks = new ArrayList<>(taskCount); for (int i = 0; i < taskCount; i++) { tasks.add(() -> { - while (true) { - try { - // Get resource - ResourceHandle resource = connectionPool.getResource(resourceSpec, alloc, javaEETransaction); - assertNotNull(resource); - assertResourceIsBusy(resource); - - // Keep resource a bit occupied, to ensure more than 1 resource from the - // pool is used at the same time - Thread.sleep(0, 10); - // Return resource - connectionPool.resourceErrorOccurred(resource); - assertResourceIsNotBusy(resource); - return null; - } catch (PoolingException e) { - // Try again a bit later, should get a connection at one point - Thread.sleep(0, 5); - } + // Get resource. PoolingException should only be possible if the maxWaitTimeInMillis is too low for the amount of tasks. + ResourceHandle resource = connectionPool.getResource(resourceSpec, alloc, javaEETransaction); + assertNotNull(resource); + assertResourceIsBusy(resource); + + // Keep track of unique resources, we want to validate if resources are NOT being reused + // because the resource is marked "error occurred", we expect it to be replaced with a new resource. + boolean isNewlyAdded = usedResouceHandles.add(resource); + if (isNewlyAdded) { + LOG.log(Level.FINE, "Adding resource: {0}", resource); + } else { + // We only call resourceErrorOccurred, thus resources should have been removed from 'ConnectionPool.dataStructure' + LOG.log(Level.SEVERE, "Adding resource failed for: {0}, thus a resource is being reused", resource); + fail("Resource may not be reused in this test."); } + + // No need to keep resource busy, since we will return it as failed and may not be reused again + + // Return resource + connectionPool.resourceErrorOccurred(resource); + + // We can test the resource results, because the resource should not be reused by another thread + assertResourceIsNotBusy(resource); + + numberOfThreadsFinished.incrementAndGet(); + return null; }); } runTheTasks(tasks); - connectionPool.emptyPool(); + // Pool should be empty: no steady pool size configured and all resources should have been removed from the pool. assertResourcesSize(0); + + cleanupConnectionPool(); + + // Just another proof that all 1000 threads finished. + assertEquals(taskCount, numberOfThreadsFinished.get()); + + // Failed connections should not be reused in the connection pool. New resources should have been created. Since this + // tests returns all resources using resourceErrorOccurred the amount must be equal to the number of threads started. + assertEquals(taskCount, usedResouceHandles.size()); } - private void runTheTasks(List> tasks) throws InterruptedException, MultipleFailuresError { + private void runTheTasks(List> tasks) throws Exception { ExecutorService threadPool = Executors.newFixedThreadPool(1000); - List> futures = threadPool.invokeAll(tasks, 10, TimeUnit.SECONDS); + List> futures = threadPool.invokeAll(tasks, 30, TimeUnit.SECONDS); assertAll( () -> assertTrue(futures.stream().allMatch(f -> f.isDone()), "All done."), () -> assertFalse(futures.stream().anyMatch(f -> f.isCancelled()), "None cancelled.")); + + // Call get() to see if there was any assertion error in the thread execution, this call + // forces the ExecutionException to be thrown, if the Callable execution failed. + for (Future future : futures) { + future.get(); + } } private void assertResourceIsBusy(ResourceHandle resource) { @@ -269,20 +428,28 @@ private void assertResourceIsBusy(ResourceHandle resource) { private void assertResourceIsNotBusy(ResourceHandle resource) { // Resource must be marked as not isBusy / isFree right after call to resourceClosed ResourceState resourceState = resource.getResourceState(); - assertTrue(resourceState.isFree()); - assertFalse(resourceState.isBusy()); + assertTrue(resourceState.isFree(), "assertResourceIsNotBusy - Expected isFree for resource" + resource); + assertFalse(resourceState.isBusy(), "assertResourceIsNotBusy - Expected !isBusy for resource" + resource); } private void assertResourcesSize(int expectedSize) { - RWLockDataStructure dataStructure = (RWLockDataStructure) connectionPool.dataStructure; + DataStructure dataStructure = connectionPool.dataStructure; assertEquals(expectedSize, dataStructure.getResourcesSize()); assertEquals(expectedSize, dataStructure.getAllResources() .size()); } + private void cleanupConnectionPool() { + // Clean the pool + connectionPool.emptyPool(); + assertResourcesSize(0); + } + 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<>()); @@ -290,12 +457,16 @@ public MyConnectionPool(PoolInfo poolInfo) throws PoolingException { @Override protected ConnectorConnectionPool getPoolConfigurationFromJndi(Hashtable env) throws PoolingException { + // Note: this Util has other defaults than the real pool. + // Example: maxWaitTimeInMillis is: 7889, while "max-wait-time-in-millis" is documented as 60.000ms ConnectorConnectionPool connectorConnectionPool = ConnectionPoolObjectsUtils .createDefaultConnectorPoolObject(poolInfo, null); - // lower the default pool size - connectorConnectionPool.setSteadyPoolSize("1"); + // Override some defaults + connectorConnectionPool.setSteadyPoolSize("0"); connectorConnectionPool.setMaxPoolSize("" + myMaxPoolSize); + connectorConnectionPool.setMaxWaitTimeInMillis("" + maxWaitTimeInMillis); + connectorConnectionPool.setPoolResizeQuantity("" + poolResizeQuantity); return connectorConnectionPool; } diff --git a/appserver/pom.xml b/appserver/pom.xml index 6cc40aed084..069c989a391 100644 --- a/appserver/pom.xml +++ b/appserver/pom.xml @@ -89,7 +89,7 @@ 2.2.0-M1 - 3.1.0-RC2 + 3.1.0 3.1.0-M3 @@ -98,7 +98,7 @@ 4.0.0-M2 - 3.0.0-M4 + 3.0.0 3.1.0-M1 4.0.0-M3 @@ -763,7 +763,7 @@ org.codehaus.plexus plexus-utils - 4.0.0 + 4.0.1 diff --git a/appserver/tests/application/pom.xml b/appserver/tests/application/pom.xml index 1c2439ab611..57ad1bcfaf4 100755 --- a/appserver/tests/application/pom.xml +++ b/appserver/tests/application/pom.xml @@ -152,7 +152,7 @@ com.sun.xml.ws jaxws-maven-plugin - 3.0.0 + 4.0.2 jaxws-generate diff --git a/appserver/tests/appserv-tests/lib/pom.xml b/appserver/tests/appserv-tests/lib/pom.xml index ff4eef971aa..80e3ac02145 100644 --- a/appserver/tests/appserv-tests/lib/pom.xml +++ b/appserver/tests/appserv-tests/lib/pom.xml @@ -101,7 +101,7 @@ org.testng testng - 7.9.0 + 7.10.1 diff --git a/appserver/tests/tck/cdi/cdi-model/pom.xml b/appserver/tests/tck/cdi/cdi-model/pom.xml index da72efc9464..9ef359ab41d 100644 --- a/appserver/tests/tck/cdi/cdi-model/pom.xml +++ b/appserver/tests/tck/cdi/cdi-model/pom.xml @@ -53,28 +53,14 @@ cdi-tck-lang-model ${cdi.tck-4-0.version} - - org.jboss.weld - weld-lite-extension-translator - ${weld.version} - - - org.jboss.weld - weld-core-test-common - ${weld.version} - - - org.jboss.weld - weld-core-impl - ${weld.version} - - org.jboss.weld - weld-lite-extension-translator + jakarta.enterprise + jakarta.enterprise.cdi-api + provided @@ -84,43 +70,12 @@ test - - org.jboss.weld - weld-core-test-common - test - - - - org.jboss.weld - weld-core-impl - test - - jakarta.enterprise cdi-tck-lang-model test - - org.jboss.weld.module - weld-web - ${weld.version} - - - - - jakarta.enterprise - cdi-tck-api - ${cdi.tck-4-0.version} - - - jakarta.el - jakarta.el-api - - - - junit junit @@ -128,13 +83,6 @@ test - - commons-lang - commons-lang - 2.6 - test - - @@ -143,7 +91,6 @@ arquillian-glassfish-server-managed 1.4 - @@ -211,6 +158,9 @@ ${glassfish.root}/glassfish8 :org.jboss.cdi... + + CDILangModelTCKRunner.java + diff --git a/appserver/tests/tck/cdi/cdi-model/src/test/java/org/glassfish/tck/cdi/lang/model/CDILangModelTCKRunner.java b/appserver/tests/tck/cdi/cdi-model/src/test/java/org/glassfish/tck/cdi/lang/model/CDILangModelTCKRunner.java index edfbb29df20..8379b8bab4b 100644 --- a/appserver/tests/tck/cdi/cdi-model/src/test/java/org/glassfish/tck/cdi/lang/model/CDILangModelTCKRunner.java +++ b/appserver/tests/tck/cdi/cdi-model/src/test/java/org/glassfish/tck/cdi/lang/model/CDILangModelTCKRunner.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2022 Contributors to the Eclipse Foundation. All rights reserved. + * Copyright (c) 2022, 2024 Contributors to the Eclipse Foundation. 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 @@ -15,28 +15,29 @@ */ package org.glassfish.tck.cdi.lang.model; -import static org.glassfish.tck.cdi.lang.model.LangModelVerifierBuildCompatibleExtension.langModelVerifierBuildCompatibleExtensionPassed; -import static org.jboss.shrinkwrap.api.BeanDiscoveryMode.ALL; -import static org.junit.Assert.assertTrue; +import jakarta.enterprise.inject.build.compatible.spi.BuildCompatibleExtension; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.junit.Arquillian; import org.jboss.cdi.lang.model.tck.LangModelVerifier; import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.EmptyAsset; import org.jboss.shrinkwrap.api.spec.WebArchive; -import org.jboss.shrinkwrap.impl.BeansXml; import org.junit.Test; import org.junit.runner.RunWith; -import jakarta.enterprise.inject.build.compatible.spi.BuildCompatibleExtension; -import jakarta.enterprise.inject.spi.Extension; +import static java.lang.System.Logger.Level.INFO; +import static org.glassfish.tck.cdi.lang.model.LangModelVerifierBuildCompatibleExtension.langModelVerifierBuildCompatibleExtensionPassed; +import static org.junit.Assert.assertTrue; @RunWith(Arquillian.class) public class CDILangModelTCKRunner { + private static final System.Logger LOG = System.getLogger(CDILangModelTCKRunner.class.getName()); + @Deployment public static WebArchive deploy() { - WebArchive archive= ShrinkWrap.create(WebArchive.class) + WebArchive archive = ShrinkWrap.create(WebArchive.class) // The package we are testing .addPackage(LangModelVerifier.class.getPackage()) @@ -45,17 +46,11 @@ public static WebArchive deploy() { .addClass(LangModelVerifierBuildCompatibleExtension.class) .addAsServiceProvider(BuildCompatibleExtension.class, LangModelVerifierBuildCompatibleExtension.class) - // The cleanup extension that vetoes all classes that we're testing, since they are only - // meant for the lang model verifier and shouldn't be processed afterwards. - .addClass(CleanupExtension.class) - .addAsServiceProvider(Extension.class, CleanupExtension.class) + .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml"); - .addAsWebInfResource(new BeansXml(ALL), "beans.xml"); - - System.out.println(archive.toString(true)); + LOG.log(INFO, archive.toString(true)); return archive; - } @Test diff --git a/appserver/tests/tck/cdi/cdi-model/src/test/java/org/glassfish/tck/cdi/lang/model/CleanupExtension.java b/appserver/tests/tck/cdi/cdi-model/src/test/java/org/glassfish/tck/cdi/lang/model/CleanupExtension.java deleted file mode 100644 index 220b3b9c0dc..00000000000 --- a/appserver/tests/tck/cdi/cdi-model/src/test/java/org/glassfish/tck/cdi/lang/model/CleanupExtension.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2022, 2022 Contributors to the Eclipse Foundation. 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 org.glassfish.tck.cdi.lang.model; - -import jakarta.annotation.Priority; -import jakarta.enterprise.event.Observes; -import jakarta.enterprise.inject.spi.Extension; -import jakarta.enterprise.inject.spi.ProcessAnnotatedType; - -public class CleanupExtension implements Extension { - - public void enhancement(@Priority(Integer.MAX_VALUE) @Observes ProcessAnnotatedType pat) { - if (LangModelVerifierBuildCompatibleExtension.langModelVerifierBuildCompatibleExtensionCalled && - pat.getAnnotatedType().getJavaClass().getName().startsWith("org.jboss.cdi.lang.model.tck")) { - pat.veto(); - } - } -} diff --git a/appserver/tests/tck/cdi/cdi-model/src/test/java/org/glassfish/tck/cdi/lang/model/LangModelVerifierBuildCompatibleExtension.java b/appserver/tests/tck/cdi/cdi-model/src/test/java/org/glassfish/tck/cdi/lang/model/LangModelVerifierBuildCompatibleExtension.java index d56f35ee063..89156db06de 100644 --- a/appserver/tests/tck/cdi/cdi-model/src/test/java/org/glassfish/tck/cdi/lang/model/LangModelVerifierBuildCompatibleExtension.java +++ b/appserver/tests/tck/cdi/cdi-model/src/test/java/org/glassfish/tck/cdi/lang/model/LangModelVerifierBuildCompatibleExtension.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2022 Contributors to the Eclipse Foundation. All rights reserved. + * Copyright (c) 2022, 2024 Contributors to the Eclipse Foundation. 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 @@ -15,23 +15,27 @@ */ package org.glassfish.tck.cdi.lang.model; -import org.jboss.cdi.lang.model.tck.LangModelVerifier; - import jakarta.enterprise.inject.build.compatible.spi.BuildCompatibleExtension; import jakarta.enterprise.inject.build.compatible.spi.ClassConfig; +import jakarta.enterprise.inject.build.compatible.spi.Discovery; import jakarta.enterprise.inject.build.compatible.spi.Enhancement; +import jakarta.enterprise.inject.build.compatible.spi.ScannedClasses; + +import org.jboss.cdi.lang.model.tck.LangModelVerifier; public class LangModelVerifierBuildCompatibleExtension implements BuildCompatibleExtension { - public static boolean langModelVerifierBuildCompatibleExtensionCalled; public static boolean langModelVerifierBuildCompatibleExtensionPassed; + @Discovery + public void discovery(ScannedClasses scannedClasses) { + scannedClasses.add(LangModelVerifier.class.getName()); + } + @Enhancement(types = LangModelVerifier.class) public void configure(ClassConfig classConfig) { - langModelVerifierBuildCompatibleExtensionCalled = true; LangModelVerifier.verify(classConfig.info()); // If there's an error, the verify() method will throw an exception langModelVerifierBuildCompatibleExtensionPassed = true; } - } \ No newline at end of file diff --git a/appserver/tests/tck/rest/pom.xml b/appserver/tests/tck/rest/pom.xml index 2b9f4ce558b..b8d9cdd504b 100644 --- a/appserver/tests/tck/rest/pom.xml +++ b/appserver/tests/tck/rest/pom.xml @@ -1,7 +1,7 @@ - 7.0.13 + 7.0.14 ${glassfish.version.7x} ${glassfish.version.7x} diff --git a/docs/website/src/main/resources/README.md b/docs/website/src/main/resources/README.md index 1e2375cdfee..5a6cb9652c5 100644 --- a/docs/website/src/main/resources/README.md +++ b/docs/website/src/main/resources/README.md @@ -9,6 +9,23 @@ sponsored by the Eclipse Foundation. ## Latest News +## March 31, 2024 -- Eclipse GlassFish 7.0.14 Available + +We are very happy to bring you Eclipse GlassFish 7.0.14. + +Download links are available from the [GlassFish Download page](download.md). + +This release features among others an important NPE fix for the SSHLauncher and is highly recommended for our users who make use of this launcher. + +The thread safety of transactions was improved, and the JDBC connection pool sizing logic was also improved, fixing an annoying bug. + +To maintain future quality of GlassFish, many fixes to the internal TCKs tests were done. + +As a new feature, command completion is now enabled in the OSGi interactive console. + +Download links are available from the [GlassFish Download page](download.md) + + ## February 29, 2024 -- Eclipse GlassFish 7.0.13 Available We are very happy to bring you Eclipse GlassFish 7.0.13. diff --git a/docs/website/src/main/resources/download.md b/docs/website/src/main/resources/download.md index 70fa873fafc..401019b217c 100644 --- a/docs/website/src/main/resources/download.md +++ b/docs/website/src/main/resources/download.md @@ -2,26 +2,28 @@ ## Eclipse GlassFish 7.x -GlassFish 7.0.13 is a final release, containing final Jakarta EE 10 APIs. It compiles and runs on JDK 11 to JDK 21. MicroProfile support requires JDK 17 or higher. +GlassFish 7.0.14 is a final release, containing final Jakarta EE 10 APIs. It compiles and runs on JDK 11 to JDK 21. MicroProfile support requires JDK 17 or higher. -GlassFish 7.0.13 compiles and passes all internal tests using OpenJDK 21. Several Jakarta EE 10 TCKs passed on JDK 21 as well. The Jakarta EE 10 TCK as a whole is not JDK 21 compatible and it's not possible to run all the Jakarta EE TCK tests. +GlassFish 7.0.14 compiles and passes all internal tests using OpenJDK 21. Several Jakarta EE 10 TCKs passed on JDK 21 as well. The Jakarta EE 10 TCK as a whole is not JDK 21 compatible and it's not possible to run all the Jakarta EE TCK tests. -In the release for this month we replaced many synchronized blocks by reentrant locks (to accommodate JDK 21 virtual threads). +This release features among others an important NPE fix for the SSHLauncher and is highly recommended for our users who make use of this launcher. -We also looked at SSO between clusters, and fixed a long standing bug there. +The thread safety of transactions was improved, and the JDBC connection pool sizing logic was also improved, fixing an annoying bug. -A long running investigation into potential resource leaks finally came to a conclusion, and resulted in many fixes throughout the code base. The admin console also saw various improvements, specifically with respect to loading. +To maintain future quality of GlassFish, many fixes to the internal TCKs tests were done. + +As a new feature, command completion is now enabled in the OSGi interactive console. Download: -* [Eclipse GlassFish 7.0.13, Jakarta EE Platform, 10](https://www.eclipse.org/downloads/download.php?file=/ee4j/glassfish/glassfish-7.0.13.zip) -* [Eclipse GlassFish 7.0.13, Jakarta EE Web Profile, 10](https://www.eclipse.org/downloads/download.php?file=/ee4j/glassfish/web-7.0.13.zip) -* [Eclipse GlassFish Embedded 7.0.13, Jakarta EE Full Profile, 10](https://search.maven.org/artifact/org.glassfish.main.extras/glassfish-embedded-all/7.0.13/jar) -* [Eclipse GlassFish Embedded 7.0.13, Jakarta EE Web Profile, 10](https://search.maven.org/artifact/org.glassfish.main.extras/glassfish-embedded-web/7.0.13/jar) +* [Eclipse GlassFish 7.0.14, Jakarta EE Platform, 10](https://www.eclipse.org/downloads/download.php?file=/ee4j/glassfish/glassfish-7.0.14.zip) +* [Eclipse GlassFish 7.0.14, Jakarta EE Web Profile, 10](https://www.eclipse.org/downloads/download.php?file=/ee4j/glassfish/web-7.0.14.zip) +* [Eclipse GlassFish Embedded 7.0.14, Jakarta EE Full Profile, 10](https://search.maven.org/artifact/org.glassfish.main.extras/glassfish-embedded-all/7.0.14/jar) +* [Eclipse GlassFish Embedded 7.0.14, Jakarta EE Web Profile, 10](https://search.maven.org/artifact/org.glassfish.main.extras/glassfish-embedded-web/7.0.14/jar) More details: -* [Eclipse GlassFish 7.0.13 Release Notes](https://github.com/eclipse-ee4j/glassfish/releases/tag/7.0.13) +* [Eclipse GlassFish 7.0.14 Release Notes](https://github.com/eclipse-ee4j/glassfish/releases/tag/7.0.14) * [Jakarte EE Platform Specification Project](https://jakartaee.github.io/jakartaee-platform/) for more info about Jakarta EE 10 diff --git a/docs/website/src/main/resources/download_gf7.md b/docs/website/src/main/resources/download_gf7.md index 54d8012e648..c5dbc759395 100644 --- a/docs/website/src/main/resources/download_gf7.md +++ b/docs/website/src/main/resources/download_gf7.md @@ -1,5 +1,32 @@ # Eclipse GlassFish 7.x Downloads +### GlassFish 7.0.14 + +GlassFish 7.0.14 is a final release, containing final Jakarta EE 10 APIs. It compiles and runs on JDK 11 to JDK 21. MicroProfile support requires JDK 17 or higher. + +GlassFish 7.0.14 compiles and passes all internal tests using OpenJDK 21. Several Jakarta EE 10 TCKs passed on JDK 21 as well. The Jakarta EE 10 TCK as a whole is not JDK 21 compatible and it's not possible to run all the Jakarta EE TCK tests. + +This release features among others an important NPE fix for the SSHLauncher and is highly recommended for our users who make use of this launcher. + +The thread safety of transactions was improved, and the JDBC connection pool sizing logic was also improved, fixing an annoying bug. + +To maintain future quality of GlassFish, many fixes to the internal TCKs tests were done. + +As a new feature, command completion is now enabled in the OSGi interactive console. + +Download: + +* [Eclipse GlassFish 7.0.14, Jakarta EE Platform, 10](https://www.eclipse.org/downloads/download.php?file=/ee4j/glassfish/glassfish-7.0.14.zip) +* [Eclipse GlassFish 7.0.14, Jakarta EE Web Profile, 10](https://www.eclipse.org/downloads/download.php?file=/ee4j/glassfish/web-7.0.14.zip) +* [Eclipse GlassFish Embedded 7.0.14, Jakarta EE Full Profile, 10](https://search.maven.org/artifact/org.glassfish.main.extras/glassfish-embedded-all/7.0.14/jar) +* [Eclipse GlassFish Embedded 7.0.14, Jakarta EE Web Profile, 10](https://search.maven.org/artifact/org.glassfish.main.extras/glassfish-embedded-web/7.0.14/jar) + +More details: + +* [Eclipse GlassFish 7.0.14 Release Notes](https://github.com/eclipse-ee4j/glassfish/releases/tag/7.0.14) +* [Jakarte EE Platform Specification Project](https://jakartaee.github.io/jakartaee-platform/) for more info about Jakarta EE 10 + + ### GlassFish 7.0.13 GlassFish 7.0.13 is a final release, containing final Jakarta EE 10 APIs. It compiles and runs on JDK 11 to JDK 21. MicroProfile support requires JDK 17 or higher. diff --git a/nucleus/parent/pom.xml b/nucleus/parent/pom.xml index 67da8a9f654..caa3e330ff0 100644 --- a/nucleus/parent/pom.xml +++ b/nucleus/parent/pom.xml @@ -131,7 +131,7 @@ - 2.16.0 + 2.16.1 2.2.1.Final 3.5.3.Final 3.30.2-GA @@ -487,7 +487,7 @@ org.apache.felix org.apache.felix.webconsole - 5.0.0 + 5.0.2 org.apache.felix @@ -844,6 +844,7 @@ maven-surefire-plugin 3.2.5 + 3 en @@ -867,6 +868,7 @@ maven-failsafe-plugin 3.2.5 + 3 en @@ -1051,7 +1053,7 @@ io.github.git-commit-id git-commit-id-maven-plugin - 5.0.0 + 8.0.2 get-the-git-infos @@ -1063,8 +1065,6 @@ false - YYYY-MM-dd'T'HH:mm:ss'Z' - UTC full git.commit.id.full diff --git a/runtests.sh b/runtests.sh index 7786b0fa430..6b406bb390a 100755 --- a/runtests.sh +++ b/runtests.sh @@ -37,7 +37,7 @@ install_glassfish() { install_jacoco() { mvn -N org.apache.maven.plugins:maven-dependency-plugin:3.2.0:copy \ - -Dartifact=org.jacoco:org.jacoco.agent:0.8.8:jar:runtime \ + -Dartifact=org.jacoco:org.jacoco.agent:0.8.12:jar:runtime \ -Dmdep.stripVersion=true \ -Dmdep.stripClassifier=true \ -DoutputDirectory=${WORKSPACE}/bundles \