This is an automated email from the ASF dual-hosted git repository. rmaucher pushed a commit to branch 10.1.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit d7120db1a73827a9ae953768baff10f2ef4ec99e Author: opencode <[email protected]> AuthorDate: Thu Nov 5 22:11:23 2026 +0100 Attempt the slow query JMX publication once per pool JMX is a best-effort management channel, and pool names are unique under Catalina. SlowQueryReportJmx nevertheless retried a refused MBean publication on every borrow, forming the ObjectName and building a Registration each time, with throttle state to keep the repeated failures quiet. Attempt the publication once per pool instead. A refusal is logged once in registerJmx(), later borrows leave the MBean server alone, and PoolJmx drops the retry and throttle state (publishing, failureLoggedUnder, malformedNameReported, refused, failureMarker). Update the tests and log messages to the no-retry contract. --- .../jdbc/pool/interceptor/SlowQueryReportJmx.java | 177 +++++++-------------- .../tomcat/jdbc/pool/util/LocalStrings.properties | 6 +- .../interceptor/TestSlowQueryJmxNotifications.java | 86 ++++------ .../TestSlowQueryReportAbandonedPool.java | 3 - 4 files changed, 89 insertions(+), 183 deletions(-) diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/SlowQueryReportJmx.java b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/SlowQueryReportJmx.java index 05f2994574..82d4040e1e 100644 --- a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/SlowQueryReportJmx.java +++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/SlowQueryReportJmx.java @@ -63,7 +63,8 @@ import org.apache.tomcat.jdbc.pool.util.MessageFactory; * first interceptor instance of a pool to borrow a connection publishes them, and every other instance of that pool - * its other connections, the second definition of the recommended two-thresholds configuration - reports through them. * The pool start callback only records the pool and its registration name; an idle pool is not published, so - * monitoring begins with the first borrow. + * monitoring begins with the first borrow. The publication is attempted once: JMX is a best-effort management channel, + * so a refusal is logged for the operator and later borrows leave the server alone. * They are published under the name the pool started with - see {@link ConnectionPool#getRegistrationName()} - which * identifies the pool and must be unique among the live pools of a class loader: Tomcat gives every pool it manages a * name of its own, and a programmatically built pool has a unique default name. A duplicate live name is reported as @@ -136,8 +137,7 @@ public class SlowQueryReportJmx extends SlowQueryReport implements NotificationE /** * Everything this class publishes for one pool: the notification broadcaster its clients attach their listeners to - * and, in the {@code notifyPool=false} mode, the ObjectName the pool's MBean occupies together with the marker of a - * publication the MBeanServer refused. + * and, in the {@code notifyPool=false} mode, the ObjectName the pool's MBean occupies. * <p> * The state hangs from the pool's statistics store (see {@link PoolStatistics#getJmxState()}), which is 1:1 with * the pool, and nothing a pool published can be given up by another pool's close. It is built by the first @@ -145,9 +145,10 @@ public class SlowQueryReportJmx extends SlowQueryReport implements NotificationE * pool is. * </p> * <p> - * Publication and release are serialised against each other by this object's monitor, and the MBeanServer is - * called outside it: a borrow either publishes, or finds the publication already made, already refused and left for - * a later borrow to retry, or already superseded by the pool's close - in which case the borrow gives back the + * The publication is attempted once per pool: JMX is a best-effort management channel, so a refusal is logged and + * not retried by later borrows. Publication and release are serialised against each other by this object's monitor, + * and the MBeanServer is called outside it: a borrow either publishes, finds that the attempt has already been + * made, or finds the state already superseded by the pool's close - in which case the borrow gives back the * publication it just made. A published MBean can therefore never outlive the state that published it, and the * ObjectName a release gives up is the one this state recorded when it published, whatever class or * {@code objectName} property the interceptor instance running the close happens to carry. @@ -170,20 +171,10 @@ public class SlowQueryReportJmx extends SlowQueryReport implements NotificationE */ private ObjectName publishedUnder; /** - * the ObjectName whose refusal this state has already reported with its exception, so that a persistent - * refusal is not reported once per borrow; guarded by this + * whether a publication has been attempted for this pool, so that a refusal is logged once and later borrows + * leave the management server alone; guarded by this */ - private ObjectName failureLoggedUnder; - /** - * whether a failure that could not form an ObjectName has been reported, so that a permanent malformed name - * is not logged with a stack trace on every borrow; guarded by this - */ - private boolean malformedNameReported; - /** - * whether a publication attempt is in flight, so that the concurrent borrows of one pool make one attempt - * rather than one per thread; guarded by this - */ - private boolean publishing; + private boolean attempted; /** * whether the close of the pool has given this state up; guarded by this */ @@ -216,15 +207,6 @@ public class SlowQueryReportJmx extends SlowQueryReport implements NotificationE return publishedUnder; } - /** - * Returns the ObjectName whose refusal this state has already reported loudly. - * - * @return the reported ObjectName, or {@code null} while no publication for this pool has been refused - */ - protected synchronized ObjectName failureMarker() { - return failureLoggedUnder; - } - /** * Returns whether the close of this pool has given this state up. * @@ -255,23 +237,21 @@ public class SlowQueryReportJmx extends SlowQueryReport implements NotificationE } /** - * Decides whether the calling borrow is to publish this pool's MBean. + * Decides whether the calling borrow is to make this pool's one publication attempt. * - * @return {@code true} if the caller is to publish it now - nothing is published for the pool, no other borrow - * is publishing, and the pool has not closed - and {@code false} when there is nothing to publish - * or someone else is at it + * @return {@code true} if the caller is to attempt the publication - none has been made or attempted yet, and + * the pool has not closed - and {@code false} when there is nothing left to do */ protected synchronized boolean beginPublication() { - if (released || publishedUnder != null || publishing) { + if (released || attempted) { return false; } - publishing = true; + attempted = true; return true; } /** - * Records a publication the MBeanServer accepted, and clears the refusal marker: a registration under the name - * works again, so a later failure cycle reports loudly once more. + * Records a publication the MBeanServer accepted. * * @param oname the ObjectName the MBean was published under * @@ -280,66 +260,13 @@ public class SlowQueryReportJmx extends SlowQueryReport implements NotificationE * that publication, so the borrow that made it has to give it back */ protected synchronized boolean published(ObjectName oname) { - publishing = false; if (released) { return false; } publishedUnder = oname; - failureLoggedUnder = null; return true; } - /** - * Ends a publication attempt that did not publish, and reports it. The attempt being over, a later borrow - * retries. - * <p> - * The retry is not made cheaper than the attempt it repeats, and that is deliberate: the first borrow that - * finds nothing published publishes it, so a clash with a stale or foreign MBean costs monitoring only for as - * long as that MBean is there, while a backoff would make every pool that ever clashed wait out a delay before - * its monitoring came back. What a persistent refusal therefore costs is one publish attempt per borrow - the - * ObjectName formed, a {@link Registration} built, and the MBeanServer's refusal - on a pool whose monitoring is - * misconfigured, which is the cost paid for that recovery. Only the first refusal of an ObjectName is reported - * with its exception, so a persistent refusal is not reported once per borrow. - * </p> - * - * @param oname the ObjectName whose publication was refused, or {@code null} when no ObjectName could be formed - * - there is then no name to mark, so this state remembers that the malformed name was - * reported and later retries stay at debug level - * @param x what the MBeanServer answered - */ - protected synchronized void refused(ObjectName oname, Exception x) { - publishing = false; - if (oname == null) { - if (!malformedNameReported) { - malformedNameReported = true; - log.error(MessageFactory.get("slowQueryReportJmx.jmxRegistrationFailedNoData"), x); - } else if (log.isDebugEnabled()) { - log.debug("Jmx registration failed again: " + x); - } - return; - } - if (oname.equals(failureLoggedUnder)) { - // The loud report went out with the first refusal of this - // name; repeats ride along at debug, without a stack trace, so - // a persistent refusal stays quiet under borrow load - if (log.isDebugEnabled()) { - log.debug("Jmx registration of [" + oname + "] failed again: " + x); - } - return; - } - // First refusal for this ObjectName, or a refusal under another - // ObjectName than the one already reported: report it loudly and - // remember to throttle the repeats - failureLoggedUnder = oname; - if (x instanceof InstanceAlreadyExistsException) { - // Retryable: the name may be held by a stale or foreign MBean - // that a later borrow finds gone - log.warn(MessageFactory.get("slowQueryReportJmx.jmxRegistrationInUse", oname), x); - } else { - log.error(MessageFactory.get("slowQueryReportJmx.jmxRegistrationFailed", oname), x); - } - } - /** * Gives up everything this state holds: the ObjectName the pool's MBean occupied, which the MBeanServer is * asked to release, and with it the broadcaster the pool's clients' listeners are attached to. The close of the @@ -866,11 +793,11 @@ public class SlowQueryReportJmx extends SlowQueryReport implements NotificationE /** * Registers this pool's MBean with JMX. * <p> - * The attempt is made by the first borrow that finds nothing published for the pool, and only by that borrow: - * {@link PoolJmx#beginPublication()} hands the publication to one thread at a time, and a borrow that finds the - * publication made - or refused and waiting for a retry, which is the next borrow's business - is turned away - * before the ObjectName is formed. Forming it and building a {@link Registration} for a race that can only be lost - * is work a healthy pool would pay per borrow for no possible use. + * The publication is attempted once per pool, by the first borrow that needs it: + * {@link PoolJmx#beginPublication()} hands the attempt to one thread, and every later borrow is turned away before + * the ObjectName is formed. JMX is a best-effort management channel, so a refusal is logged for the operator and + * not retried; forming the name and building a {@link Registration} again on every borrow would cost the pool work + * for a publication that is not part of its operation. * </p> * <p> * A pool this instance cannot resolve a statistics store for is skipped before any of it: a pool that carries no @@ -899,38 +826,44 @@ public class SlowQueryReportJmx extends SlowQueryReport implements NotificationE if (state == null || !state.beginPublication()) { return; } - ObjectName oname = null; + ObjectName oname; try { oname = getObjectName(getClass(), state.getPoolName()); - // The MBeanServer keeps the registration resource, never this - // instance: an instance reaches the pool (its own and the inherited - // pool fields), so publishing one made the JVM-lifetime registration - // pin a pool that was abandoned without closing - the only path that - // ever takes a registration down again is a close, which abandonment - // never runs - Registration registration = new Registration(state.getPoolName(), statistics, state); - try { - ManagementFactory.getPlatformMBeanServer().registerMBean(registration, oname); - } catch (Exception x) { - // What the server refused - a name a foreign or stale MBean - // occupies, a non-compliant resource, a security restriction. A - // later borrow retries. - state.refused(oname, x); - return; - } - if (!state.published(oname)) { - // The pool's close landed while the server was taking this in, - // and has already given up everything the state held; this - // publication belongs to nobody now but the thread that made it - JmxUtil.unregisterJmx(oname); - } } catch (MalformedObjectNameException | RuntimeOperationsException x) { // A name that cannot be formed - a malformed objectName property, or - // a pool name carrying characters the ObjectName grammar reserves - - // has no ObjectName to record the refusal against; the state - // remembers that it was reported and later retries stay at debug - // level (see refused). - state.refused(null, x); + // a pool name carrying characters the ObjectName grammar reserves. + // There is no publication to make, and JMX is best effort, so this + // is reported once and not attempted again. + log.error(MessageFactory.get("slowQueryReportJmx.jmxRegistrationFailedNoData"), x); + return; + } + // The MBeanServer keeps the registration resource, never this + // instance: an instance reaches the pool (its own and the inherited + // pool fields), so publishing one made the JVM-lifetime registration + // pin a pool that was abandoned without closing - the only path that + // ever takes a registration down again is a close, which abandonment + // never runs + Registration registration = new Registration(state.getPoolName(), statistics, state); + try { + ManagementFactory.getPlatformMBeanServer().registerMBean(registration, oname); + } catch (InstanceAlreadyExistsException x) { + // The name is held by a foreign or stale MBean. JMX is best effort + // and names are unique under Catalina, so this is reported rather + // than retried: by the time a retry ran, the name could describe a + // different component. + log.warn(MessageFactory.get("slowQueryReportJmx.jmxRegistrationInUse", oname), x); + return; + } catch (Exception x) { + // A non-compliant resource, a security restriction, or another + // server-side refusal. Reported once, not retried. + log.error(MessageFactory.get("slowQueryReportJmx.jmxRegistrationFailed", oname), x); + return; + } + if (!state.published(oname)) { + // The pool's close landed while the server was taking this in, + // and has already given up everything the state held; this + // publication belongs to nobody now but the thread that made it + JmxUtil.unregisterJmx(oname); } } diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/util/LocalStrings.properties b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/util/LocalStrings.properties index 44f3d82f17..54d36c999d 100644 --- a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/util/LocalStrings.properties +++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/util/LocalStrings.properties @@ -216,9 +216,9 @@ slowQueryReport.slowQueryReport=Slow Query Report SQL={0}; time={1} ms; slowQueryReport.unknownProperty=The interceptor property [{0}] is not recognized and is not applied: property names are matched exactly, and a name this interceptor does not know leaves the setting it was meant to configure at the value it carries. Check the jdbcInterceptors configuration for the name that was meant to be configured. slowQueryReportJmx.invalidNotifyPool=The configured notifyPool [{0}] is neither true nor false and is not applied: query notifications keep going to the channel the current setting [{1}] selects. Correct the value in the jdbcInterceptors configuration to choose the notification channel that was meant to be configured. -slowQueryReportJmx.jmxRegistrationFailed=Jmx registration of [{0}] failed, no JMX data will be exposed for the query stats until a registration succeeds. The registration is retried by later borrows; repeated failures are logged at debug level. -slowQueryReportJmx.jmxRegistrationFailedNoData=Jmx registration failed, no JMX data will be exposed for the query stats. -slowQueryReportJmx.jmxRegistrationInUse=Jmx registration of [{0}] failed: the name is already in use by another MBean. The registration is retried by later borrows; repeated failures are logged at debug level. +slowQueryReportJmx.jmxRegistrationFailed=Jmx registration of [{0}] failed, so no JMX data will be exposed for the query stats. JMX is a best-effort management channel, so the registration is not retried; correct the reported cause and restart the pool to restore the management surface. +slowQueryReportJmx.jmxRegistrationFailedNoData=Jmx registration failed, so no JMX data will be exposed for the query stats. JMX is a best-effort management channel, so the registration is not retried. +slowQueryReportJmx.jmxRegistrationInUse=Jmx registration of [{0}] failed because the name is already in use by another MBean. JMX is a best-effort management channel, so the registration is not retried; give the pool a unique name or remove the other MBean and restart the pool to restore the management surface. slowQueryReportJmx.noJmxSupport={0}- No JMX support, composite type was not found. slowQueryReportJmx.unableToInitCompositeType=Unable to initialize composite data type for JMX stats and notifications. diff --git a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/interceptor/TestSlowQueryJmxNotifications.java b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/interceptor/TestSlowQueryJmxNotifications.java index 5698f700dd..0379991ab7 100644 --- a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/interceptor/TestSlowQueryJmxNotifications.java +++ b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/interceptor/TestSlowQueryJmxNotifications.java @@ -793,9 +793,7 @@ class TestSlowQueryJmxNotifications extends JdbcPoolBaseTest { + " the name is formed and a Registration built to lose the" + " slot race: parsing a full ObjectName and allocating a" + " publication that cannot happen on every borrow of a" - + " perfectly registered pool is cost with no possible use -" - + " and a refused registration still leaves the slot free, so" - + " the retry semantics of a failed publish are untouched"); + + " perfectly registered pool is cost with no possible use"); } } finally { ds.close(); @@ -803,8 +801,7 @@ class TestSlowQueryJmxNotifications extends JdbcPoolBaseTest { } @Test - void aRefusedPublicationKeepsRetryingAndAPublishTheServerTookStandsUntilTheClose() - throws Exception { + void aRefusedPublicationIsNotRetriedByALaterBorrow() throws Exception { String poolName = "JmxRegFail-" + System.nanoTime(); DataSource ds = newDataSource(); ds.setName(poolName); @@ -829,31 +826,24 @@ class TestSlowQueryJmxNotifications extends JdbcPoolBaseTest { SlowQueryReportJmx.PoolJmx state = SlowQueryReportJmx.jmxStateFor(pool); Assertions.assertNull(state.publishedUnder(), "a publish the MBeanServer refused must not leave the pool claiming an" - + " MBean that was never published - every later borrow would find" - + " the pool published and never retry"); + + " MBean that was never published"); + } - // With the obstacle gone, a later borrow must still get the - // MBean published: the refused attempt must not have latched - // anything for the life of the pool. - server.unregisterMBean(oname); - try (@SuppressWarnings("unused") Connection next = ds.getConnection()) { - Assertions.assertTrue(server.isRegistered(oname), - "the borrow after the obstacle was removed must publish the MBean"); - // the attribute of the interceptor's MBean interface, - // answered by the registered resource: not the obstacle, - // which exposes no such attribute - Assertions.assertDoesNotThrow(() -> server.getAttribute(oname, "SlowQueriesCD"), - "the published MBean must be the interceptor's, answering its statistics query"); - Assertions.assertEquals(oname, - SlowQueryReportJmx.jmxStateFor(pool).publishedUnder(), - "the retrying borrow must have recorded what it published"); - } + // JMX is best effort. With the obstacle gone, a later borrow does + // not repeat the attempt: the refusal was reported once for the + // operator, and retrying by name could collide with yet another + // component that took it. + server.unregisterMBean(oname); + try (@SuppressWarnings("unused") Connection next = ds.getConnection()) { + Assertions.assertFalse(server.isRegistered(oname), + "a later borrow must not retry a refused publication"); + Assertions.assertNull(SlowQueryReportJmx.jmxStateFor(pool).publishedUnder(), + "the pool must still not claim a publication it never made"); } + ds.close(); - Assertions.assertFalse(server.isRegistered(oname), - "closing the pool must release the registration the retry published"); Assertions.assertNull(SlowQueryReportJmx.jmxStateFor(pool), - "and give up the JMX state it hung from"); + "closing the pool must give up the JMX state it hung from"); } finally { // remove the obstacle if the test never got as far as removing // it itself; on the green path nothing is registered any more @@ -866,7 +856,7 @@ class TestSlowQueryJmxNotifications extends JdbcPoolBaseTest { } @Test - void persistentPublicationRefusalKeepsRetryingAndCleansUpOnClose() throws Exception { + void aRefusedPublicationIsReportedOnceAndCleanedUpOnClose() throws Exception { String poolName = "JmxRegRefuse-" + System.nanoTime(); DataSource refusingPool = reportingPoolNamed(poolName); @@ -879,36 +869,27 @@ class TestSlowQueryJmxNotifications extends JdbcPoolBaseTest { List<Notification> received = new CopyOnWriteArrayList<>(); NotificationListener listener = (notification, handback) -> received.add(notification); try { - // The first refused borrow: nothing is published for the pool, and - // the failure is marked as reported - the loud report of this pool - // (warn/error) has gone out, its repeats ride at debug without - // a stack trace (not asserted: the module's test infrastructure - // captures no log, so the throttle is pinned through its - // bookkeeping only) + // The first borrow attempts the publication and is refused: + // nothing is published for the pool, and the refusal is reported + // once by registerJmx(). Connection first = refusingPool.getConnection(); ConnectionPool pool = refusingPool.getPool(); SlowQueryReportJmx.PoolJmx state = SlowQueryReportJmx.jmxStateFor(pool); Assertions.assertNull(state.publishedUnder(), "a refused publication must not leave the pool claiming an MBean"); - Assertions.assertEquals(oname, state.failureMarker(), - "the first refusal of this ObjectName must be marked as reported for the" - + " throttle"); interceptorOf(first, SlowQueryReportJmx.class) .addNotificationListener(listener, null, null); - // Later borrows keep retrying: still refused (the obstacle holds - // the name), still nothing published, and the identical marker - // keeps their logging throttled + // Later borrows do not retry the publication; the obstacle keeps + // the name and nothing is published for this pool, but its own + // broadcaster still serves its listeners. try (@SuppressWarnings("unused") Connection second = refusingPool.getConnection(); @SuppressWarnings("unused") Connection third = refusingPool.getConnection()) { Assertions.assertNull(state.publishedUnder(), - "retried refusals must leave the pool unpublished, so that the next borrow" - + " can try again"); - Assertions.assertEquals(oname, state.failureMarker(), - "repeated refusals of the same ObjectName must not re-mark it loudly"); + "later borrows must not retry a refused publication"); Assertions.assertEquals("registration-obstacle", server.getAttribute(oname, "HeldBy"), - "the retried publishes must still be colliding with the obstacle"); + "the name must still belong to the obstacle"); // A slow query of the refused pool still reaches the pool's own // broadcaster - state the close has to give up even though no // publication of this pool ever succeeded @@ -923,9 +904,7 @@ class TestSlowQueryJmxNotifications extends JdbcPoolBaseTest { first.close(); // Close a pool that never got published: its close gives up the - // state it reported through - broadcaster and throttle marker and - // all - so neither outlives the pool, and a later pool under the - // same name starts its own failure cycle marked loudly again + // state it reported through, so it does not outlive the pool. refusingPool.close(); Assertions.assertNull(SlowQueryReportJmx.jmxStateFor(pool), "the close of the pool gives up the JMX state it published nothing from"); @@ -933,18 +912,17 @@ class TestSlowQueryJmxNotifications extends JdbcPoolBaseTest { server.getAttribute(oname, "HeldBy"), "and the name still belongs to the obstacle: the refused pool never took it"); - // the name stayed retryable through every refusal: once the - // obstacle is gone, a fresh pool under the name publishes at once - // and publishes cleanly - success marks nothing in the throttle + // A fresh pool under a freed name makes its own attempt and + // publishes cleanly. server.unregisterMBean(oname); DataSource revivedPool = reportingPoolNamed(poolName); try (@SuppressWarnings("unused") Connection borrowed = revivedPool.getConnection()) { Assertions.assertTrue(server.isRegistered(oname), "the first borrow of a pool started after the obstacle is gone" + " must publish the MBean"); - Assertions.assertNull(SlowQueryReportJmx.jmxStateFor(revivedPool.getPool()) - .failureMarker(), - "a succeeding publication must not leave a throttle marker behind"); + Assertions.assertEquals(oname, + SlowQueryReportJmx.jmxStateFor(revivedPool.getPool()).publishedUnder(), + "the published MBean must be the revived pool's"); } revivedPool.close(); Assertions.assertFalse(server.isRegistered(oname), @@ -1270,8 +1248,6 @@ class TestSlowQueryJmxNotifications extends JdbcPoolBaseTest { SlowQueryReportJmx.PoolJmx state = SlowQueryReportJmx.jmxStateFor(ds.getPool()); Assertions.assertNull(state.publishedUnder(), "a name that cannot be formed must never leave the pool claiming a publication"); - Assertions.assertNull(state.failureMarker(), - "a failure that cannot form its ObjectName has no name to mark as reported"); } } finally { logger.removeHandler(handler); diff --git a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/interceptor/TestSlowQueryReportAbandonedPool.java b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/interceptor/TestSlowQueryReportAbandonedPool.java index ede0c4cb4b..91e585ccc5 100644 --- a/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/interceptor/TestSlowQueryReportAbandonedPool.java +++ b/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/pool/interceptor/TestSlowQueryReportAbandonedPool.java @@ -220,9 +220,6 @@ class TestSlowQueryReportAbandonedPool extends JdbcPoolBaseTest { Assertions.assertNull(state.publishedUnder(), "and the successor must not report through the abandoned pool's MBean:" + " its own publication is the one the MBeanServer refused"); - Assertions.assertNotNull(state.failureMarker(), - "the refusal is recorded against the ObjectName, reported once and retried" - + " on the next borrow"); survivor.close(); --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
