001: /*
002:
003: Derby - Class org.apache.derby.jdbc.EmbedPooledConnection
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to You under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.jdbc;
023:
024: import org.apache.derby.iapi.services.sanity.SanityManager;
025: import org.apache.derby.iapi.reference.Property;
026: import org.apache.derby.iapi.error.ExceptionSeverity;
027: import org.apache.derby.iapi.reference.JDBC30Translation;
028:
029: /* import impl class */
030: import org.apache.derby.impl.jdbc.Util;
031: import org.apache.derby.impl.jdbc.EmbedConnection;
032: import org.apache.derby.iapi.jdbc.BrokeredConnection;
033: import org.apache.derby.iapi.jdbc.BrokeredConnectionControl;
034: import org.apache.derby.iapi.jdbc.EngineConnection;
035: import org.apache.derby.impl.jdbc.EmbedPreparedStatement;
036: import org.apache.derby.impl.jdbc.EmbedCallableStatement;
037:
038: import java.sql.Connection;
039: import java.sql.SQLException;
040: import java.sql.Statement;
041: import java.sql.PreparedStatement;
042: import java.sql.CallableStatement;
043:
044: import java.util.Vector;
045: import java.util.Enumeration;
046:
047: /* -- New jdbc 20 extension types --- */
048: import javax.sql.DataSource;
049: import javax.sql.PooledConnection;
050: import javax.sql.ConnectionEventListener;
051: import javax.sql.ConnectionEvent;
052:
053: /**
054: A PooledConnection object is a connection object that provides hooks for
055: connection pool management.
056:
057: <P>This is Derby's implementation of a PooledConnection for use in
058: the following environments:
059: <UL>
060: <LI> JDBC 3.0 - Java 2 - JDK 1.4, J2SE 5.0
061: <LI> JDBC 2.0 - Java 2 - JDK 1.2,1.3
062: </UL>
063:
064: */
065: class EmbedPooledConnection implements javax.sql.PooledConnection,
066: BrokeredConnectionControl {
067:
068: /** Static counter for connection ids */
069: private static int idCounter = 0;
070:
071: /** The id for this connection. */
072: private int connectionId;
073:
074: /** the connection string */
075: private String connString;
076:
077: private Vector eventListener; // who wants to know I am closed or error
078:
079: EmbedConnection realConnection;
080: int defaultIsolationLevel;
081: private boolean defaultReadOnly;
082: BrokeredConnection currentConnectionHandle;
083:
084: // set up once by the data source
085: final ReferenceableDataSource dataSource;
086: private final String username;
087: private final String password;
088: /**
089: True if the password was passed in on the connection request, false if it came from the data source property.
090: */
091: private final boolean requestPassword;
092:
093: protected boolean isActive;
094:
095: private synchronized int nextId() {
096: return idCounter++;
097: }
098:
099: EmbedPooledConnection(ReferenceableDataSource ds, String u,
100: String p, boolean requestPassword) throws SQLException {
101: connectionId = nextId();
102:
103: dataSource = ds;
104: username = u;
105: password = p;
106: this .requestPassword = requestPassword;
107: isActive = true;
108:
109: // open the connection up front in order to do authentication
110: openRealConnection();
111:
112: }
113:
114: String getUsername() {
115: if (username == null || username.equals(""))
116: return Property.DEFAULT_USER_NAME;
117: else
118: return username;
119: }
120:
121: String getPassword() {
122: if (password == null)
123: return "";
124: else
125: return password;
126: }
127:
128: /**
129: Create an object handle for a database connection.
130:
131: @return a Connection object
132:
133: @exception SQLException - if a database-access error occurs.
134: */
135: public synchronized Connection getConnection() throws SQLException {
136: checkActive();
137:
138: // need to do this in case the connection is forcibly removed without
139: // first being closed.
140: closeCurrentConnectionHandle();
141:
142: // RealConnection is not null if the app server yanks a local
143: // connection from one client and give it to another. In this case,
144: // the real connection is ready to be used. Otherwise, set it up
145: if (realConnection == null) {
146: // first time we establish a connection
147: openRealConnection();
148: } else {
149: resetRealConnection();
150: }
151:
152: // now make a brokered connection wrapper and give this to the user
153: // we reuse the EmbedConnection(ie realConnection).
154: Connection c = getNewCurrentConnectionHandle();
155: return c;
156: }
157:
158: final void openRealConnection() throws SQLException {
159: // first time we establish a connection
160: Connection rc = dataSource.getConnection(username, password,
161: requestPassword);
162:
163: this .realConnection = (EmbedConnection) rc;
164: defaultIsolationLevel = rc.getTransactionIsolation();
165: defaultReadOnly = rc.isReadOnly();
166: if (currentConnectionHandle != null)
167: realConnection
168: .setApplicationConnection(currentConnectionHandle);
169: }
170:
171: final Connection getNewCurrentConnectionHandle() {
172: Connection applicationConnection = currentConnectionHandle = ((org.apache.derby.jdbc.Driver20) (realConnection
173: .getLocalDriver())).newBrokeredConnection(this );
174: realConnection.setApplicationConnection(applicationConnection);
175: return applicationConnection;
176:
177: }
178:
179: /**
180: In this case the Listeners are *not* notified. JDBC 3.0 spec section 11.4
181: */
182: private void closeCurrentConnectionHandle() throws SQLException {
183: if (currentConnectionHandle != null) {
184: Vector tmpEventListener = eventListener;
185: eventListener = null;
186:
187: try {
188: currentConnectionHandle.close();
189: } finally {
190: eventListener = tmpEventListener;
191: }
192:
193: currentConnectionHandle = null;
194: }
195: }
196:
197: void resetRealConnection() throws SQLException {
198:
199: // ensure any outstanding changes from the previous
200: // user are rolledback.
201: realConnection.rollback();
202:
203: // clear any warnings that are left over
204: realConnection.clearWarnings();
205:
206: // need to reset transaction isolation, autocommit, readonly, holdability states
207: if (realConnection.getTransactionIsolation() != defaultIsolationLevel) {
208:
209: realConnection
210: .setTransactionIsolation(defaultIsolationLevel);
211: }
212:
213: if (!realConnection.getAutoCommit())
214: realConnection.setAutoCommit(true);
215:
216: if (realConnection.isReadOnly() != defaultReadOnly)
217: realConnection.setReadOnly(defaultReadOnly);
218:
219: if (realConnection.getHoldability() != JDBC30Translation.HOLD_CURSORS_OVER_COMMIT)
220: realConnection
221: .setHoldability(JDBC30Translation.HOLD_CURSORS_OVER_COMMIT);
222:
223: // reset any remaining state of the connection
224: realConnection.resetFromPool();
225: }
226:
227: /**
228: Close the Pooled connection.
229:
230: @exception SQLException - if a database-access error occurs.
231: */
232: public synchronized void close() throws SQLException {
233: if (!isActive)
234: return;
235:
236: closeCurrentConnectionHandle();
237: try {
238: if (realConnection != null) {
239: if (!realConnection.isClosed())
240: realConnection.close();
241: }
242:
243: } finally {
244:
245: realConnection = null; // make sure I am not accessed again.
246: isActive = false;
247: eventListener = null;
248: }
249: }
250:
251: /**
252: Add an event listener.
253: */
254: public final synchronized void addConnectionEventListener(
255: ConnectionEventListener listener) {
256: if (!isActive)
257: return;
258: if (listener == null)
259: return;
260: if (eventListener == null)
261: eventListener = new Vector();
262: eventListener.addElement(listener);
263: }
264:
265: /**
266: Remove an event listener.
267: */
268: public final synchronized void removeConnectionEventListener(
269: ConnectionEventListener listener) {
270: if (listener == null)
271: return;
272: if (eventListener != null)
273: eventListener.removeElement(listener);
274: }
275:
276: /*
277: * class specific method
278: */
279:
280: // called by ConnectionHandle when it needs to forward things to the
281: // underlying connection
282: public synchronized EngineConnection getRealConnection()
283: throws SQLException {
284: checkActive();
285:
286: return realConnection;
287: }
288:
289: // my conneciton handle has caught an error (actually, the real connection
290: // has already handled the error, we just need to nofity the listener an
291: // error is about to be thrown to the app).
292: public synchronized void notifyError(SQLException exception) {
293: // only report fatal error to the connection pool manager
294: if (exception.getErrorCode() < ExceptionSeverity.SESSION_SEVERITY)
295: return;
296:
297: // tell my listeners an exception is about to be thrown
298: if (eventListener != null && eventListener.size() > 0) {
299: ConnectionEvent errorEvent = new ConnectionEvent(this ,
300: exception);
301:
302: for (Enumeration e = eventListener.elements(); e
303: .hasMoreElements();) {
304: ConnectionEventListener l = (ConnectionEventListener) e
305: .nextElement();
306: l.connectionErrorOccurred(errorEvent);
307: }
308: }
309: }
310:
311: // my conneciton handle is being closed
312: public synchronized void notifyClose() {
313: // tell my listeners I am closed
314: if (eventListener != null && eventListener.size() > 0) {
315: ConnectionEvent closeEvent = new ConnectionEvent(this );
316:
317: for (Enumeration e = eventListener.elements(); e
318: .hasMoreElements();) {
319: ConnectionEventListener l = (ConnectionEventListener) e
320: .nextElement();
321: l.connectionClosed(closeEvent);
322: }
323: }
324: }
325:
326: final void checkActive() throws SQLException {
327: if (!isActive)
328: throw Util.noCurrentConnection();
329: }
330:
331: /*
332: ** BrokeredConnectionControl api
333: */
334:
335: /**
336: Returns true if isolation level has been set using either JDBC api or SQL
337: */
338: public boolean isIsolationLevelSetUsingSQLorJDBC()
339: throws SQLException {
340: if (realConnection != null)
341: return realConnection.getLanguageConnection()
342: .isIsolationLevelSetUsingSQLorJDBC();
343: else
344: return false;
345: }
346:
347: /**
348: Reset the isolation level flag used to keep state in
349: BrokeredConnection. It will get set to true when isolation level
350: is set using JDBC/SQL. It will get reset to false at the start
351: and the end of a global transaction.
352: */
353: public void resetIsolationLevelFlag() throws SQLException {
354: realConnection.getLanguageConnection()
355: .resetIsolationLevelFlagUsedForSQLandJDBC();
356: }
357:
358: /**
359: Notify the control class that a SQLException was thrown
360: during a call on one of the brokered connection's methods.
361: */
362: public void notifyException(SQLException sqle) {
363: this .notifyError(sqle);
364: }
365:
366: /**
367: Allow control over setting auto commit mode.
368: */
369: public void checkAutoCommit(boolean autoCommit) throws SQLException {
370: }
371:
372: /**
373: Are held cursors allowed.
374: */
375: public int checkHoldCursors(int holdability, boolean downgrade)
376: throws SQLException {
377: return holdability;
378: }
379:
380: /**
381: Allow control over creating a Savepoint (JDBC 3.0)
382: */
383: public void checkSavepoint() throws SQLException {
384: }
385:
386: /**
387: Allow control over calling rollback.
388: */
389: public void checkRollback() throws SQLException {
390: }
391:
392: /**
393: Allow control over calling commit.
394: */
395: public void checkCommit() throws SQLException {
396: }
397:
398: /**
399: Close called on BrokeredConnection. If this call
400: returns true then getRealConnection().close() will be called.
401:
402: Don't close the underlying real connection as
403: it is pooled.
404: */
405: public boolean closingConnection() throws SQLException {
406: notifyClose();
407: currentConnectionHandle = null;
408: return false;
409: }
410:
411: /**
412: No need to wrap statements for PooledConnections.
413: */
414: public Statement wrapStatement(Statement s) throws SQLException {
415: return s;
416: }
417:
418: /**
419: * Call the setBrokeredConnectionControl method inside the
420: * EmbedPreparedStatement class to set the BrokeredConnectionControl
421: * variable to this instance of EmbedPooledConnection
422: * This will then be used to call the onStatementErrorOccurred
423: * and onStatementClose events when the corresponding events
424: * occur on the PreparedStatement
425: *
426: * @param ps PreparedStatment to be wrapped
427: * @param sql String
428: * @param generatedKeys Object
429: * @return returns the wrapped PreparedStatement
430: * @throws java.sql.SQLException
431: */
432: public PreparedStatement wrapStatement(PreparedStatement ps,
433: String sql, Object generatedKeys) throws SQLException {
434: /*
435:
436: */
437: EmbedPreparedStatement ps_ = (EmbedPreparedStatement) ps;
438: ps_.setBrokeredConnectionControl(this );
439: return (PreparedStatement) ps_;
440: }
441:
442: /**
443: * Call the setBrokeredConnectionControl method inside the
444: * EmbedCallableStatement class to set the BrokeredConnectionControl
445: * variable to this instance of EmbedPooledConnection
446: * This will then be used to call the onStatementErrorOccurred
447: * and onStatementClose events when the corresponding events
448: * occur on the CallableStatement
449: *
450: * @param cs CallableStatment to be wrapped
451: * @param sql String
452: * @return returns the wrapped CallableStatement
453: * @throws java.sql.SQLException
454: */
455: public CallableStatement wrapStatement(CallableStatement cs,
456: String sql) throws SQLException {
457: EmbedCallableStatement cs_ = (EmbedCallableStatement) cs;
458: cs_.setBrokeredConnectionControl(this );
459: return (CallableStatement) cs_;
460: }
461:
462: /**
463: * Get the string representation of this pooled connection.
464: *
465: * A pooled connection is assigned a separate id from a physical
466: * connection. When a container calls PooledConnection.toString(),
467: * it gets the string representation of this id. This is useful for
468: * developers implementing connection pools when they are trying to
469: * debug pooled connections.
470: *
471: * @return a string representation of the uniquie id for this pooled
472: * connection.
473: *
474: */
475: public String toString() {
476: if (connString == null) {
477: String physicalConnString = isActive ? realConnection
478: .toString() : "<none>";
479:
480: connString = this .getClass().getName() + "@"
481: + this .hashCode() + " " + "(ID = " + connectionId
482: + "), " + "Physical Connection = "
483: + physicalConnString;
484: }
485:
486: return connString;
487: }
488:
489: /*-----------------------------------------------------------------*/
490: /*
491: * These methods are from the BrokeredConnectionControl interface.
492: * These methods are needed to provide StatementEvent support for
493: * derby.
494: * They are actually implemented in EmbedPooledConnection40 but have
495: * a dummy implementation here so that the compilation wont fail when they
496: * are compiled with jdk1.4
497: */
498:
499: /**
500: * Dummy implementation for the actual methods found in
501: * org.apache.derby.jdbc.EmbedPooledConnection40
502: * @param statement PreparedStatement
503: */
504: public void onStatementClose(PreparedStatement statement) {
505:
506: }
507:
508: /**
509: * Dummy implementation for the actual methods found in
510: * org.apache.derby.jdbc.EmbedPooledConnection40
511: * @param statement PreparedStatement
512: * @param sqle SQLException
513: */
514: public void onStatementErrorOccurred(PreparedStatement statement,
515: SQLException sqle) {
516:
517: }
518: }
|