/*
* Copyright 2015-2016 the original author or authors.
*
* 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.
*/
package org.glowroot.agent.plugin.jdbc;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import com.google.common.collect.Sets;
import org.apache.commons.dbcp.DelegatingConnection;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.glowroot.agent.it.harness.AppUnderTest;
import org.glowroot.agent.it.harness.Container;
import org.glowroot.agent.it.harness.Containers;
import org.glowroot.agent.it.harness.TransactionMarker;
import org.glowroot.wire.api.model.TraceOuterClass.Trace;
import static org.assertj.core.api.Assertions.assertThat;
public class CommitRollbackIT {
private static Container container;
@BeforeClass
public static void setUp() throws Exception {
container = Containers.create();
}
@AfterClass
public static void tearDown() throws Exception {
container.close();
}
@After
public void afterEachTest() throws Exception {
container.checkAndReset();
}
@Test
public void testCommit() throws Exception {
// when
Trace trace = container.execute(ExecuteJdbcCommit.class);
// then
Trace.Timer rootTimer = trace.getHeader().getMainThreadRootTimer();
assertThat(rootTimer.getName()).isEqualTo("mock trace marker");
assertThat(rootTimer.getChildTimerList()).hasSize(2);
// ordering is by total desc, so order is not fixed
Set<String> childTimerNames = Sets.newHashSet();
childTimerNames.add(rootTimer.getChildTimerList().get(0).getName());
childTimerNames.add(rootTimer.getChildTimerList().get(1).getName());
assertThat(childTimerNames).containsOnly("jdbc execute", "jdbc commit");
Iterator<Trace.Entry> i = trace.getEntryList().iterator();
List<Trace.SharedQueryText> sharedQueryTexts = trace.getSharedQueryTextList();
Trace.Entry entry = i.next();
assertThat(entry.getDepth()).isEqualTo(0);
assertThat(entry.getMessage()).isEmpty();
assertThat(sharedQueryTexts.get(entry.getQueryEntryMessage().getSharedQueryTextIndex())
.getFullText()).isEqualTo("insert into employee (name) values ('john doe')");
assertThat(entry.getQueryEntryMessage().getPrefix()).isEqualTo("jdbc execution: ");
assertThat(entry.getQueryEntryMessage().getSuffix()).isEmpty();
entry = i.next();
assertThat(entry.getDepth()).isEqualTo(0);
assertThat(entry.getMessage()).isEqualTo("jdbc commit");
assertThat(i.hasNext()).isFalse();
}
@Test
public void testCommitThrowing() throws Exception {
// when
Trace trace = container.execute(ExecuteJdbcCommitThrowing.class);
// then
Trace.Timer rootTimer = trace.getHeader().getMainThreadRootTimer();
assertThat(rootTimer.getName()).isEqualTo("mock trace marker");
assertThat(rootTimer.getChildTimerList()).hasSize(2);
// ordering is by total desc, so order is not fixed
Set<String> childTimerNames = Sets.newHashSet();
childTimerNames.add(rootTimer.getChildTimerList().get(0).getName());
childTimerNames.add(rootTimer.getChildTimerList().get(1).getName());
assertThat(childTimerNames).containsOnly("jdbc execute", "jdbc commit");
Iterator<Trace.Entry> i = trace.getEntryList().iterator();
List<Trace.SharedQueryText> sharedQueryTexts = trace.getSharedQueryTextList();
Trace.Entry entry = i.next();
assertThat(entry.getDepth()).isEqualTo(0);
assertThat(entry.getMessage()).isEmpty();
assertThat(sharedQueryTexts.get(entry.getQueryEntryMessage().getSharedQueryTextIndex())
.getFullText()).isEqualTo("insert into employee (name) values ('john doe')");
assertThat(entry.getQueryEntryMessage().getPrefix()).isEqualTo("jdbc execution: ");
assertThat(entry.getQueryEntryMessage().getSuffix()).isEmpty();
entry = i.next();
assertThat(entry.getDepth()).isEqualTo(0);
assertThat(entry.getMessage()).isEqualTo("jdbc commit");
assertThat(entry.getError().getMessage()).isEqualTo("A commit failure");
assertThat(i.hasNext()).isFalse();
}
@Test
public void testRollback() throws Exception {
// when
Trace trace = container.execute(ExecuteJdbcRollback.class);
// then
Trace.Timer rootTimer = trace.getHeader().getMainThreadRootTimer();
assertThat(rootTimer.getName()).isEqualTo("mock trace marker");
assertThat(rootTimer.getChildTimerList()).hasSize(2);
// ordering is by total desc, so order is not fixed
Set<String> childTimerNames = Sets.newHashSet();
childTimerNames.add(rootTimer.getChildTimerList().get(0).getName());
childTimerNames.add(rootTimer.getChildTimerList().get(1).getName());
assertThat(childTimerNames).containsOnly("jdbc execute", "jdbc rollback");
Iterator<Trace.Entry> i = trace.getEntryList().iterator();
List<Trace.SharedQueryText> sharedQueryTexts = trace.getSharedQueryTextList();
Trace.Entry entry = i.next();
assertThat(entry.getDepth()).isEqualTo(0);
assertThat(entry.getMessage()).isEmpty();
assertThat(sharedQueryTexts.get(entry.getQueryEntryMessage().getSharedQueryTextIndex())
.getFullText()).isEqualTo("insert into employee (name) values ('john doe')");
assertThat(entry.getQueryEntryMessage().getPrefix()).isEqualTo("jdbc execution: ");
assertThat(entry.getQueryEntryMessage().getSuffix()).isEmpty();
entry = i.next();
assertThat(entry.getDepth()).isEqualTo(0);
assertThat(entry.getMessage()).isEqualTo("jdbc rollback");
assertThat(i.hasNext()).isFalse();
}
@Test
public void testRollbackThrowing() throws Exception {
// when
Trace trace = container.execute(ExecuteJdbcRollbackThrowing.class);
// then
Trace.Timer rootTimer = trace.getHeader().getMainThreadRootTimer();
assertThat(rootTimer.getChildTimerList()).hasSize(2);
// ordering is by total desc, so order is not fixed
Set<String> childTimerNames = Sets.newHashSet();
childTimerNames.add(rootTimer.getChildTimerList().get(0).getName());
childTimerNames.add(rootTimer.getChildTimerList().get(1).getName());
assertThat(childTimerNames).containsOnly("jdbc execute", "jdbc rollback");
Iterator<Trace.Entry> i = trace.getEntryList().iterator();
List<Trace.SharedQueryText> sharedQueryTexts = trace.getSharedQueryTextList();
Trace.Entry entry = i.next();
assertThat(entry.getDepth()).isEqualTo(0);
assertThat(entry.getMessage()).isEmpty();
assertThat(sharedQueryTexts.get(entry.getQueryEntryMessage().getSharedQueryTextIndex())
.getFullText()).isEqualTo("insert into employee (name) values ('john doe')");
assertThat(entry.getQueryEntryMessage().getPrefix()).isEqualTo("jdbc execution: ");
assertThat(entry.getQueryEntryMessage().getSuffix()).isEmpty();
entry = i.next();
assertThat(entry.getDepth()).isEqualTo(0);
assertThat(entry.getMessage()).isEqualTo("jdbc rollback");
assertThat(entry.getError().getMessage()).isEqualTo("A rollback failure");
assertThat(i.hasNext()).isFalse();
}
public abstract static class ExecuteJdbcCommitBase implements AppUnderTest, TransactionMarker {
protected Connection connection;
@Override
public void executeApp() throws Exception {
connection = Connections.createConnection();
connection.setAutoCommit(false);
try {
transactionMarker();
} finally {
Connections.closeConnection(connection);
}
}
void executeInsert() throws Exception {
Statement statement = connection.createStatement();
try {
statement.execute("insert into employee (name) values ('john doe')");
} finally {
statement.close();
}
}
}
public abstract static class ExecuteJdbcCommitThrowingBase
implements AppUnderTest, TransactionMarker {
protected Connection connection;
@Override
public void executeApp() throws Exception {
connection = new DelegatingConnection(Connections.createConnection()) {
@Override
public void commit() throws SQLException {
throw new SQLException("A commit failure");
}
@Override
public void rollback() throws SQLException {
throw new SQLException("A rollback failure");
}
};
connection.setAutoCommit(false);
try {
transactionMarker();
} finally {
Connections.closeConnection(connection);
}
}
void executeInsert() throws Exception {
Statement statement = connection.createStatement();
try {
statement.execute("insert into employee (name) values ('john doe')");
} finally {
statement.close();
}
}
}
public static class ExecuteJdbcCommit extends ExecuteJdbcCommitBase {
@Override
public void transactionMarker() throws Exception {
executeInsert();
connection.commit();
}
}
public static class ExecuteJdbcRollback extends ExecuteJdbcCommitBase {
@Override
public void transactionMarker() throws Exception {
executeInsert();
connection.rollback();
}
}
public static class ExecuteJdbcCommitThrowing extends ExecuteJdbcCommitThrowingBase {
@Override
public void transactionMarker() throws Exception {
executeInsert();
try {
connection.commit();
} catch (SQLException e) {
}
}
}
public static class ExecuteJdbcRollbackThrowing extends ExecuteJdbcCommitThrowingBase {
@Override
public void transactionMarker() throws Exception {
executeInsert();
try {
connection.rollback();
} catch (SQLException e) {
}
}
}
}