--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.jdbc.EmbedPooledConnection\r
+\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to You under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+\r
+ */\r
+\r
+package org.apache.derby.jdbc;\r
+\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+import org.apache.derby.iapi.reference.Property;\r
+import org.apache.derby.iapi.error.ExceptionSeverity;\r
+import org.apache.derby.iapi.reference.JDBC30Translation;\r
+import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;\r
+\r
+/* import impl class */\r
+import org.apache.derby.impl.jdbc.Util;\r
+import org.apache.derby.impl.jdbc.EmbedConnection;\r
+import org.apache.derby.iapi.jdbc.BrokeredConnection;\r
+import org.apache.derby.iapi.jdbc.BrokeredConnectionControl;\r
+import org.apache.derby.iapi.jdbc.EngineConnection;\r
+import org.apache.derby.impl.jdbc.EmbedPreparedStatement;\r
+import org.apache.derby.impl.jdbc.EmbedCallableStatement;\r
+\r
+\r
+import java.sql.Connection;\r
+import java.sql.SQLException;\r
+import java.sql.Statement;\r
+import java.sql.PreparedStatement;\r
+import java.sql.CallableStatement;\r
+\r
+import java.util.Vector;\r
+import java.util.Enumeration;\r
+\r
+/* -- New jdbc 20 extension types --- */\r
+import javax.sql.DataSource;\r
+import javax.sql.PooledConnection;\r
+import javax.sql.ConnectionEventListener;\r
+import javax.sql.ConnectionEvent;\r
+import javax.sql.StatementEventListener;\r
+\r
+/** \r
+ A PooledConnection object is a connection object that provides hooks for\r
+ connection pool management.\r
+\r
+ <P>This is Derby's implementation of a PooledConnection for use in\r
+ the following environments:\r
+ <UL>\r
+ <LI> JDBC 3.0 - Java 2 - JDK 1.4, J2SE 5.0\r
+ <LI> JDBC 2.0 - Java 2 - JDK 1.2,1.3\r
+ </UL>\r
+\r
+ */\r
+class EmbedPooledConnection implements javax.sql.PooledConnection, BrokeredConnectionControl\r
+{\r
+\r
+ /** Static counter for connection ids */\r
+ private static int idCounter = 0;\r
+ \r
+ /** The id for this connection. */\r
+ private int connectionId;\r
+ \r
+ /** the connection string */\r
+ private String connString;\r
+ \r
+ private Vector eventListener; // who wants to know I am closed or error\r
+\r
+ EmbedConnection realConnection;\r
+ int defaultIsolationLevel;\r
+ private boolean defaultReadOnly;\r
+ BrokeredConnection currentConnectionHandle;\r
+\r
+ // set up once by the data source\r
+ final ReferenceableDataSource dataSource;\r
+ private final String username;\r
+ private final String password;\r
+ /**\r
+ True if the password was passed in on the connection request, false if it came from the data source property.\r
+ */\r
+ private final boolean requestPassword;\r
+\r
+ protected boolean isActive;\r
+ \r
+ private synchronized int nextId()\r
+ {\r
+ return idCounter++;\r
+ }\r
+\r
+ EmbedPooledConnection(ReferenceableDataSource ds, String u, String p, boolean requestPassword) throws SQLException\r
+ {\r
+ connectionId = nextId();\r
+\r
+ dataSource = ds;\r
+ username = u;\r
+ password = p;\r
+ this.requestPassword = requestPassword;\r
+ isActive = true;\r
+\r
+ // open the connection up front in order to do authentication\r
+ openRealConnection();\r
+\r
+ }\r
+\r
+ String getUsername()\r
+ {\r
+ if (username == null || username.equals(""))\r
+ return Property.DEFAULT_USER_NAME;\r
+ else\r
+ return username;\r
+ }\r
+\r
+ String getPassword()\r
+ {\r
+ if (password == null)\r
+ return "";\r
+ else\r
+ return password;\r
+ }\r
+\r
+\r
+ /** \r
+ Create an object handle for a database connection.\r
+\r
+ @return a Connection object\r
+\r
+ @exception SQLException - if a database-access error occurs.\r
+ */\r
+ public synchronized Connection getConnection() throws SQLException\r
+ {\r
+ checkActive();\r
+\r
+ // need to do this in case the connection is forcibly removed without\r
+ // first being closed.\r
+ closeCurrentConnectionHandle();\r
+\r
+\r
+ // RealConnection is not null if the app server yanks a local\r
+ // connection from one client and give it to another. In this case,\r
+ // the real connection is ready to be used. Otherwise, set it up\r
+ if (realConnection == null)\r
+ {\r
+ // first time we establish a connection\r
+ openRealConnection();\r
+ }\r
+ else\r
+ {\r
+ resetRealConnection();\r
+ }\r
+\r
+ // now make a brokered connection wrapper and give this to the user\r
+ // we reuse the EmbedConnection(ie realConnection).\r
+ Connection c = getNewCurrentConnectionHandle(); \r
+ return c;\r
+ }\r
+\r
+ final void openRealConnection() throws SQLException {\r
+ // first time we establish a connection\r
+ Connection rc = dataSource.getConnection(username, password, requestPassword);\r
+\r
+ this.realConnection = (EmbedConnection) rc;\r
+ defaultIsolationLevel = rc.getTransactionIsolation();\r
+ defaultReadOnly = rc.isReadOnly();\r
+ if (currentConnectionHandle != null)\r
+ realConnection.setApplicationConnection(currentConnectionHandle);\r
+ }\r
+\r
+ final Connection getNewCurrentConnectionHandle() {\r
+ Connection applicationConnection = currentConnectionHandle =\r
+ ((org.apache.derby.jdbc.Driver20) (realConnection.getLocalDriver())).newBrokeredConnection(this);\r
+ realConnection.setApplicationConnection(applicationConnection);\r
+ return applicationConnection;\r
+\r
+ }\r
+\r
+ /**\r
+ In this case the Listeners are *not* notified. JDBC 3.0 spec section 11.4\r
+ */\r
+ private void closeCurrentConnectionHandle() throws SQLException {\r
+ if (currentConnectionHandle != null)\r
+ {\r
+ Vector tmpEventListener = eventListener;\r
+ eventListener = null;\r
+\r
+ try {\r
+ currentConnectionHandle.close();\r
+ } finally {\r
+ eventListener = tmpEventListener;\r
+ }\r
+\r
+ currentConnectionHandle = null;\r
+ }\r
+ }\r
+\r
+ void resetRealConnection() throws SQLException {\r
+\r
+ // ensure any outstanding changes from the previous\r
+ // user are rolledback.\r
+ realConnection.rollback();\r
+\r
+ // clear any warnings that are left over\r
+ realConnection.clearWarnings();\r
+\r
+ // need to reset transaction isolation, autocommit, readonly, holdability states\r
+ if (realConnection.getTransactionIsolation() != defaultIsolationLevel) {\r
+\r
+ realConnection.setTransactionIsolation(defaultIsolationLevel);\r
+ }\r
+\r
+ if (!realConnection.getAutoCommit())\r
+ realConnection.setAutoCommit(true);\r
+\r
+ if (realConnection.isReadOnly() != defaultReadOnly)\r
+ realConnection.setReadOnly(defaultReadOnly);\r
+\r
+ if (realConnection.getHoldability() != JDBC30Translation.HOLD_CURSORS_OVER_COMMIT)\r
+ realConnection.setHoldability(JDBC30Translation.HOLD_CURSORS_OVER_COMMIT);\r
+\r
+ // reset any remaining state of the connection\r
+ realConnection.resetFromPool();\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(realConnection.transactionIsIdle(),\r
+ "real connection should have been idle at this point"); \r
+ }\r
+ }\r
+\r
+ /**\r
+ Close the Pooled connection.\r
+\r
+ @exception SQLException - if a database-access error occurs.\r
+ */\r
+ public synchronized void close() throws SQLException\r
+ {\r
+ if (!isActive)\r
+ return;\r
+\r
+ closeCurrentConnectionHandle();\r
+ try {\r
+ if (realConnection != null) {\r
+ if (!realConnection.isClosed())\r
+ realConnection.close();\r
+ }\r
+\r
+ } finally {\r
+\r
+ realConnection = null; // make sure I am not accessed again.\r
+ isActive = false;\r
+ eventListener = null;\r
+ }\r
+ }\r
+\r
+ /**\r
+ Add an event listener.\r
+ */\r
+ public final synchronized void addConnectionEventListener(ConnectionEventListener listener) \r
+ {\r
+ if (!isActive)\r
+ return;\r
+ if (listener == null)\r
+ return;\r
+ if (eventListener == null)\r
+ eventListener = new Vector();\r
+ eventListener.addElement(listener);\r
+ }\r
+\r
+ /**\r
+ Remove an event listener.\r
+ */\r
+ public final synchronized void removeConnectionEventListener(ConnectionEventListener listener)\r
+ {\r
+ if (listener == null)\r
+ return;\r
+ if (eventListener != null)\r
+ eventListener.removeElement(listener);\r
+ }\r
+\r
+ /*\r
+ * class specific method\r
+ */\r
+\r
+ // called by ConnectionHandle when it needs to forward things to the\r
+ // underlying connection\r
+ public synchronized EngineConnection getRealConnection()\r
+ throws SQLException\r
+ {\r
+ checkActive();\r
+\r
+ return realConnection;\r
+ }\r
+\r
+ /**\r
+ * @return The underlying language connection.\r
+ */\r
+ public synchronized LanguageConnectionContext getLanguageConnection()\r
+ throws SQLException\r
+ {\r
+ checkActive();\r
+\r
+ return realConnection.getLanguageConnection();\r
+ }\r
+\r
+\r
+ // my conneciton handle has caught an error (actually, the real connection\r
+ // has already handled the error, we just need to nofity the listener an\r
+ // error is about to be thrown to the app).\r
+ public synchronized void notifyError(SQLException exception)\r
+ {\r
+ // only report fatal error to the connection pool manager \r
+ if (exception.getErrorCode() < ExceptionSeverity.SESSION_SEVERITY)\r
+ return;\r
+\r
+ // tell my listeners an exception is about to be thrown\r
+ if (eventListener != null && eventListener.size() > 0)\r
+ {\r
+ ConnectionEvent errorEvent = new ConnectionEvent(this, exception);\r
+\r
+ for (Enumeration e = eventListener.elements();\r
+ e.hasMoreElements(); )\r
+ {\r
+ ConnectionEventListener l =\r
+ (ConnectionEventListener)e.nextElement();\r
+ l.connectionErrorOccurred(errorEvent);\r
+ }\r
+ }\r
+ }\r
+\r
+\r
+ \r
+\r
+ final void checkActive() throws SQLException {\r
+ if (!isActive)\r
+ throw Util.noCurrentConnection();\r
+ }\r
+\r
+\r
+ /*\r
+ ** BrokeredConnectionControl api\r
+ */\r
+\r
+ /**\r
+ Returns true if isolation level has been set using either JDBC api or SQL\r
+ */\r
+ public boolean isIsolationLevelSetUsingSQLorJDBC() throws SQLException {\r
+ if (realConnection != null)\r
+ return realConnection.getLanguageConnection().isIsolationLevelSetUsingSQLorJDBC();\r
+ else\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ Reset the isolation level flag used to keep state in \r
+ BrokeredConnection. It will get set to true when isolation level \r
+ is set using JDBC/SQL. It will get reset to false at the start\r
+ and the end of a global transaction.\r
+ */\r
+ public void resetIsolationLevelFlag() throws SQLException {\r
+ realConnection.getLanguageConnection().resetIsolationLevelFlagUsedForSQLandJDBC();\r
+ }\r
+ \r
+ \r
+ /**\r
+ Notify the control class that a SQLException was thrown\r
+ during a call on one of the brokered connection's methods.\r
+ */\r
+ public void notifyException(SQLException sqle) {\r
+ this.notifyError(sqle);\r
+ }\r
+\r
+\r
+ /**\r
+ Allow control over setting auto commit mode.\r
+ */\r
+ public void checkAutoCommit(boolean autoCommit) throws SQLException {\r
+ }\r
+\r
+ /**\r
+ Are held cursors allowed.\r
+ */\r
+ public int checkHoldCursors(int holdability, boolean downgrade)\r
+ throws SQLException\r
+ {\r
+ return holdability;\r
+ }\r
+\r
+ /**\r
+ Allow control over creating a Savepoint (JDBC 3.0)\r
+ */\r
+ public void checkSavepoint() throws SQLException {\r
+ }\r
+\r
+ /**\r
+ Allow control over calling rollback.\r
+ */\r
+ public void checkRollback() throws SQLException {\r
+ }\r
+\r
+ /**\r
+ Allow control over calling commit.\r
+ */\r
+ public void checkCommit() throws SQLException {\r
+ }\r
+\r
+ /**\r
+ Close called on BrokeredConnection. If this call\r
+ returns true then getRealConnection().close() will be called.\r
+ \r
+ \r
+ Notify listners that connection is closed.\r
+ Don't close the underlying real connection as\r
+ it is pooled.\r
+ */\r
+ public synchronized boolean closingConnection() throws SQLException { \r
+ //DERBY-2142-Null out the connection handle BEFORE notifying listeners.\r
+ //At time of the callback the PooledConnection must be \r
+ //disassociated from its previous logical connection.\r
+ //If not there is a risk that the Pooled\r
+ //Connection could be returned to the pool, ready for pickup by a \r
+ //new thread. This new thread then might obtain a java.sql.Connection \r
+ //whose reference might get assigned to the currentConnectionHandle \r
+ //field, meanwhile the previous thread completes the close making \r
+ //the newly assigned currentConnectionHandle null, resulting in an NPE.\r
+ currentConnectionHandle = null;\r
+ // tell my listeners I am closed \r
+ if (eventListener != null && eventListener.size() > 0)\r
+ {\r
+ ConnectionEvent closeEvent = new ConnectionEvent(this);\r
+\r
+ for (Enumeration e = eventListener.elements();\r
+ e.hasMoreElements(); )\r
+ {\r
+ ConnectionEventListener l =\r
+ (ConnectionEventListener)e.nextElement();\r
+ l.connectionClosed(closeEvent);\r
+ }\r
+ }\r
+\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ No need to wrap statements for PooledConnections.\r
+ */\r
+ public Statement wrapStatement(Statement s) throws SQLException {\r
+ return s;\r
+ }\r
+ /**\r
+ * Call the setBrokeredConnectionControl method inside the \r
+ * EmbedPreparedStatement class to set the BrokeredConnectionControl \r
+ * variable to this instance of EmbedPooledConnection\r
+ * This will then be used to call the onStatementErrorOccurred\r
+ * and onStatementClose events when the corresponding events\r
+ * occur on the PreparedStatement\r
+ *\r
+ * @param ps PreparedStatment to be wrapped\r
+ * @param sql String\r
+ * @param generatedKeys Object\r
+ * @return returns the wrapped PreparedStatement\r
+ * @throws java.sql.SQLException\r
+ */\r
+ public PreparedStatement wrapStatement(PreparedStatement ps, String sql, Object generatedKeys) throws SQLException {\r
+ /*\r
+ \r
+ */\r
+ EmbedPreparedStatement ps_ = (EmbedPreparedStatement)ps;\r
+ ps_.setBrokeredConnectionControl(this);\r
+ return (PreparedStatement)ps_;\r
+ }\r
+ \r
+ /**\r
+ * Call the setBrokeredConnectionControl method inside the \r
+ * EmbedCallableStatement class to set the BrokeredConnectionControl \r
+ * variable to this instance of EmbedPooledConnection\r
+ * This will then be used to call the onStatementErrorOccurred\r
+ * and onStatementClose events when the corresponding events\r
+ * occur on the CallableStatement\r
+ *\r
+ * @param cs CallableStatment to be wrapped\r
+ * @param sql String\r
+ * @return returns the wrapped CallableStatement\r
+ * @throws java.sql.SQLException\r
+ */\r
+ public CallableStatement wrapStatement(CallableStatement cs, String sql) throws SQLException {\r
+ EmbedCallableStatement cs_ = (EmbedCallableStatement)cs;\r
+ cs_.setBrokeredConnectionControl(this);\r
+ return (CallableStatement)cs_;\r
+ }\r
+ \r
+ /** \r
+ * Get the string representation of this pooled connection.\r
+ *\r
+ * A pooled connection is assigned a separate id from a physical \r
+ * connection. When a container calls PooledConnection.toString(), \r
+ * it gets the string representation of this id. This is useful for \r
+ * developers implementing connection pools when they are trying to\r
+ * debug pooled connections. \r
+ *\r
+ * @return a string representation of the uniquie id for this pooled\r
+ * connection.\r
+ *\r
+ */\r
+ public String toString()\r
+ {\r
+ if ( connString == null )\r
+ {\r
+ String physicalConnString = isActive ?\r
+ realConnection.toString() : "<none>";\r
+ \r
+ connString = \r
+ this.getClass().getName() + "@" + this.hashCode() + " " +\r
+ "(ID = " + connectionId + "), " +\r
+ "Physical Connection = " + physicalConnString;\r
+ } \r
+ \r
+ return connString;\r
+ }\r
+ \r
+ /*-----------------------------------------------------------------*/\r
+ /*\r
+ * These methods are from the BrokeredConnectionControl interface. \r
+ * These methods are needed to provide StatementEvent support for \r
+ * derby. \r
+ * They are actually implemented in EmbedPooledConnection40 but have\r
+ * a dummy implementation here so that the compilation wont fail when they\r
+ * are compiled with jdk1.4\r
+ */\r
+ \r
+ /**\r
+ * Dummy implementation for the actual methods found in \r
+ * org.apache.derby.jdbc.EmbedPooledConnection40\r
+ * @param statement PreparedStatement\r
+ */\r
+ public void onStatementClose(PreparedStatement statement) {\r
+ \r
+ }\r
+ \r
+ /**\r
+ * Dummy implementation for the actual methods found in \r
+ * org.apache.derby.jdbc.EmbedPooledConnection40\r
+ * @param statement PreparedStatement\r
+ * @param sqle SQLException \r
+ */\r
+ public void onStatementErrorOccurred(PreparedStatement statement,\r
+ SQLException sqle) {\r
+ \r
+ }\r
+\r
+ public void addStatementEventListener(StatementEventListener listener) {\r
+ // TODO Auto-generated method stub\r
+ \r
+ }\r
+\r
+ public void removeStatementEventListener(StatementEventListener listener) {\r
+ // TODO Auto-generated method stub\r
+ \r
+ }\r
+}\r