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 030 * {@link PoolingConnection} to implement a pool of {@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 stmt the underlying {@link CallableStatement} 053 * @param key the key for this statement in the {@link KeyedObjectPool} 054 * @param pool the {@link KeyedObjectPool} from which this CallableStatement was obtained 055 * @param conn the {@link DelegatingConnection} that created this CallableStatement 056 */ 057 public PoolableCallableStatement(final CallableStatement stmt, final PStmtKey key, 058 final KeyedObjectPool<PStmtKey,DelegatingPreparedStatement> pool, 059 final DelegatingConnection<Connection> conn) { 060 super(conn, stmt); 061 _pool = pool; 062 _key = key; 063 064 // Remove from trace now because this statement will be 065 // added by the activate method. 066 if(getConnectionInternal() != null) { 067 getConnectionInternal().removeTrace(this); 068 } 069 } 070 071 /** 072 * Returns the CallableStatement to the pool. If {{@link #isClosed()}, this is a No-op. 073 */ 074 @Override 075 public void close() throws SQLException { 076 // calling close twice should have no effect 077 if (!isClosed()) { 078 try { 079 _pool.returnObject(_key,this); 080 } catch(final SQLException e) { 081 throw e; 082 } catch(final RuntimeException e) { 083 throw e; 084 } catch(final Exception e) { 085 throw new SQLException("Cannot close CallableStatement (return to pool failed)", e); 086 } 087 } 088 } 089 090 /** 091 * Activates after retrieval from the pool. Adds a trace for this CallableStatement to the Connection 092 * that created it. 093 */ 094 @Override 095 protected void activate() throws SQLException { 096 setClosedInternal(false); 097 if( getConnectionInternal() != null ) { 098 getConnectionInternal().addTrace( this ); 099 } 100 super.activate(); 101 } 102 103 /** 104 * Passivates to prepare for return to the pool. Removes the trace associated with this CallableStatement 105 * from the Connection that created it. Also closes any associated ResultSets. 106 */ 107 @Override 108 protected void passivate() throws SQLException { 109 setClosedInternal(true); 110 if( getConnectionInternal() != null ) { 111 getConnectionInternal().removeTrace(this); 112 } 113 114 // The JDBC spec requires that a statement close any open 115 // ResultSet's when it is closed. 116 // FIXME The PreparedStatement we're wrapping should handle this for us. 117 // See DBCP-10 for what could happen when ResultSets are closed twice. 118 final List<AbandonedTrace> resultSets = getTrace(); 119 if(resultSets != null) { 120 final ResultSet[] set = resultSets.toArray(new ResultSet[resultSets.size()]); 121 for (final ResultSet element : set) { 122 element.close(); 123 } 124 clearTrace(); 125 } 126 127 super.passivate(); 128 } 129 130}