From 423275d12abe5c8c644950cec6915f0d2083f55e Mon Sep 17 00:00:00 2001 From: Matt Sicker Date: Mon, 6 Jan 2014 21:27:39 -0600 Subject: [PATCH 1/2] Add transaction wrapper. --- .../core/appender/db/AbstractTransaction.java | 53 ++++++++++ .../log4j/core/appender/db/FakeTransaction.java | 51 +++++++++ .../core/appender/db/TransactionException.java | 34 ++++++ .../core/appender/db/jdbc/JDBCTransaction.java | 116 +++++++++++++++++++++ 4 files changed, 254 insertions(+) create mode 100644 log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/AbstractTransaction.java create mode 100644 log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/FakeTransaction.java create mode 100644 log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/TransactionException.java create mode 100644 log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jdbc/JDBCTransaction.java diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/AbstractTransaction.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/AbstractTransaction.java new file mode 100644 index 0000000..365f86f --- /dev/null +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/AbstractTransaction.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.logging.log4j.core.appender.db; + +/** + * Unified wrapper interface for transactional classes. This provides a + * simplified model for handling transactions from different committees. + */ +public interface AbstractTransaction { + + /** + * Marks the beginning of a transaction. If supported, this is the point + * where a call to {@link #rollback()} would come back to. + * + * @throws TransactionException if there was a problem beginning the transaction. + */ + void begin() throws TransactionException; + + /** + * Commits a transaction to the database. If the transaction has been marked + * for rollback via {@link #setRollbackOnly()}, this call will instead roll + * back the transaction. + * + * @throws TransactionException if there was a problem committing the transaction. + */ + void commit() throws TransactionException; + + /** + * Rolls back a transaction to where it was when {@link #begin()} was called. + */ + void rollback(); + + /** + * Marks this transaction for rollback regardless of whether {@link #commit()} + * or {@link #rollback()} are called afterward. + */ + void setRollbackOnly(); + +} diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/FakeTransaction.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/FakeTransaction.java new file mode 100644 index 0000000..1916e36 --- /dev/null +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/FakeTransaction.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.logging.log4j.core.appender.db; + +/** + * Fake transaction that doesn't do anything. Useful for databases that don't + * support transactions, or for databases with auto-commit enabled. As there is + * no point in wasting memory with a dummy transaction, this is provided as a + * singleton. + */ +public class FakeTransaction implements AbstractTransaction { + private FakeTransaction() {} + + private static class Holder { + private static final FakeTransaction INSTANCE = new FakeTransaction(); + } + + public static FakeTransaction getInstance() { + return Holder.INSTANCE; + } + + @Override + public void begin() { + } + + @Override + public void commit() { + } + + @Override + public void rollback() { + } + + @Override + public void setRollbackOnly() { + } +} diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/TransactionException.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/TransactionException.java new file mode 100644 index 0000000..9aeded2 --- /dev/null +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/TransactionException.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.logging.log4j.core.appender.db; + +public class TransactionException extends Exception { + public TransactionException() { + } + + public TransactionException(String message) { + super(message); + } + + public TransactionException(String message, Throwable cause) { + super(message, cause); + } + + public TransactionException(Throwable cause) { + super(cause); + } +} \ No newline at end of file diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jdbc/JDBCTransaction.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jdbc/JDBCTransaction.java new file mode 100644 index 0000000..f85b197 --- /dev/null +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/db/jdbc/JDBCTransaction.java @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.logging.log4j.core.appender.db.jdbc; + +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.appender.db.AbstractTransaction; +import org.apache.logging.log4j.core.appender.db.TransactionException; +import org.apache.logging.log4j.status.StatusLogger; + +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Savepoint; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Transaction wrapper for a JDBC Connection object. + */ +public class JDBCTransaction implements AbstractTransaction { + + private static final Logger LOGGER = StatusLogger.getLogger(); + private final Connection connection; + private final AtomicBoolean shouldRollback = new AtomicBoolean(false); + private Savepoint savepoint; + + public JDBCTransaction(Connection connection) { + this.connection = connection; + } + + @Override + public void begin() throws TransactionException { + if (this.isConnectionOpen()) { + try { + this.shouldRollback.set(false); + this.savepoint = this.connection.setSavepoint(); + } catch (SQLException e) { + LOGGER.catching(e); + this.setRollbackOnly(); + throw LOGGER.throwing(new TransactionException(e)); + } + } + } + + @Override + public void commit() throws TransactionException { + if (this.isConnectionOpen()) { + try { + if (this.shouldRollback.get()) { + this.rollback(); + } else { + this.connection.commit(); + } + } catch (SQLException e) { + LOGGER.catching(e); + this.rollback(); + throw LOGGER.throwing(new TransactionException(e)); + } + } + } + + @Override + public void rollback() { + if (this.isConnectionOpen()) { + try { + if (this.savepoint == null) { + this.connection.rollback(); + } else { + this.connection.rollback(this.savepoint); + } + } catch (SQLException e) { + LOGGER.warn("Error rolling back transaction.", e); + } finally { + this.releaseSavepoint(); + } + } + } + + @Override + public void setRollbackOnly() { + this.shouldRollback.set(true); + } + + private boolean isConnectionOpen() { + try { + return !this.connection.isClosed(); + } catch (SQLException e) { + LOGGER.catching(e); + return false; + } + } + + private void releaseSavepoint() { + if (this.savepoint != null) { + try { + this.connection.releaseSavepoint(this.savepoint); + } catch (SQLException e) { + LOGGER.warn("Error releasing savepoint.", e); + } finally { + this.savepoint = null; + } + } + } +} -- 1.8.5.2