| /** |
| * 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.oozie.util.db; |
| |
| import com.google.common.base.Preconditions; |
| import com.google.common.base.Strings; |
| import com.google.common.collect.Sets; |
| import org.apache.oozie.util.XLog; |
| |
| import javax.annotation.Nullable; |
| import javax.persistence.PersistenceException; |
| import java.sql.Array; |
| import java.sql.Blob; |
| import java.sql.CallableStatement; |
| import java.sql.Clob; |
| import java.sql.Connection; |
| import java.sql.DatabaseMetaData; |
| import java.sql.NClob; |
| import java.sql.PreparedStatement; |
| import java.sql.SQLClientInfoException; |
| import java.sql.SQLException; |
| import java.sql.SQLWarning; |
| import java.sql.SQLXML; |
| import java.sql.Savepoint; |
| import java.sql.Statement; |
| import java.sql.Struct; |
| import java.util.Map; |
| import java.util.Properties; |
| import java.util.Set; |
| import java.util.concurrent.Executor; |
| import java.util.function.Predicate; |
| |
| public class FailingConnectionWrapper implements Connection { |
| private static final XLog LOG = XLog.getLog(FailingConnectionWrapper.class); |
| |
| private final Connection delegate; |
| private RuntimeExceptionInjector<PersistenceException> injector; |
| private Predicate<String> predicate; |
| |
| public FailingConnectionWrapper(final Connection delegate, final int failurePercent, |
| @Nullable final Predicate<String> predicate) { |
| this.delegate = delegate; |
| injector = new RuntimeExceptionInjector<>(PersistenceException.class, failurePercent); |
| if (predicate == null) { |
| this.predicate = new OozieDmlStatementPredicate(); |
| } else { |
| this.predicate = predicate; |
| } |
| } |
| |
| @Override |
| public Statement createStatement() throws SQLException { |
| return delegate.createStatement(); |
| } |
| |
| @Override |
| public PreparedStatement prepareStatement(final String sql) throws SQLException { |
| return delegate.prepareStatement(sql); |
| } |
| |
| @Override |
| public CallableStatement prepareCall(final String sql) throws SQLException { |
| return delegate.prepareCall(sql); |
| } |
| |
| @Override |
| public String nativeSQL(final String sql) throws SQLException { |
| return delegate.nativeSQL(sql); |
| } |
| |
| @Override |
| public void setAutoCommit(final boolean autoCommit) throws SQLException { |
| delegate.setAutoCommit(autoCommit); |
| } |
| |
| @Override |
| public boolean getAutoCommit() throws SQLException { |
| return delegate.getAutoCommit(); |
| } |
| |
| @Override |
| public void commit() throws SQLException { |
| delegate.commit(); |
| } |
| |
| @Override |
| public void rollback() throws SQLException { |
| delegate.rollback(); |
| } |
| |
| @Override |
| public void close() throws SQLException { |
| delegate.close(); |
| } |
| |
| @Override |
| public boolean isClosed() throws SQLException { |
| return delegate.isClosed(); |
| } |
| |
| @Override |
| public DatabaseMetaData getMetaData() throws SQLException { |
| return delegate.getMetaData(); |
| } |
| |
| @Override |
| public void setReadOnly(final boolean readOnly) throws SQLException { |
| delegate.setReadOnly(readOnly); |
| } |
| |
| @Override |
| public boolean isReadOnly() throws SQLException { |
| return delegate.isReadOnly(); |
| } |
| |
| @Override |
| public void setCatalog(final String catalog) throws SQLException { |
| delegate.setCatalog(catalog); |
| } |
| |
| @Override |
| public String getCatalog() throws SQLException { |
| return delegate.getCatalog(); |
| } |
| |
| @Override |
| public void setTransactionIsolation(final int level) throws SQLException { |
| delegate.setTransactionIsolation(level); |
| } |
| |
| @Override |
| public int getTransactionIsolation() throws SQLException { |
| return delegate.getTransactionIsolation(); |
| } |
| |
| @Override |
| public SQLWarning getWarnings() throws SQLException { |
| return delegate.getWarnings(); |
| } |
| |
| @Override |
| public void clearWarnings() throws SQLException { |
| delegate.clearWarnings(); |
| } |
| |
| @Override |
| public Statement createStatement(final int resultSetType, final int resultSetConcurrency) throws SQLException { |
| return delegate.createStatement(resultSetType, resultSetConcurrency); |
| } |
| |
| @Override |
| public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency) |
| throws SQLException { |
| if (predicate.test(sql)) { |
| LOG.trace("Injecting random failure. Preparing this statement might fail."); |
| injector.inject(String.format("Deliberately failing to prepare statement. [sql=%s]", sql)); |
| } |
| |
| LOG.trace("Preparing statement. [sql={0}]", sql); |
| return delegate.prepareStatement(sql, resultSetType, resultSetConcurrency); |
| } |
| |
| @Override |
| public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency) |
| throws SQLException { |
| return delegate.prepareCall(sql, resultSetType, resultSetConcurrency); |
| } |
| |
| @Override |
| public Map<String, Class<?>> getTypeMap() throws SQLException { |
| return delegate.getTypeMap(); |
| } |
| |
| @Override |
| public void setTypeMap(final Map<String, Class<?>> map) throws SQLException { |
| delegate.setTypeMap(map); |
| } |
| |
| @Override |
| public void setHoldability(final int holdability) throws SQLException { |
| delegate.setHoldability(holdability); |
| } |
| |
| @Override |
| public int getHoldability() throws SQLException { |
| return delegate.getHoldability(); |
| } |
| |
| @Override |
| public Savepoint setSavepoint() throws SQLException { |
| return delegate.setSavepoint(); |
| } |
| |
| @Override |
| public Savepoint setSavepoint(final String name) throws SQLException { |
| return delegate.setSavepoint(name); |
| } |
| |
| @Override |
| public void rollback(final Savepoint savepoint) throws SQLException { |
| delegate.rollback(); |
| } |
| |
| @Override |
| public void releaseSavepoint(final Savepoint savepoint) throws SQLException { |
| delegate.releaseSavepoint(savepoint); |
| } |
| |
| @Override |
| public Statement createStatement(final int resultSetType, final int resultSetConcurrency, final int resultSetHoldability) |
| throws SQLException { |
| return delegate.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability); |
| } |
| |
| @Override |
| public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency, |
| final int resultSetHoldability) throws SQLException { |
| return delegate.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability); |
| } |
| |
| @Override |
| public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency, |
| final int resultSetHoldability) throws SQLException { |
| return delegate.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability); |
| } |
| |
| @Override |
| public PreparedStatement prepareStatement(final String sql, final int autoGeneratedKeys) throws SQLException { |
| return delegate.prepareStatement(sql, autoGeneratedKeys); |
| } |
| |
| @Override |
| public PreparedStatement prepareStatement(final String sql, final int[] columnIndexes) throws SQLException { |
| return delegate.prepareStatement(sql, columnIndexes); |
| } |
| |
| @Override |
| public PreparedStatement prepareStatement(final String sql, final String[] columnNames) throws SQLException { |
| return delegate.prepareStatement(sql, columnNames); |
| } |
| |
| @Override |
| public Clob createClob() throws SQLException { |
| return delegate.createClob(); |
| } |
| |
| @Override |
| public Blob createBlob() throws SQLException { |
| return delegate.createBlob(); |
| } |
| |
| @Override |
| public NClob createNClob() throws SQLException { |
| return delegate.createNClob(); |
| } |
| |
| @Override |
| public SQLXML createSQLXML() throws SQLException { |
| return delegate.createSQLXML(); |
| } |
| |
| @Override |
| public boolean isValid(final int timeout) throws SQLException { |
| return delegate.isValid(timeout); |
| } |
| |
| @Override |
| public void setClientInfo(final String name, final String value) throws SQLClientInfoException { |
| delegate.setClientInfo(name, value); |
| } |
| |
| @Override |
| public void setClientInfo(final Properties properties) throws SQLClientInfoException { |
| delegate.setClientInfo(properties); |
| } |
| |
| @Override |
| public String getClientInfo(final String name) throws SQLException { |
| return delegate.getClientInfo(name); |
| } |
| |
| @Override |
| public Properties getClientInfo() throws SQLException { |
| return delegate.getClientInfo(); |
| } |
| |
| @Override |
| public Array createArrayOf(final String typeName, final Object[] elements) throws SQLException { |
| return delegate.createArrayOf(typeName, elements); |
| } |
| |
| @Override |
| public Struct createStruct(final String typeName, final Object[] attributes) throws SQLException { |
| return delegate.createStruct(typeName, attributes); |
| } |
| |
| @Override |
| public void setSchema(final String schema) throws SQLException { |
| delegate.setSchema(schema); |
| } |
| |
| @Override |
| public String getSchema() throws SQLException { |
| return delegate.getSchema(); |
| } |
| |
| @Override |
| public void abort(final Executor executor) throws SQLException { |
| delegate.abort(executor); |
| } |
| |
| @Override |
| public void setNetworkTimeout(final Executor executor, final int milliseconds) throws SQLException { |
| delegate.setNetworkTimeout(executor, milliseconds); |
| } |
| |
| @Override |
| public int getNetworkTimeout() throws SQLException { |
| return delegate.getNetworkTimeout(); |
| } |
| |
| @Override |
| public <T> T unwrap(final Class<T> iface) throws SQLException { |
| return delegate.unwrap(iface); |
| } |
| |
| @Override |
| public boolean isWrapperFor(final Class<?> iface) throws SQLException { |
| return delegate.isWrapperFor(iface); |
| } |
| |
| static class OozieDmlStatementPredicate implements Predicate<String> { |
| private static final Set<String> DML_PREFIXES = Sets.newHashSet( |
| "SELECT ", "INSERT INTO ", "UPDATE ", "DELETE FROM "); |
| private static final Set<String> OOZIE_TABLE_NAMES = Sets.newHashSet( |
| "BUNDLE_ACTIONS", "BUNDLE_JOBS", "COORD_ACTIONS", "COORD_JOBS", "SLA_REGISTRATION", "SLA_SUMMARY", |
| "WF_ACTIONS", "WF_JOBS"); |
| |
| @Override |
| public boolean test(@Nullable String input) { |
| Preconditions.checkArgument(!Strings.isNullOrEmpty(input)); |
| |
| boolean isDmlStatement = false; |
| for (final String dmlPrefix : DML_PREFIXES) { |
| if (input.toUpperCase().startsWith(dmlPrefix)) { |
| isDmlStatement = true; |
| } |
| } |
| |
| boolean isOozieTable = false; |
| for (final String oozieTableName : OOZIE_TABLE_NAMES) { |
| if (input.toUpperCase().contains(oozieTableName)) { |
| isOozieTable = true; |
| } |
| } |
| |
| return isDmlStatement && isOozieTable; |
| } |
| } |
| } |