/* * The MIT License (MIT) * * Copyright (C) 2018-2019 Fabricio Barros Cabral * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.github.fabriciofx.cactoos.jdbc.prepared; import java.io.InputStream; import java.math.BigDecimal; import java.sql.Date; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Time; import java.sql.Timestamp; import java.time.Duration; import java.time.Instant; import java.util.logging.Level; import java.util.logging.Logger; import org.cactoos.text.FormattedText; import org.cactoos.text.Joined; import org.cactoos.text.UncheckedText; /** * Logged PreparedStatement. * * @since 0.1 * @checkstyle ParameterNameCheck (1500 lines) * @checkstyle ParameterNumberCheck (1500 lines) */ @SuppressWarnings( { "PMD.TooManyMethods", "PMD.LongVariable", "PMD.UseVarargs", "PMD.LoggerIsNotStaticFinal", "PMD.BooleanGetMethodName", "PMD.ExcessivePublicCount", "PMD.AvoidDuplicateLiterals", "PMD.AvoidUsingShortType" } ) public final class Logged extends PreparedStatementEnvelope { /** * The PreparedStatement. */ private final PreparedStatement origin; /** * The name of source value. */ private final String source; /** * The logger. */ private final Logger logger; /** * The connection level. */ private final Level level; /** * The PreparedStatement id. */ private final int id; /** * Ctor. * @param stmt Decorated PreparedStatement * @param src The name of source data * @param lggr The logger * @param lvl The connection level * @param id The PreparedStatement id */ public Logged( final PreparedStatement stmt, final String src, final Logger lggr, final Level lvl, final int id ) { super(stmt); this.origin = stmt; this.source = src; this.logger = lggr; this.level = lvl; this.id = id; } @Override public ResultSet executeQuery() throws SQLException { final Instant start = Instant.now(); final ResultSet rset = this.origin.executeQuery(); final Instant end = Instant.now(); final long millis = Duration.between(start, end).toMillis(); this.logger.log( this.level, new UncheckedText( new FormattedText( new Joined( " ", "[%s] PreparedStatement[#%d] retrieved a", "ResultSet in %dms." ), this.source, this.id, millis ) ).asString() ); return rset; } @Override public int executeUpdate() throws SQLException { final Instant start = Instant.now(); final int updated = this.origin.executeUpdate(); final Instant end = Instant.now(); final long millis = Duration.between(start, end).toMillis(); this.logger.log( this.level, new UncheckedText( new FormattedText( new Joined( " ", "[%s] PreparedStatement[#%d] updated a source and", "returned '%d' in %dms." ), this.source, this.id, updated, millis ) ).asString() ); return updated; } @Override public void setNull(final int index, final int type) throws SQLException { this.origin.setNull(index, type); this.logger.log( this.level, new UncheckedText( new FormattedText( new Joined( " ", "[%s] PreparedStatement[#%d] changed at", "parameter[#%d] with '%d' value." ), this.source, this.id, index, type ) ).asString() ); } @Override public void setBoolean( final int index, final boolean value ) throws SQLException { this.origin.setBoolean(index, value); this.logger.log( this.level, new UncheckedText( new FormattedText( new Joined( " ", "[%s] PreparedStatement[#%d] changed at", "parameter[#%d] with '%s' value." ), this.source, this.id, index, value ) ).asString() ); } @Override public void setByte(final int index, final byte value) throws SQLException { this.origin.setByte(index, value); this.logger.log( this.level, new UncheckedText( new FormattedText( new Joined( " ", "[%s] PreparedStatement[#%d] changed at", "parameter[#%d] with '%d' value." ), this.source, this.id, index, value ) ).asString() ); } @Override public void setShort( final int index, final short value ) throws SQLException { this.origin.setShort(index, value); this.logger.log( this.level, new UncheckedText( new FormattedText( new Joined( " ", "[%s] PreparedStatement[#%d] changed at", "parameter[#%d] with '%d' value." ), this.source, this.id, index, value ) ).asString() ); } @Override public void setInt(final int index, final int value) throws SQLException { this.origin.setInt(index, value); this.logger.log( this.level, new UncheckedText( new FormattedText( new Joined( " ", "[%s] PreparedStatement[#%d] changed at", "parameter[#%d] with '%d' value." ), this.source, this.id, index, value ) ).asString() ); } @Override public void setLong(final int index, final long value) throws SQLException { this.origin.setLong(index, value); this.logger.log( this.level, new UncheckedText( new FormattedText( new Joined( " ", "[%s] PreparedStatement[#%d] changed at", "parameter[#%d] with '%d' value." ), this.source, this.id, index, value ) ).asString() ); } @Override public void setFloat( final int index, final float value ) throws SQLException { this.origin.setFloat(index, value); this.logger.log( this.level, new UncheckedText( new FormattedText( new Joined( " ", "[%s] PreparedStatement[#%d] changed at", "parameter[#%d] with '%f' value." ), this.source, this.id, index, value ) ).asString() ); } @Override public void setDouble( final int index, final double value ) throws SQLException { this.origin.setDouble(index, value); this.logger.log( this.level, new UncheckedText( new FormattedText( new Joined( " ", "[%s] PreparedStatement[#%d] changed at", "parameter[#%d] with '%f' value." ), this.source, this.id, index, value ) ).asString() ); } @Override public void setBigDecimal( final int index, final BigDecimal value ) throws SQLException { this.origin.setBigDecimal(index, value); this.logger.log( this.level, new UncheckedText( new FormattedText( new Joined( " ", "[%s] PreparedStatement[#%d] changed at", "parameter[#%d] with '%s' value." ), this.source, this.id, index, value.toString() ) ).asString() ); } @Override public void setString( final int index, final String value ) throws SQLException { this.origin.setString(index, value); this.logger.log( this.level, new UncheckedText( new FormattedText( new Joined( " ", "[%s] PreparedStatement[#%d] changed at", "parameter[#%d] with '%s' value." ), this.source, this.id, index, value ) ).asString() ); } @Override public void setBytes( final int index, final byte[] values ) throws SQLException { this.origin.setBytes(index, values); this.logger.log( this.level, new UncheckedText( new FormattedText( new Joined( " ", "[%s] PreparedStatement[#%d] changed at", "parameter[#%d] with '%d' bytes." ), this.source, this.id, index, values.length ) ).asString() ); } @Override public void setDate(final int index, final Date value) throws SQLException { this.origin.setDate(index, value); this.logger.log( this.level, new UncheckedText( new FormattedText( new Joined( " ", "[%s] PreparedStatement[#%d] changed at", "parameter[#%d] with '%s' value." ), this.source, this.id, index, value.toString() ) ).asString() ); } @Override public void setTime(final int index, final Time value) throws SQLException { this.origin.setTime(index, value); this.logger.log( this.level, new UncheckedText( new FormattedText( new Joined( " ", "[%s] PreparedStatement[#%d] changed at", "parameter[#%d] with '%s' value." ), this.source, this.id, index, value.toString() ) ).asString() ); } @Override public void setTimestamp( final int index, final Timestamp value ) throws SQLException { this.origin.setTimestamp(index, value); this.logger.log( this.level, new UncheckedText( new FormattedText( new Joined( " ", "[%s] PreparedStatement[#%d] changed at", "parameter[#%d] with '%s' value." ), this.source, this.id, index, value.toString() ) ).asString() ); } @Override public void setAsciiStream( final int index, final InputStream stream, final int length ) throws SQLException { this.origin.setAsciiStream(index, stream, length); this.logger.log( this.level, new UncheckedText( new FormattedText( new Joined( " ", "[%s] PreparedStatement[#%d] changed at", "parameter[#%d] with '%d' bytes." ), this.source, this.id, index, length ) ).asString() ); } /** * Set a stream to Unicode. * @deprecated It not should be used * @param index Parameter index * @param stream InputStream * @param length Data length * @throws SQLException If fails */ @Deprecated public void setUnicodeStream( final int index, final InputStream stream, final int length ) throws SQLException { this.origin.setUnicodeStream(index, stream, length); this.logger.log( this.level, new UncheckedText( new FormattedText( new Joined( " ", "[%s] PreparedStatement[#%d] changed at", "parameter[#%d] with '%d' bytes." ), this.source, this.id, index, length ) ).asString() ); } @Override public void setBinaryStream( final int index, final InputStream stream, final int length ) throws SQLException { this.origin.setBinaryStream(index, stream, length); this.logger.log( this.level, new UncheckedText( new FormattedText( new Joined( " ", "[%s] PreparedStatement[#%d] changed at", "parameter[#%d] with '%d' bytes." ), this.source, this.id, index, length ) ).asString() ); } @Override public void clearParameters() throws SQLException { this.origin.clearParameters(); this.logger.log( this.level, new UncheckedText( new FormattedText( "[%s] PreparedStatement[#%d] parameters has been cleaned.", this.source, this.id ) ).asString() ); } @Override public void setObject( final int index, final Object value, final int type ) throws SQLException { this.origin.setObject(index, value, type); this.logger.log( this.level, new UncheckedText( new FormattedText( new Joined( " ", "[%s] PreparedStatement[#%d] changed at", "parameter[#%d] with '%s' data and '%d' type." ), this.source, this.id, index, value.toString(), type ) ).asString() ); } @Override public void setObject( final int index, final Object value ) throws SQLException { this.origin.setObject(index, value); this.logger.log( this.level, new UncheckedText( new FormattedText( new Joined( " ", "[%s] PreparedStatement[#%d] changed at", "parameter[#%d] with '%s' value." ), this.source, this.id, index, value.toString() ) ).asString() ); } @Override public boolean execute() throws SQLException { final Instant start = Instant.now(); final boolean result = this.origin.execute(); final Instant end = Instant.now(); final long millis = Duration.between(start, end).toMillis(); this.logger.log( this.level, new UncheckedText( new FormattedText( "[%s] PreparedStatement[#%d] returned '%s' in %dms.", this.source, this.id, result, millis ) ).asString() ); return result; } @Override public void addBatch() throws SQLException { this.origin.addBatch(); this.logger.log( this.level, new UncheckedText( new FormattedText( "[%s] PreparedStatement[#%d] added a batch.", this.source, this.id ) ).asString() ); } @Override public ResultSet executeQuery(final String sql) throws SQLException { final Instant start = Instant.now(); final ResultSet rset = this.origin.executeQuery(sql); final Instant end = Instant.now(); final long millis = Duration.between(start, end).toMillis(); this.logger.log( this.level, new UncheckedText( new FormattedText( "[%s] PreparedStatement[#%d] executed SQL %s in %dms.", this.source, this.id, sql, millis ) ).asString() ); return rset; } @Override public int executeUpdate(final String sql) throws SQLException { final Instant start = Instant.now(); final int updated = this.origin.executeUpdate(sql); final Instant end = Instant.now(); final long millis = Duration.between(start, end).toMillis(); this.logger.log( this.level, new UncheckedText( new FormattedText( "[%s] PreparedStatement[#%d] executed SQL %s in %dms.", this.source, this.id, sql, updated, millis ) ).asString() ); return updated; } @Override public void close() throws SQLException { this.origin.close(); this.logger.log( this.level, new UncheckedText( new FormattedText( "[%s] PreparedStatement[#%d] closed.", this.source, this.id ) ).asString() ); } @Override public void setMaxFieldSize(final int max) throws SQLException { this.origin.setMaxFieldSize(max); this.logger.log( this.level, new UncheckedText( new FormattedText( new Joined( " ", "[%s] PreparedStatement[#%d] changed", "max field size to '%d' bytes." ), this.source, this.id, max ) ).asString() ); } @Override public void setMaxRows(final int max) throws SQLException { this.origin.setMaxRows(max); this.logger.log( this.level, new UncheckedText( new FormattedText( "[%s] PreparedStatement[#%d] changed max rows to '%d'.", this.source, this.id, max ) ).asString() ); } @Override public void setQueryTimeout(final int seconds) throws SQLException { this.origin.setQueryTimeout(seconds); this.logger.log( this.level, new UncheckedText( new FormattedText( new Joined( " ", "[%s] PreparedStatement[#%d] changed", "timeout to '%d' seconds." ), this.source, this.id, seconds ) ).asString() ); } @Override public void cancel() throws SQLException { this.origin.cancel(); this.logger.log( this.level, new UncheckedText( new FormattedText( "[%s] PreparedStatement[#%d] canceled.", this.source, this.id ) ).asString() ); } @Override public boolean execute(final String sql) throws SQLException { final Instant start = Instant.now(); final boolean result = this.origin.execute(sql); final Instant end = Instant.now(); final long millis = Duration.between(start, end).toMillis(); this.logger.log( this.level, new UncheckedText( new FormattedText( "[%s] PreparedStatement[#%d] executed SQL '%s' in %dms.", this.source, this.id, sql, millis ) ).asString() ); return result; } @Override public ResultSet getResultSet() throws SQLException { final Instant start = Instant.now(); final ResultSet rset = this.origin.getResultSet(); final Instant end = Instant.now(); final long millis = Duration.between(start, end).toMillis(); this.logger.log( this.level, new UncheckedText( new FormattedText( "[%s] PreparedStatement[#%d] returned a ResultSet in %dms.", this.source, this.id, millis ) ).asString() ); return rset; } @Override public void addBatch(final String sql) throws SQLException { this.origin.addBatch(sql); this.logger.log( this.level, new UncheckedText( new FormattedText( "[%s] PreparedStatement[#%d] added batch with SQL '%s'.", this.source, this.id, sql ) ).asString() ); } @Override public int[] executeBatch() throws SQLException { final int[] counts = this.origin.executeBatch(); this.logger.log( this.level, new UncheckedText( new FormattedText( "[%s] PreparedStatement[#%d] returned '%d' counts.", this.source, this.id, counts.length ) ).asString() ); return counts; } @Override public ResultSet getGeneratedKeys() throws SQLException { final Instant start = Instant.now(); final ResultSet rset = this.origin.getGeneratedKeys(); final Instant end = Instant.now(); final long millis = Duration.between(start, end).toMillis(); this.logger.log( this.level, new UncheckedText( new FormattedText( new Joined( " ", "[%s] PreparedStatement[#%d] returned", "a ResultSet keys in %dms." ), this.source, this.id, millis ) ).asString() ); return rset; } @Override public void setPoolable(final boolean poolable) throws SQLException { this.logger.log( this.level, new UncheckedText( new FormattedText( "[%s] PreparedStatement[#%d] changed poolable to %s.", this.source, this.id, poolable ) ).asString() ); } @Override public void closeOnCompletion() throws SQLException { this.logger.log( this.level, new UncheckedText( new FormattedText( "[%s] PreparedStatement[#%d] will be closed on completion.", this.source, this.id ) ).asString() ); } }