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 */ 017 018package org.apache.commons.dbcp2; 019 020import java.sql.CallableStatement; 021import java.sql.Connection; 022import java.sql.ResultSet; 023import java.sql.SQLException; 024import java.util.List; 025 026import org.apache.commons.pool2.KeyedObjectPool; 027 028/** 029 * A {@link DelegatingCallableStatement} that cooperates with {@link PoolingConnection} to implement a pool of 030 * {@link CallableStatement}s. 031 * <p> 032 * The {@link #close} method returns this statement to its containing pool. (See {@link PoolingConnection}.) 033 * 034 * @see PoolingConnection 035 * @since 2.0 036 */ 037public class PoolableCallableStatement extends DelegatingCallableStatement { 038 039 /** 040 * The {@link KeyedObjectPool} from which this CallableStatement was obtained. 041 */ 042 private final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> pool; 043 044 /** 045 * Key for this statement in the containing {@link KeyedObjectPool}. 046 */ 047 private final PStmtKey key; 048 049 /** 050 * Constructor. 051 * 052 * @param callableStatement 053 * the underlying {@link CallableStatement} 054 * @param key 055 * the key for this statement in the {@link KeyedObjectPool} 056 * @param pool 057 * the {@link KeyedObjectPool} from which this CallableStatement was obtained 058 * @param connection 059 * the {@link DelegatingConnection} that created this CallableStatement 060 */ 061 public PoolableCallableStatement(final CallableStatement callableStatement, final PStmtKey key, 062 final KeyedObjectPool<PStmtKey, DelegatingPreparedStatement> pool, 063 final DelegatingConnection<Connection> connection) { 064 super(connection, callableStatement); 065 this.pool = pool; 066 this.key = key; 067 068 // Remove from trace now because this statement will be 069 // added by the activate method. 070 if (getConnectionInternal() != null) { 071 getConnectionInternal().removeTrace(this); 072 } 073 } 074 075 /** 076 * Returns the CallableStatement to the pool. If {{@link #isClosed()}, this is a No-op. 077 */ 078 @Override 079 public void close() throws SQLException { 080 // calling close twice should have no effect 081 if (!isClosed()) { 082 try { 083 pool.returnObject(key, this); 084 } catch (final SQLException e) { 085 throw e; 086 } catch (final RuntimeException e) { 087 throw e; 088 } catch (final Exception e) { 089 throw new SQLException("Cannot close CallableStatement (return to pool failed)", e); 090 } 091 } 092 } 093 094 /** 095 * Activates after retrieval from the pool. Adds a trace for this CallableStatement to the Connection that created 096 * it. 097 * 098 * @since 2.4.0 made public, was protected in 2.3.0. 099 */ 100 @Override 101 public void activate() throws SQLException { 102 setClosedInternal(false); 103 if (getConnectionInternal() != null) { 104 getConnectionInternal().addTrace(this); 105 } 106 super.activate(); 107 } 108 109 /** 110 * Passivates to prepare for return to the pool. Removes the trace associated with this CallableStatement from the 111 * Connection that created it. Also closes any associated ResultSets. 112 * 113 * @since 2.4.0 made public, was protected in 2.3.0. 114 */ 115 @Override 116 public void passivate() throws SQLException { 117 setClosedInternal(true); 118 if (getConnectionInternal() != null) { 119 getConnectionInternal().removeTrace(this); 120 } 121 122 // The JDBC spec requires that a statement close any open 123 // ResultSet's when it is closed. 124 // FIXME The PreparedStatement we're wrapping should handle this for us. 125 // See DBCP-10 for what could happen when ResultSets are closed twice. 126 final List<AbandonedTrace> resultSets = getTrace(); 127 if (resultSets != null) { 128 final ResultSet[] set = resultSets.toArray(new ResultSet[resultSets.size()]); 129 for (final ResultSet element : set) { 130 element.close(); 131 } 132 clearTrace(); 133 } 134 135 super.passivate(); 136 } 137 138}