001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *      http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019package org.apache.oozie.util.db;
020
021import com.google.common.base.Preconditions;
022import com.google.common.base.Predicate;
023import com.google.common.collect.Sets;
024import org.apache.directory.api.util.Strings;
025import org.apache.oozie.util.XLog;
026
027import javax.annotation.Nullable;
028import javax.persistence.PersistenceException;
029import java.sql.Array;
030import java.sql.Blob;
031import java.sql.CallableStatement;
032import java.sql.Clob;
033import java.sql.Connection;
034import java.sql.DatabaseMetaData;
035import java.sql.NClob;
036import java.sql.PreparedStatement;
037import java.sql.SQLClientInfoException;
038import java.sql.SQLException;
039import java.sql.SQLWarning;
040import java.sql.SQLXML;
041import java.sql.Savepoint;
042import java.sql.Statement;
043import java.sql.Struct;
044import java.util.Map;
045import java.util.Properties;
046import java.util.Set;
047import java.util.concurrent.Executor;
048
049public class FailingConnectionWrapper implements Connection {
050    private static final XLog LOG = XLog.getLog(FailingConnectionWrapper.class);
051
052    private final Connection delegate;
053    private static final RuntimeExceptionInjector<PersistenceException> injector =
054            new RuntimeExceptionInjector<>(PersistenceException.class, 5);
055    private static final OozieDmlStatementPredicate oozieDmlStatementPredicate =
056            new OozieDmlStatementPredicate();
057
058    public FailingConnectionWrapper(final Connection delegate) throws SQLException {
059        this.delegate = delegate;
060    }
061
062    @Override
063    public Statement createStatement() throws SQLException {
064        return delegate.createStatement();
065    }
066
067    @Override
068    public PreparedStatement prepareStatement(final String sql) throws SQLException {
069        return delegate.prepareStatement(sql);
070    }
071
072    @Override
073    public CallableStatement prepareCall(final String sql) throws SQLException {
074        return delegate.prepareCall(sql);
075    }
076
077    @Override
078    public String nativeSQL(final String sql) throws SQLException {
079        return delegate.nativeSQL(sql);
080    }
081
082    @Override
083    public void setAutoCommit(final boolean autoCommit) throws SQLException {
084        delegate.setAutoCommit(autoCommit);
085    }
086
087    @Override
088    public boolean getAutoCommit() throws SQLException {
089        return delegate.getAutoCommit();
090    }
091
092    @Override
093    public void commit() throws SQLException {
094        delegate.commit();
095    }
096
097    @Override
098    public void rollback() throws SQLException {
099        delegate.rollback();
100    }
101
102    @Override
103    public void close() throws SQLException {
104        delegate.close();
105    }
106
107    @Override
108    public boolean isClosed() throws SQLException {
109        return delegate.isClosed();
110    }
111
112    @Override
113    public DatabaseMetaData getMetaData() throws SQLException {
114        return delegate.getMetaData();
115    }
116
117    @Override
118    public void setReadOnly(final boolean readOnly) throws SQLException {
119        delegate.setReadOnly(readOnly);
120    }
121
122    @Override
123    public boolean isReadOnly() throws SQLException {
124        return delegate.isReadOnly();
125    }
126
127    @Override
128    public void setCatalog(final String catalog) throws SQLException {
129        delegate.setCatalog(catalog);
130    }
131
132    @Override
133    public String getCatalog() throws SQLException {
134        return delegate.getCatalog();
135    }
136
137    @Override
138    public void setTransactionIsolation(final int level) throws SQLException {
139        delegate.setTransactionIsolation(level);
140    }
141
142    @Override
143    public int getTransactionIsolation() throws SQLException {
144        return delegate.getTransactionIsolation();
145    }
146
147    @Override
148    public SQLWarning getWarnings() throws SQLException {
149        return delegate.getWarnings();
150    }
151
152    @Override
153    public void clearWarnings() throws SQLException {
154        delegate.clearWarnings();
155    }
156
157    @Override
158    public Statement createStatement(final int resultSetType, final int resultSetConcurrency) throws SQLException {
159        return delegate.createStatement(resultSetType, resultSetConcurrency);
160    }
161
162    @Override
163    public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency)
164            throws SQLException {
165        if (oozieDmlStatementPredicate.apply(sql)) {
166            LOG.trace("Injecting random failure. It's a DML statement of an Oozie table, preparing this statement might fail.");
167            injector.inject(String.format("Deliberately failing to prepare statement. [sql=%s]", sql));
168        }
169
170        LOG.trace("Preparing statement. [sql={0}]", sql);
171        return delegate.prepareStatement(sql, resultSetType, resultSetConcurrency);
172    }
173
174    @Override
175    public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency)
176            throws SQLException {
177        return delegate.prepareCall(sql, resultSetType, resultSetConcurrency);
178    }
179
180    @Override
181    public Map<String, Class<?>> getTypeMap() throws SQLException {
182        return delegate.getTypeMap();
183    }
184
185    @Override
186    public void setTypeMap(final Map<String, Class<?>> map) throws SQLException {
187        delegate.setTypeMap(map);
188    }
189
190    @Override
191    public void setHoldability(final int holdability) throws SQLException {
192        delegate.setHoldability(holdability);
193    }
194
195    @Override
196    public int getHoldability() throws SQLException {
197        return delegate.getHoldability();
198    }
199
200    @Override
201    public Savepoint setSavepoint() throws SQLException {
202        return delegate.setSavepoint();
203    }
204
205    @Override
206    public Savepoint setSavepoint(final String name) throws SQLException {
207        return delegate.setSavepoint(name);
208    }
209
210    @Override
211    public void rollback(final Savepoint savepoint) throws SQLException {
212        delegate.rollback();
213    }
214
215    @Override
216    public void releaseSavepoint(final Savepoint savepoint) throws SQLException {
217        delegate.releaseSavepoint(savepoint);
218    }
219
220    @Override
221    public Statement createStatement(final int resultSetType, final int resultSetConcurrency, final int resultSetHoldability)
222            throws SQLException {
223        return delegate.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability);
224    }
225
226    @Override
227    public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency,
228                                              final int resultSetHoldability) throws SQLException {
229        return delegate.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
230    }
231
232    @Override
233    public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency,
234                                         final int resultSetHoldability) throws SQLException {
235        return delegate.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
236    }
237
238    @Override
239    public PreparedStatement prepareStatement(final String sql, final int autoGeneratedKeys) throws SQLException {
240        return delegate.prepareStatement(sql, autoGeneratedKeys);
241    }
242
243    @Override
244    public PreparedStatement prepareStatement(final String sql, final int[] columnIndexes) throws SQLException {
245        return delegate.prepareStatement(sql, columnIndexes);
246    }
247
248    @Override
249    public PreparedStatement prepareStatement(final String sql, final String[] columnNames) throws SQLException {
250        return delegate.prepareStatement(sql, columnNames);
251    }
252
253    @Override
254    public Clob createClob() throws SQLException {
255        return delegate.createClob();
256    }
257
258    @Override
259    public Blob createBlob() throws SQLException {
260        return delegate.createBlob();
261    }
262
263    @Override
264    public NClob createNClob() throws SQLException {
265        return delegate.createNClob();
266    }
267
268    @Override
269    public SQLXML createSQLXML() throws SQLException {
270        return delegate.createSQLXML();
271    }
272
273    @Override
274    public boolean isValid(final int timeout) throws SQLException {
275        return delegate.isValid(timeout);
276    }
277
278    @Override
279    public void setClientInfo(final String name, final String value) throws SQLClientInfoException {
280        delegate.setClientInfo(name, value);
281    }
282
283    @Override
284    public void setClientInfo(final Properties properties) throws SQLClientInfoException {
285        delegate.setClientInfo(properties);
286    }
287
288    @Override
289    public String getClientInfo(final String name) throws SQLException {
290        return delegate.getClientInfo(name);
291    }
292
293    @Override
294    public Properties getClientInfo() throws SQLException {
295        return delegate.getClientInfo();
296    }
297
298    @Override
299    public Array createArrayOf(final String typeName, final Object[] elements) throws SQLException {
300        return delegate.createArrayOf(typeName, elements);
301    }
302
303    @Override
304    public Struct createStruct(final String typeName, final Object[] attributes) throws SQLException {
305        return delegate.createStruct(typeName, attributes);
306    }
307
308    @Override
309    public void setSchema(final String schema) throws SQLException {
310        delegate.setSchema(schema);
311    }
312
313    @Override
314    public String getSchema() throws SQLException {
315        return delegate.getSchema();
316    }
317
318    @Override
319    public void abort(final Executor executor) throws SQLException {
320        delegate.abort(executor);
321    }
322
323    @Override
324    public void setNetworkTimeout(final Executor executor, final int milliseconds) throws SQLException {
325        delegate.setNetworkTimeout(executor, milliseconds);
326    }
327
328    @Override
329    public int getNetworkTimeout() throws SQLException {
330        return delegate.getNetworkTimeout();
331    }
332
333    @Override
334    public <T> T unwrap(final Class<T> iface) throws SQLException {
335        return delegate.unwrap(iface);
336    }
337
338    @Override
339    public boolean isWrapperFor(final Class<?> iface) throws SQLException {
340        return delegate.isWrapperFor(iface);
341    }
342
343    static class OozieDmlStatementPredicate implements Predicate<String> {
344        private static final Set<String> DML_PREFIXES = Sets.newHashSet(
345                "SELECT ", "INSERT INTO ", "UPDATE ", "DELETE FROM ");
346        private static final Set<String> OOZIE_TABLE_NAMES = Sets.newHashSet(
347                "BUNDLE_ACTIONS", "BUNDLE_JOBS", "COORD_ACTIONS", "COORD_JOBS", "SLA_REGISTRATION", "SLA_SUMMARY",
348                "WF_ACTIONS", "WF_JOBS");
349
350        @Override
351        public boolean apply(@Nullable String input) {
352            Preconditions.checkArgument(Strings.isNotEmpty(input));
353
354            boolean isDmlStatement = false;
355            for (final String dmlPrefix : DML_PREFIXES) {
356                if (input.toUpperCase().startsWith(dmlPrefix)) {
357                    isDmlStatement = true;
358                }
359            }
360
361            boolean isOozieTable = false;
362            for (final String oozieTableName : OOZIE_TABLE_NAMES) {
363                if (input.toUpperCase().contains(oozieTableName)) {
364                    isOozieTable = true;
365                }
366            }
367
368            return isDmlStatement && isOozieTable;
369        }
370    }
371}