001    /*
002     * $Id: StasiStatement.java,v 1.4 2014/05/17 06:55:25 oboehm Exp $
003     *
004     * Copyright (c) 2014 by Oliver Boehm
005     *
006     * Licensed under the Apache License, Version 2.0 (the "License");
007     * you may not use this file except in compliance with the License.
008     * 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 orimplied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     *
018     * (c)reated 16.03.2014 by oliver (ob@oasd.de)
019     */
020    
021    package patterntesting.runtime.monitor.db.internal;
022    
023    import java.sql.*;
024    
025    import org.slf4j.*;
026    
027    import patterntesting.runtime.log.LogWatch;
028    import patterntesting.runtime.monitor.ProfileMonitor;
029    import patterntesting.runtime.monitor.db.SqlStatistic;
030    import patterntesting.runtime.util.Converter;
031    
032    /**
033     * A simple wrapper for {@link Statement} to be able to find resource problems
034     * while reading and writing to the database. It allows us also to measure times
035     * of SQL statements.
036     * <p>
037     * Why the name "Stasi..."? The Stasi was the official state security service of
038     * Eastern Germany which controls the people (like NSA in the U.S. or KGB in
039     * Russia, see also <a href="http://en.wikipedia.org/wiki/Stasi">Wikipedia</a>).
040     * The StasiStatement controls the embedded {@link Statement} - therefore the
041     * name.
042     * </p>
043     *
044     * @author oliver
045     * @version $Revision: 1.4 $
046     * @since 1.4.1 (16.03.2014)
047     */
048    public class StasiStatement implements Statement {
049    
050        private static final Logger log = LoggerFactory.getLogger(StasiStatement.class);
051        private final LogWatch logWatch = new LogWatch();
052        private final Statement statement;
053    
054        /**
055         * Instantiates a new proxy statement.
056         *
057         * @param statement the statement
058         */
059        public StasiStatement(final Statement statement) {
060            this.statement = statement;
061        }
062    
063        /**
064         * Gets the statement.
065         *
066         * @return the statement
067         */
068        protected final Statement getStatement() {
069            return this.statement;
070        }
071    
072        /**
073         * Adds the batch.
074         *
075         * @param sql the sql
076         * @throws SQLException the sQL exception
077         * @see java.sql.Statement#addBatch(java.lang.String)
078         */
079        public final void addBatch(final String sql) throws SQLException {
080            ProfileMonitor mon = SqlStatistic.start(sql);
081            try {
082                statement.addBatch(sql);
083                SqlStatistic.stop(mon, sql);
084            } catch (SQLException ex) {
085                throw enrichedSQLException(mon, sql, ex);
086            }
087        }
088    
089        /**
090         * Cancel.
091         *
092         * @throws SQLException the sQL exception
093         * @see java.sql.Statement#cancel()
094         */
095        public final void cancel() throws SQLException {
096            statement.cancel();
097            log.trace("{} cancelled.", statement);
098        }
099    
100        /**
101         * Clear batch.
102         *
103         * @throws SQLException the sQL exception
104         * @see java.sql.Statement#clearBatch()
105         */
106        public final void clearBatch() throws SQLException {
107            statement.clearBatch();
108            log.trace("Batch cleared.");
109        }
110    
111        /**
112         * Clear warnings.
113         *
114         * @throws SQLException the sQL exception
115         * @see java.sql.Statement#clearWarnings()
116         */
117        public final void clearWarnings() throws SQLException {
118            statement.clearWarnings();
119            log.trace("Warnings cleared.");
120        }
121    
122        /**
123         * Close.
124         *
125         * @throws SQLException the sQL exception
126         * @see java.sql.Statement#close()
127         */
128        public void close() throws SQLException {
129            statement.close();
130            log.debug("Statement {} was closed after {}.", this.statement, this.logWatch);
131        }
132    
133        /**
134         * Execute.
135         *
136         * @param sql the sql
137         * @param autoGeneratedKeys the auto generated keys
138         * @return true, if successful
139         * @throws SQLException the sQL exception
140         * @see java.sql.Statement#execute(java.lang.String, int)
141         */
142        public final boolean execute(final String sql, final int autoGeneratedKeys) throws SQLException {
143            ProfileMonitor mon = SqlStatistic.start(sql);
144            try {
145                boolean ok = statement.execute(sql, autoGeneratedKeys);
146                SqlStatistic.stop(mon, sql, ok);
147                return ok;
148            } catch (SQLException ex) {
149                throw enrichedSQLException(mon, sql, ex);
150            }
151        }
152    
153        /**
154         * Execute.
155         *
156         * @param sql the sql
157         * @param columnIndexes the column indexes
158         * @return true, if successful
159         * @throws SQLException the sQL exception
160         * @see java.sql.Statement#execute(java.lang.String, int[])
161         */
162        public final boolean execute(final String sql, final int[] columnIndexes) throws SQLException {
163            ProfileMonitor mon = SqlStatistic.start(sql);
164            try {
165                boolean ok = statement.execute(sql, columnIndexes);
166                SqlStatistic.stop(mon, sql, ok);
167                return ok;
168            } catch (SQLException ex) {
169                throw enrichedSQLException(mon, sql, ex);
170            }
171        }
172    
173        /**
174         * Execute.
175         *
176         * @param sql the sql
177         * @param columnNames the column names
178         * @return true, if successful
179         * @throws SQLException the sQL exception
180         * @see java.sql.Statement#execute(java.lang.String, java.lang.String[])
181         */
182        public final boolean execute(final String sql, final String[] columnNames) throws SQLException {
183            ProfileMonitor mon = SqlStatistic.start(sql);
184            try {
185                boolean ok = statement.execute(sql, columnNames);
186                SqlStatistic.stop(mon, sql, ok);
187                return ok;
188            } catch (SQLException ex) {
189                throw enrichedSQLException(mon, sql, ex);
190            }
191        }
192    
193        /**
194         * Execute.
195         *
196         * @param sql the sql
197         * @return true, if successful
198         * @throws SQLException the sQL exception
199         * @see java.sql.Statement#execute(java.lang.String)
200         */
201        public final boolean execute(final String sql) throws SQLException {
202            ProfileMonitor mon = SqlStatistic.start(sql);
203            try {
204                boolean ok = statement.execute(sql);
205                SqlStatistic.stop(mon, sql, ok);
206                return ok;
207            } catch (SQLException ex) {
208                throw enrichedSQLException(mon, sql, ex);
209            }
210        }
211    
212        /**
213         * Execute batch.
214         *
215         * @return the int[]
216         * @throws SQLException the sQL exception
217         * @see java.sql.Statement#executeBatch()
218         */
219        public final int[] executeBatch() throws SQLException {
220            LogWatch watch = new LogWatch();
221            int[] ret = statement.executeBatch();
222            if (log.isDebugEnabled()) {
223                log.debug("Batch execution returns with {} after {}.", Converter.toString(ret), watch);
224            }
225            return ret;
226        }
227    
228        /**
229         * Execute query.
230         *
231         * @param sql the sql
232         * @return the result set
233         * @throws SQLException the sQL exception
234         * @see java.sql.Statement#executeQuery(java.lang.String)
235         */
236        public final ResultSet executeQuery(final String sql) throws SQLException {
237            ProfileMonitor mon = SqlStatistic.start(sql);
238            try {
239                ResultSet rs = new StasiResultSet(statement.executeQuery(sql));
240                SqlStatistic.stop(mon, sql, rs);
241                return rs;
242            } catch (SQLException ex) {
243                throw enrichedSQLException(mon, sql, ex);
244            }
245        }
246    
247        /**
248         * Execute update.
249         *
250         * @param sql the sql
251         * @param autoGeneratedKeys the auto generated keys
252         * @return the int
253         * @throws SQLException the sQL exception
254         * @see java.sql.Statement#executeUpdate(java.lang.String, int)
255         */
256        public final int executeUpdate(final String sql, final int autoGeneratedKeys) throws SQLException {
257            ProfileMonitor mon = SqlStatistic.start(sql);
258            try {
259                int ret = statement.executeUpdate(sql, autoGeneratedKeys);
260                SqlStatistic.stop(mon, sql, ret);
261                return ret;
262            } catch (SQLException ex) {
263                throw enrichedSQLException(mon, sql, ex);
264            }
265        }
266    
267        /**
268         * Execute update.
269         *
270         * @param sql the sql
271         * @param columnIndexes the column indexes
272         * @return the int
273         * @throws SQLException the sQL exception
274         * @see java.sql.Statement#executeUpdate(java.lang.String, int[])
275         */
276        public final int executeUpdate(final String sql, final int[] columnIndexes) throws SQLException {
277            ProfileMonitor mon = SqlStatistic.start(sql);
278            try {
279                int ret = statement.executeUpdate(sql, columnIndexes);
280                SqlStatistic.stop(mon, sql, ret);
281                return ret;
282            } catch (SQLException ex) {
283                throw enrichedSQLException(mon, sql, ex);
284            }
285        }
286    
287        /**
288         * Execute update.
289         *
290         * @param sql the sql
291         * @param columnNames the column names
292         * @return the int
293         * @throws SQLException the sQL exception
294         * @see java.sql.Statement#executeUpdate(java.lang.String, java.lang.String[])
295         */
296        public final int executeUpdate(final String sql, final String[] columnNames) throws SQLException {
297            ProfileMonitor mon = SqlStatistic.start(sql);
298            try {
299                int ret = statement.executeUpdate(sql, columnNames);
300                SqlStatistic.stop(mon, sql, ret);
301                return ret;
302            } catch (SQLException ex) {
303                throw enrichedSQLException(mon, sql, ex);
304            }
305        }
306    
307        /**
308         * Execute update.
309         *
310         * @param sql the sql
311         * @return the int
312         * @throws SQLException the sQL exception
313         * @see java.sql.Statement#executeUpdate(java.lang.String)
314         */
315        public final int executeUpdate(final String sql) throws SQLException {
316            ProfileMonitor mon = SqlStatistic.start(sql);
317            try {
318                int ret = statement.executeUpdate(sql);
319                SqlStatistic.stop(mon, sql, ret);
320                return ret;
321            } catch (SQLException ex) {
322                throw enrichedSQLException(mon, sql, ex);
323            }
324        }
325    
326        /**
327         * For better error analysis the original {@link SQLException} will be
328         * enriched with some additional infos.
329         *
330         * @param mon the mon
331         * @param sql the sql
332         * @param original the original
333         * @return the sQL exception
334         */
335        protected SQLException enrichedSQLException(final ProfileMonitor mon, final String sql,
336                final SQLException original) {
337            mon.stop();
338            return new SQLException("SQL \"" + sql + "\" failed after " + mon.getLastTime(), original);
339        }
340    
341        /**
342         * Gets the connection.
343         *
344         * @return the connection
345         * @throws SQLException the sQL exception
346         * @see java.sql.Statement#getConnection()
347         */
348        public final Connection getConnection() throws SQLException {
349            return statement.getConnection();
350        }
351    
352        /**
353         * Gets the fetch direction.
354         *
355         * @return the fetch direction
356         * @throws SQLException the sQL exception
357         * @see java.sql.Statement#getFetchDirection()
358         */
359        public final int getFetchDirection() throws SQLException {
360            return statement.getFetchDirection();
361        }
362    
363        /**
364         * Gets the fetch size.
365         *
366         * @return the fetch size
367         * @throws SQLException the sQL exception
368         * @see java.sql.Statement#getFetchSize()
369         */
370        public final int getFetchSize() throws SQLException {
371            return statement.getFetchSize();
372        }
373    
374        /**
375         * Gets the generated keys.
376         *
377         * @return the generated keys
378         * @throws SQLException the sQL exception
379         * @see java.sql.Statement#getGeneratedKeys()
380         */
381        public final ResultSet getGeneratedKeys() throws SQLException {
382            return new StasiResultSet(statement.getGeneratedKeys());
383        }
384    
385        /**
386         * Gets the max field size.
387         *
388         * @return the max field size
389         * @throws SQLException the sQL exception
390         * @see java.sql.Statement#getMaxFieldSize()
391         */
392        public final int getMaxFieldSize() throws SQLException {
393            return statement.getMaxFieldSize();
394        }
395    
396        /**
397         * Gets the max rows.
398         *
399         * @return the max rows
400         * @throws SQLException the sQL exception
401         * @see java.sql.Statement#getMaxRows()
402         */
403        public final int getMaxRows() throws SQLException {
404            return statement.getMaxRows();
405        }
406    
407        /**
408         * Gets the more results.
409         *
410         * @return the more results
411         * @throws SQLException the sQL exception
412         * @see java.sql.Statement#getMoreResults()
413         */
414        public final boolean getMoreResults() throws SQLException {
415            return statement.getMoreResults();
416        }
417    
418        /**
419         * Gets the more results.
420         *
421         * @param current the current
422         * @return the more results
423         * @throws SQLException the sQL exception
424         * @see java.sql.Statement#getMoreResults(int)
425         */
426        public final boolean getMoreResults(final int current) throws SQLException {
427            return statement.getMoreResults(current);
428        }
429    
430        /**
431         * Gets the query timeout.
432         *
433         * @return the query timeout
434         * @throws SQLException the sQL exception
435         * @see java.sql.Statement#getQueryTimeout()
436         */
437        public final int getQueryTimeout() throws SQLException {
438            return statement.getQueryTimeout();
439        }
440    
441        /**
442         * Gets the result set.
443         *
444         * @return the result set
445         * @throws SQLException the sQL exception
446         * @see java.sql.Statement#getResultSet()
447         */
448        public final ResultSet getResultSet() throws SQLException {
449            return new StasiResultSet(statement.getResultSet());
450        }
451    
452        /**
453         * Gets the result set concurrency.
454         *
455         * @return the result set concurrency
456         * @throws SQLException the sQL exception
457         * @see java.sql.Statement#getResultSetConcurrency()
458         */
459        public final int getResultSetConcurrency() throws SQLException {
460            return statement.getResultSetConcurrency();
461        }
462    
463        /**
464         * Gets the result set holdability.
465         *
466         * @return the result set holdability
467         * @throws SQLException the sQL exception
468         * @see java.sql.Statement#getResultSetHoldability()
469         */
470        public final int getResultSetHoldability() throws SQLException {
471            return statement.getResultSetHoldability();
472        }
473    
474        /**
475         * Gets the result set type.
476         *
477         * @return the result set type
478         * @throws SQLException the sQL exception
479         * @see java.sql.Statement#getResultSetType()
480         */
481        public final int getResultSetType() throws SQLException {
482            return statement.getResultSetType();
483        }
484    
485        /**
486         * Gets the update count.
487         *
488         * @return the update count
489         * @throws SQLException the sQL exception
490         * @see java.sql.Statement#getUpdateCount()
491         */
492        public final int getUpdateCount() throws SQLException {
493            return statement.getUpdateCount();
494        }
495    
496        /**
497         * Gets the warnings.
498         *
499         * @return the warnings
500         * @throws SQLException the sQL exception
501         * @see java.sql.Statement#getWarnings()
502         */
503        public final SQLWarning getWarnings() throws SQLException {
504            return statement.getWarnings();
505        }
506    
507        /**
508         * Checks if is closed.
509         *
510         * @return true, if is closed
511         * @throws SQLException the sQL exception
512         * @see java.sql.Statement#isClosed()
513         */
514        public final boolean isClosed() throws SQLException {
515            return statement.isClosed();
516        }
517    
518        /**
519         * Checks if is poolable.
520         *
521         * @return true, if is poolable
522         * @throws SQLException the sQL exception
523         * @see java.sql.Statement#isPoolable()
524         */
525        public final boolean isPoolable() throws SQLException {
526            return statement.isPoolable();
527        }
528    
529        /**
530         * Checks if is wrapper for.
531         *
532         * @param arg0 the arg0
533         * @return true, if is wrapper for
534         * @throws SQLException the sQL exception
535         * @see java.sql.Wrapper#isWrapperFor(java.lang.Class)
536         */
537        public final boolean isWrapperFor(final Class<?> arg0) throws SQLException {
538            return statement.isWrapperFor(arg0);
539        }
540    
541        /**
542         * Sets the cursor name.
543         *
544         * @param name the new cursor name
545         * @throws SQLException the sQL exception
546         * @see java.sql.Statement#setCursorName(java.lang.String)
547         */
548        public final void setCursorName(final String name) throws SQLException {
549            statement.setCursorName(name);
550        }
551    
552        /**
553         * Sets the escape processing.
554         *
555         * @param enable the new escape processing
556         * @throws SQLException the sQL exception
557         * @see java.sql.Statement#setEscapeProcessing(boolean)
558         */
559        public final void setEscapeProcessing(final boolean enable) throws SQLException {
560            statement.setEscapeProcessing(enable);
561        }
562    
563        /**
564         * Sets the fetch direction.
565         *
566         * @param direction the new fetch direction
567         * @throws SQLException the sQL exception
568         * @see java.sql.Statement#setFetchDirection(int)
569         */
570        public final void setFetchDirection(final int direction) throws SQLException {
571            statement.setFetchDirection(direction);
572        }
573    
574        /**
575         * Sets the fetch size.
576         *
577         * @param rows the new fetch size
578         * @throws SQLException the sQL exception
579         * @see java.sql.Statement#setFetchSize(int)
580         */
581        public final void setFetchSize(final int rows) throws SQLException {
582            statement.setFetchSize(rows);
583        }
584    
585        /**
586         * Sets the max field size.
587         *
588         * @param max the new max field size
589         * @throws SQLException the sQL exception
590         * @see java.sql.Statement#setMaxFieldSize(int)
591         */
592        public final void setMaxFieldSize(final int max) throws SQLException {
593            statement.setMaxFieldSize(max);
594        }
595    
596        /**
597         * Sets the max rows.
598         *
599         * @param max the new max rows
600         * @throws SQLException the sQL exception
601         * @see java.sql.Statement#setMaxRows(int)
602         */
603        public final void setMaxRows(final int max) throws SQLException {
604            statement.setMaxRows(max);
605        }
606    
607        /**
608         * Sets the poolable.
609         *
610         * @param poolable the new poolable
611         * @throws SQLException the sQL exception
612         * @see java.sql.Statement#setPoolable(boolean)
613         */
614        public final void setPoolable(final boolean poolable) throws SQLException {
615            statement.setPoolable(poolable);
616        }
617    
618        /**
619         * Sets the query timeout.
620         *
621         * @param seconds the new query timeout
622         * @throws SQLException the sQL exception
623         * @see java.sql.Statement#setQueryTimeout(int)
624         */
625        public final void setQueryTimeout(final int seconds) throws SQLException {
626            statement.setQueryTimeout(seconds);
627        }
628    
629        /**
630         * Unwrap.
631         *
632         * @param <T> the generic type
633         * @param arg0 the arg0
634         * @return the t
635         * @throws SQLException the sQL exception
636         * @see java.sql.Wrapper#unwrap(java.lang.Class)
637         */
638        public final <T> T unwrap(final Class<T> arg0) throws SQLException {
639            return statement.unwrap(arg0);
640        }
641    
642        /**
643         * Close on completion. This method is not required for Java 6 and below
644         * but for Java 7. To be compatible with Java 6 this method has no
645         * implementation.
646         *
647         * @throws SQLException the SQL exception
648         * @since Java 7
649         */
650        public final void closeOnCompletion() throws SQLException {
651            throw new UnsupportedOperationException("not yet implemented");
652    
653        }
654    
655        /**
656         * Checks if is close on completion. This method is not required for Java 6
657         * and below but for Java 7. To be compatible with Java 6 this method has no
658         * implementation.
659         *
660         * @return true, if is close on completion
661         * @throws SQLException the SQL exception
662         * @since Java 7
663         */
664        public final boolean isCloseOnCompletion() throws SQLException {
665            throw new UnsupportedOperationException("not yet implemented");
666        }
667    
668    }
669