001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.dbcp2;
018
019import java.sql.PreparedStatement;
020import java.sql.SQLException;
021
022import org.apache.commons.pool2.KeyedObjectPool;
023
024/**
025 * A {@link DelegatingPreparedStatement} that cooperates with {@link PoolingConnection} to implement a pool of
026 * {@link PreparedStatement}s.
027 * <p>
028 * My {@link #close} method returns me to my containing pool. (See {@link PoolingConnection}.)
029 * </p>
030 *
031 * @param <K>
032 *            the key type
033 *
034 * @see PoolingConnection
035 * @since 2.0
036 */
037public class PoolablePreparedStatement<K> extends DelegatingPreparedStatement {
038
039    /**
040     * The {@link KeyedObjectPool} from which I was obtained.
041     */
042    private final KeyedObjectPool<K, PoolablePreparedStatement<K>> pool;
043
044    /**
045     * My "key" as used by {@link KeyedObjectPool}.
046     */
047    private final K key;
048
049    private volatile boolean batchAdded;
050
051    /**
052     * Constructs a new instance.
053     *
054     * @param stmt
055     *            my underlying {@link PreparedStatement}
056     * @param key
057     *            my key as used by {@link KeyedObjectPool}
058     * @param pool
059     *            the {@link KeyedObjectPool} from which I was obtained.
060     * @param conn
061     *            the {@link java.sql.Connection Connection} from which I was created
062     */
063    public PoolablePreparedStatement(final PreparedStatement stmt, final K key,
064            final KeyedObjectPool<K, PoolablePreparedStatement<K>> pool, final DelegatingConnection<?> conn) {
065        super(conn, stmt);
066        this.pool = pool;
067        this.key = key;
068
069        // Remove from trace now because this statement will be
070        // added by the activate method.
071        removeThisTrace(conn);
072    }
073
074    @Override
075    public void activate() throws SQLException {
076        setClosedInternal(false);
077        AbandonedTrace.add(getConnectionInternal(), this);
078        super.activate();
079    }
080
081    /**
082     * Add batch.
083     */
084    @Override
085    public void addBatch() throws SQLException {
086        super.addBatch();
087        batchAdded = true;
088    }
089
090    /**
091     * Clear Batch.
092     */
093    @Override
094    public void clearBatch() throws SQLException {
095        batchAdded = false;
096        super.clearBatch();
097    }
098
099    /**
100     * Return me to my pool.
101     */
102    @Override
103    public void close() throws SQLException {
104        // calling close twice should have no effect
105        if (!isClosed()) {
106            try {
107                pool.returnObject(key, this);
108            } catch (final SQLException | RuntimeException e) {
109                throw e;
110            } catch (final Exception e) {
111                throw new SQLException("Cannot close preparedstatement (return to pool failed)", e);
112            }
113        }
114    }
115
116    /**
117     * Package-protected for tests.
118     *
119     * @return The key.
120     */
121    K getKey() {
122        return key;
123    }
124
125    @Override
126    public void passivate() throws SQLException {
127        // DBCP-372. clearBatch with throw an exception if called when the
128        // connection is marked as closed.
129        if (batchAdded) {
130            clearBatch();
131        }
132        prepareToReturn();
133    }
134}