diff --git a/src/main/java/io/ebeaninternal/server/transaction/DefaultTransactionThreadLocal.java b/src/main/java/io/ebeaninternal/server/transaction/DefaultTransactionThreadLocal.java index cecd08671..38a944b13 100644 --- a/src/main/java/io/ebeaninternal/server/transaction/DefaultTransactionThreadLocal.java +++ b/src/main/java/io/ebeaninternal/server/transaction/DefaultTransactionThreadLocal.java @@ -75,6 +75,14 @@ public final class DefaultTransactionThreadLocal { return local.get().get(serverName); } + /** + * Return all transactions of the current thread (active/inactive). + * This is intended for test/debugging purposes only! + */ + public static Map currentTransactions() { + return local.get(); + } + private static SpiTransaction obtain(String serverName) { SpiTransaction transaction = local.get().remove(serverName); if (transaction == null) { diff --git a/src/main/java/io/ebeaninternal/server/transaction/JdbcTransaction.java b/src/main/java/io/ebeaninternal/server/transaction/JdbcTransaction.java index bec80e982..0d8a5ce7e 100644 --- a/src/main/java/io/ebeaninternal/server/transaction/JdbcTransaction.java +++ b/src/main/java/io/ebeaninternal/server/transaction/JdbcTransaction.java @@ -314,7 +314,11 @@ public class JdbcTransaction implements SpiTransaction, TxnProfileEventCodes { @Override public String toString() { - return logPrefix; + if (active) { + return logPrefix; + } else { + return logPrefix + "(inactive)"; + } } @Override diff --git a/src/test/java/io/ebean/BaseTestCase.java b/src/test/java/io/ebean/BaseTestCase.java index 92011f37b..ce5da0768 100644 --- a/src/test/java/io/ebean/BaseTestCase.java +++ b/src/test/java/io/ebean/BaseTestCase.java @@ -9,12 +9,18 @@ import io.ebean.meta.ServerMetrics; import io.ebean.util.StringHelper; import io.ebeaninternal.api.SpiEbeanServer; import io.ebeaninternal.api.SpiQuery; +import io.ebeaninternal.api.SpiTransaction; import io.ebeaninternal.server.core.HelpCreateQueryRequest; import io.ebeaninternal.server.core.OrmQueryRequest; import io.ebeaninternal.server.deploy.BeanDescriptor; import io.ebeaninternal.server.expression.platform.DbExpressionHandler; import io.ebeaninternal.server.expression.platform.DbExpressionHandlerFactory; +import io.ebeaninternal.server.transaction.DefaultTransactionThreadLocal; + import org.avaje.agentloader.AgentLoader; +import org.junit.After; +import org.junit.Rule; +import org.junit.rules.TestName; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -22,15 +28,32 @@ import org.tests.model.basic.Country; import java.sql.Types; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.fail; @RunWith(ConditionalTestRunner.class) public abstract class BaseTestCase { protected static Logger logger = LoggerFactory.getLogger(BaseTestCase.class); + @Rule public TestName name = new TestName(); + + @After + public void checkForLeak() { + Map trans = DefaultTransactionThreadLocal.currentTransactions(); + if (trans != null) { + if (!trans.isEmpty()) { + String msg = getClass().getSimpleName() + "." + name.getMethodName() + " did not clear threadScope:" + trans; + trans.clear(); // clear for next test + fail(msg); + } + } + } + + /** * this is the clock delta that may occur between testing machine and db server. * If the clock delta of DB server is in future, an "asOf" query may not find the