/* * (C) Copyright 2011 Nuxeo SA (http://nuxeo.com/) and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Contributors: * matic */ package org.nuxeo.ecm.core.management.jtajca; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; import static org.junit.Assert.assertThat; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.FutureTask; import javax.inject.Inject; import javax.inject.Named; import javax.transaction.TransactionManager; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.log4j.Level; import org.apache.log4j.spi.LoggingEvent; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.nuxeo.ecm.core.test.CoreFeature; import org.nuxeo.runtime.test.runner.Features; import org.nuxeo.runtime.test.runner.FeaturesRunner; import org.nuxeo.runtime.test.runner.LogCaptureFeature; import org.nuxeo.runtime.test.runner.LogCaptureFeature.NoLogCaptureFilterException; import org.slf4j.MDC; /** * @author matic */ @RunWith(FeaturesRunner.class) @Features({ JtajcaManagementFeature.class, CoreFeature.class, LogCaptureFeature.class }) public class CanMonitorTransactionsTest { @Inject @Named("default") protected TransactionMonitor monitor; @Inject protected TransactionManager tm; protected ExecutorService executor; @Before public void injectExectutor() { executor = Executors.newSingleThreadExecutor(); } @After public void shudownExecutor() { executor.shutdownNow(); } protected void begin() { try { tm.begin(); } catch (Exception cause) { throw new RuntimeException("Cannot start new transacton", cause); } } protected void rollback() { try { tm.rollback(); } catch (Exception cause) { throw new RuntimeException("Cannot rollback transaction", cause); } } protected void commit() { try { tm.commit(); } catch (Exception cause) { throw new RuntimeException("Cannot commit transaction", cause); } } @Test public void isMonitorInstalled() { assertThat(monitor, notNullValue()); monitor.getTotalCommits(); // throw exception is monitor not present } protected class TestTotalRollbacks implements Callable<Boolean> { @Override public Boolean call() { long activeCount = monitor.getActiveCount(); long totalRollbacks = monitor.getTotalRollbacks(); begin(); assertThat(monitor.getActiveCount(), is(activeCount + 1)); rollback(); assertThat(monitor.getActiveCount(), is(activeCount)); assertThat(monitor.getTotalRollbacks(), is(totalRollbacks + 1)); return Boolean.TRUE; } } @Test public void isTotalRollbacksCorrect() throws InterruptedException, ExecutionException { assertThat(monitor.getActiveCount(), is(1L)); FutureTask<Boolean> rollback = new FutureTask<>(new TestTotalRollbacks()); executor.execute(rollback); assertThat(rollback.get(), is(true)); } protected class TestTotalCommits implements Callable<Boolean> { @Override public Boolean call() { long totalCommits = monitor.getTotalCommits(); begin(); commit(); assertThat(monitor.getTotalCommits(), is(totalCommits + 1)); return Boolean.TRUE; } } @Test public void isTotalCommitsCorrect() throws InterruptedException, ExecutionException { FutureTask<Boolean> commit = new FutureTask<>(new TestTotalCommits()); executor.execute(commit); assertThat(commit.get(), is(true)); } protected class TestCollectStatistics implements Callable<Boolean> { @Override public Boolean call() { try { List<TransactionStatistics> stats = monitor.getActiveStatistics(); long count = stats.size(); begin(); stats = monitor.getActiveStatistics(); assertThat((long) stats.size(), is(count + 1)); commit(); stats = monitor.getActiveStatistics(); assertThat((long) stats.size(), is(count)); } catch (Exception cause) { LogFactory.getLog(CanMonitorTransactionsTest.class).error("Caught error while collecting statistics", cause); } return Boolean.TRUE; } } @Test public void isActiveStatisticsCollected() throws InterruptedException, ExecutionException { FutureTask<Boolean> task = new FutureTask<>(new TestCollectStatistics()); executor.execute(task); assertThat(task.get(), is(true)); } @Inject LogCaptureFeature.Result logCaptureResults; @Test @LogCaptureFeature.FilterWith(value = CanMonitorTransactionsTest.LogRollbackTraceFilter.class) public void logContainsRollbackTrace() throws InterruptedException, ExecutionException, NoLogCaptureFilterException { FutureTask<Boolean> task = new FutureTask<>(new TestLogRollbackTrace()); executor.execute(task); assertThat(task.get(), is(true)); logCaptureResults.assertHasEvent(); } protected class TestLogRollbackTrace implements Callable<Boolean> { @Override public Boolean call() throws Exception { begin(); rollback(); return true; } } public static class LogRollbackTraceFilter implements LogCaptureFeature.Filter { @Override public boolean accept(LoggingEvent event) { if (event.getLevel() != Level.TRACE) { return false; } Object msg = event.getMessage(); if (!(msg instanceof TransactionStatistics)) { return false; } TransactionStatistics stats = (TransactionStatistics) msg; if (!TransactionStatistics.Status.ROLLEDBACK.equals(stats.getStatus())) { return false; } return true; } } @Test @LogCaptureFeature.FilterWith(value = CanMonitorTransactionsTest.LogMessageFilter.class) public void logContainsTxKey() throws InterruptedException, ExecutionException, NoLogCaptureFilterException { FutureTask<Boolean> task = new FutureTask<>(new TestLogRollbackTrace()); executor.execute(task); assertThat(task.get(), is(true)); logCaptureResults.assertHasEvent(); } public static class LogMessageFilter implements LogCaptureFeature.Filter { @Override public boolean accept(LoggingEvent event) { return MDC.get("tx") != null; } } protected class TestLogMessage implements Callable<Boolean> { protected final Log log = LogFactory.getLog(TestLogMessage.class); @Override public Boolean call() throws Exception { begin(); log.warn("logging with active tx"); rollback(); return true; } } }