001: /*
002:
003: Derby - Class org.apache.derby.iapi.jdbc.BrokeredConnection
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.iapi.jdbc;
023:
024: import java.sql.Connection;
025: import java.sql.Statement;
026: import java.sql.PreparedStatement;
027: import java.sql.CallableStatement;
028: import java.sql.DatabaseMetaData;
029: import java.sql.SQLException;
030: import java.sql.SQLWarning;
031:
032: import org.apache.derby.impl.jdbc.EmbedSQLWarning;
033: import org.apache.derby.impl.jdbc.Util;
034:
035: import java.io.ObjectOutput;
036: import java.io.ObjectInput;
037:
038: import java.lang.reflect.*;
039:
040: import org.apache.derby.iapi.reference.JDBC30Translation;
041: import org.apache.derby.iapi.error.PublicAPI;
042: import org.apache.derby.iapi.error.StandardException;
043: import org.apache.derby.shared.common.reference.SQLState;
044:
045: /**
046: * This is a rudimentary connection that delegates
047: * EVERYTHING to Connection.
048: */
049: public class BrokeredConnection implements EngineConnection {
050:
051: // default for Derby
052: int stateHoldability = JDBC30Translation.HOLD_CURSORS_OVER_COMMIT;
053:
054: final BrokeredConnectionControl control;
055: private boolean isClosed;
056: private String connString;
057:
058: /**
059: Maintain state as seen by this Connection handle, not the state
060: of the underlying Connection it is attached to.
061: */
062: private int stateIsolationLevel;
063: private boolean stateReadOnly;
064: private boolean stateAutoCommit;
065:
066: /////////////////////////////////////////////////////////////////////////
067: //
068: // CONSTRUCTORS
069: //
070: /////////////////////////////////////////////////////////////////////////
071:
072: public BrokeredConnection(BrokeredConnectionControl control) {
073: this .control = control;
074: }
075:
076: public final void setAutoCommit(boolean autoCommit)
077: throws SQLException {
078: try {
079: control.checkAutoCommit(autoCommit);
080:
081: getRealConnection().setAutoCommit(autoCommit);
082:
083: stateAutoCommit = autoCommit;
084: } catch (SQLException sqle) {
085: notifyException(sqle);
086: throw sqle;
087: }
088: }
089:
090: public final boolean getAutoCommit() throws SQLException {
091: try {
092: return getRealConnection().getAutoCommit();
093: } catch (SQLException sqle) {
094: notifyException(sqle);
095: throw sqle;
096: }
097: }
098:
099: public final Statement createStatement() throws SQLException {
100: try {
101: return control.wrapStatement(getRealConnection()
102: .createStatement());
103: } catch (SQLException sqle) {
104: notifyException(sqle);
105: throw sqle;
106: }
107: }
108:
109: public final PreparedStatement prepareStatement(String sql)
110: throws SQLException {
111: try {
112: return control.wrapStatement(getRealConnection()
113: .prepareStatement(sql), sql, null);
114: } catch (SQLException sqle) {
115: notifyException(sqle);
116: throw sqle;
117: }
118: }
119:
120: public final CallableStatement prepareCall(String sql)
121: throws SQLException {
122: try {
123: return control.wrapStatement(getRealConnection()
124: .prepareCall(sql), sql);
125: } catch (SQLException sqle) {
126: notifyException(sqle);
127: throw sqle;
128: }
129: }
130:
131: public final String nativeSQL(String sql) throws SQLException {
132: try {
133: return getRealConnection().nativeSQL(sql);
134: } catch (SQLException sqle) {
135: notifyException(sqle);
136: throw sqle;
137: }
138: }
139:
140: public final void commit() throws SQLException {
141: try {
142: control.checkCommit();
143: getRealConnection().commit();
144: } catch (SQLException sqle) {
145: notifyException(sqle);
146: throw sqle;
147: }
148: }
149:
150: public final void rollback() throws SQLException {
151: try {
152: control.checkRollback();
153: getRealConnection().rollback();
154: } catch (SQLException sqle) {
155: notifyException(sqle);
156: throw sqle;
157: }
158: }
159:
160: public final void close() throws SQLException {
161: if (isClosed)
162: return;
163:
164: try {
165: if (!control.closingConnection()) {
166: isClosed = true;
167: return;
168: }
169:
170: isClosed = true;
171:
172: getRealConnection().close();
173: } catch (SQLException sqle) {
174: notifyException(sqle);
175: throw sqle;
176: }
177: }
178:
179: public final boolean isClosed() throws SQLException {
180: if (isClosed)
181: return true;
182: try {
183: boolean realIsClosed = getRealConnection().isClosed();
184: if (realIsClosed) {
185: control.closingConnection();
186: isClosed = true;
187: }
188: return realIsClosed;
189: } catch (SQLException sqle) {
190: notifyException(sqle);
191: throw sqle;
192: }
193: }
194:
195: public final SQLWarning getWarnings() throws SQLException {
196: try {
197: return getRealConnection().getWarnings();
198: } catch (SQLException sqle) {
199: notifyException(sqle);
200: throw sqle;
201: }
202: }
203:
204: public final void clearWarnings() throws SQLException {
205: try {
206: getRealConnection().clearWarnings();
207: } catch (SQLException sqle) {
208: notifyException(sqle);
209: throw sqle;
210: }
211: }
212:
213: public final DatabaseMetaData getMetaData() throws SQLException {
214: try {
215: return getRealConnection().getMetaData();
216: } catch (SQLException sqle) {
217: notifyException(sqle);
218: throw sqle;
219: }
220: }
221:
222: public final void setReadOnly(boolean readOnly) throws SQLException {
223: try {
224: getRealConnection().setReadOnly(readOnly);
225: stateReadOnly = readOnly;
226: } catch (SQLException sqle) {
227: notifyException(sqle);
228: throw sqle;
229: }
230: }
231:
232: public final boolean isReadOnly() throws SQLException {
233: try {
234: return getRealConnection().isReadOnly();
235: } catch (SQLException sqle) {
236: notifyException(sqle);
237: throw sqle;
238: }
239: }
240:
241: public final void setCatalog(String catalog) throws SQLException {
242: try {
243: getRealConnection().setCatalog(catalog);
244: } catch (SQLException sqle) {
245: notifyException(sqle);
246: throw sqle;
247: }
248: }
249:
250: public final String getCatalog() throws SQLException {
251: try {
252: return getRealConnection().getCatalog();
253: } catch (SQLException sqle) {
254: notifyException(sqle);
255: throw sqle;
256: }
257: }
258:
259: public final void setTransactionIsolation(int level)
260: throws SQLException {
261: try {
262: getRealConnection().setTransactionIsolation(level);
263: stateIsolationLevel = level;
264: } catch (SQLException sqle) {
265: notifyException(sqle);
266: throw sqle;
267: }
268: }
269:
270: public final int getTransactionIsolation() throws SQLException {
271: try {
272: return getRealConnection().getTransactionIsolation();
273: } catch (SQLException sqle) {
274: notifyException(sqle);
275: throw sqle;
276: }
277: }
278:
279: public final Statement createStatement(int resultSetType,
280: int resultSetConcurrency) throws SQLException {
281: try {
282: return control.wrapStatement(getRealConnection()
283: .createStatement(resultSetType,
284: resultSetConcurrency));
285: } catch (SQLException se) {
286: notifyException(se);
287: throw se;
288: }
289: }
290:
291: public final PreparedStatement prepareStatement(String sql,
292: int resultSetType, int resultSetConcurrency)
293: throws SQLException {
294: try {
295: return control.wrapStatement(getRealConnection()
296: .prepareStatement(sql, resultSetType,
297: resultSetConcurrency), sql, null);
298: } catch (SQLException se) {
299: notifyException(se);
300: throw se;
301: }
302: }
303:
304: public final CallableStatement prepareCall(String sql,
305: int resultSetType, int resultSetConcurrency)
306: throws SQLException {
307: try {
308: return control.wrapStatement(getRealConnection()
309: .prepareCall(sql, resultSetType,
310: resultSetConcurrency), sql);
311: } catch (SQLException se) {
312: notifyException(se);
313: throw se;
314: }
315: }
316:
317: public java.util.Map getTypeMap() throws SQLException {
318: try {
319: return getRealConnection().getTypeMap();
320: } catch (SQLException se) {
321: notifyException(se);
322: throw se;
323: }
324: }
325:
326: public final void setTypeMap(java.util.Map map) throws SQLException {
327: try {
328: getRealConnection().setTypeMap(map);
329: } catch (SQLException se) {
330: notifyException(se);
331: throw se;
332: }
333: }
334:
335: /////////////////////////////////////////////////////////////////////////
336: //
337: // MINIONS
338: //
339: /////////////////////////////////////////////////////////////////////////
340:
341: /**
342: * A little indirection for getting the real connection.
343: *
344: * @return the current connection
345: */
346: final EngineConnection getRealConnection() throws SQLException {
347: if (isClosed)
348: throw Util.noCurrentConnection();
349:
350: return control.getRealConnection();
351: }
352:
353: final void notifyException(SQLException sqle) {
354: if (!isClosed)
355: control.notifyException(sqle);
356: }
357:
358: /**
359: Sync up the state of the underlying connection
360: with the state of this new handle.
361: */
362: public void syncState() throws SQLException {
363: EngineConnection conn = getRealConnection();
364:
365: stateIsolationLevel = conn.getTransactionIsolation();
366: stateReadOnly = conn.isReadOnly();
367: stateAutoCommit = conn.getAutoCommit();
368: stateHoldability = conn.getHoldability();
369: }
370:
371: /**
372: Isolation level state in BrokeredConnection can get out of sync
373: if the isolation is set using SQL rather than JDBC. In order to
374: ensure correct state level information, this method is called
375: at the start and end of a global transaction.
376: */
377: public void getIsolationUptoDate() throws SQLException {
378: if (control.isIsolationLevelSetUsingSQLorJDBC()) {
379: stateIsolationLevel = getRealConnection()
380: .getTransactionIsolation();
381: control.resetIsolationLevelFlag();
382: }
383: }
384:
385: /**
386: Set the state of the underlying connection according to the
387: state of this connection's view of state.
388:
389: @param complete If true set the complete state of the underlying
390: Connection, otherwise set only the Connection related state (ie.
391: the non-transaction specific state).
392:
393:
394: */
395: public void setState(boolean complete) throws SQLException {
396: Class[] CONN_PARAM = { Integer.TYPE };
397: Object[] CONN_ARG = { new Integer(stateHoldability) };
398:
399: Connection conn = getRealConnection();
400:
401: if (complete) {
402: conn.setTransactionIsolation(stateIsolationLevel);
403: conn.setReadOnly(stateReadOnly);
404: conn.setAutoCommit(stateAutoCommit);
405: // make the underlying connection pick my holdability state
406: // since holdability is a state of the connection handle
407: // not the underlying transaction.
408: // jdk13 does not have Connection.setHoldability method and hence using
409: // reflection to cover both jdk13 and higher jdks
410: try {
411: Method sh = conn.getClass().getMethod("setHoldability",
412: CONN_PARAM);
413: sh.invoke(conn, CONN_ARG);
414: } catch (Exception e) {
415: throw PublicAPI.wrapStandardException(StandardException
416: .plainWrapException(e));
417: }
418: }
419: }
420:
421: public BrokeredStatement newBrokeredStatement(
422: BrokeredStatementControl statementControl)
423: throws SQLException {
424: return new BrokeredStatement(statementControl, getJDBCLevel());
425: }
426:
427: public BrokeredPreparedStatement newBrokeredStatement(
428: BrokeredStatementControl statementControl, String sql,
429: Object generatedKeys) throws SQLException {
430: return new BrokeredPreparedStatement(statementControl,
431: getJDBCLevel(), sql);
432: }
433:
434: public BrokeredCallableStatement newBrokeredStatement(
435: BrokeredStatementControl statementControl, String sql)
436: throws SQLException {
437: return new BrokeredCallableStatement(statementControl,
438: getJDBCLevel(), sql);
439: }
440:
441: /**
442: * set the DrdaId for this connection. The drdaID prints with the
443: * statement text to the errror log
444: * @param drdaID drdaID to be used for this connection
445: *
446: */
447: public final void setDrdaID(String drdaID) {
448: try {
449: getRealConnection().setDrdaID(drdaID);
450: } catch (SQLException sqle) {
451: // connection is closed, just ignore drdaId
452: // since connection cannot be used.
453: }
454: }
455:
456: /**
457: * Set the internal isolation level to use for preparing statements.
458: * Subsequent prepares will use this isoalation level
459: * @param level - internal isolation level
460: * @throws SQLException
461: * See EmbedConnection#setPrepareIsolation
462: *
463: */
464: public final void setPrepareIsolation(int level)
465: throws SQLException {
466: getRealConnection().setPrepareIsolation(level);
467: }
468:
469: /**
470: * get the isolation level that is currently being used to prepare
471: * statements (used for network server)
472: *
473: * @throws SQLException
474: * @return current prepare isolation level
475: * See EmbedConnection#getPrepareIsolation
476: */
477: public final int getPrepareIsolation() throws SQLException {
478: return getRealConnection().getPrepareIsolation();
479: }
480:
481: /**
482: * Add a SQLWarning to this Connection object.
483: * @throws SQLException
484: */
485: public final void addWarning(SQLWarning w) throws SQLException {
486: getRealConnection().addWarning(w);
487: }
488:
489: /**
490: * Checks if the connection is closed and throws an exception if
491: * it is.
492: *
493: * @exception SQLException if the connection is closed
494: */
495: protected final void checkIfClosed() throws SQLException {
496: if (isClosed()) {
497: throw Util.noCurrentConnection();
498: }
499: }
500:
501: /**
502: * Get the string representation for this connection. Return
503: * the class name/hash code and various debug information.
504: *
505: * @return unique string representation for this connection
506: */
507: public String toString() {
508: if (connString == null) {
509: String wrappedString;
510: try {
511: wrappedString = getRealConnection().toString();
512: } catch (SQLException e) {
513: wrappedString = "<none>";
514: }
515:
516: connString = this .getClass().getName() + "@"
517: + this .hashCode() + ", Wrapped Connection = "
518: + wrappedString;
519: }
520:
521: return connString;
522: }
523:
524: int getJDBCLevel() {
525: return 2;
526: }
527:
528: /*
529: * JDBC 3.0 methods that are exposed through EngineConnection.
530: */
531:
532: /**
533: * Prepare statement with explicit holdability.
534: */
535: public final PreparedStatement prepareStatement(String sql,
536: int resultSetType, int resultSetConcurrency,
537: int resultSetHoldability) throws SQLException {
538: try {
539: resultSetHoldability = statementHoldabilityCheck(resultSetHoldability);
540:
541: return control.wrapStatement(
542: getRealConnection().prepareStatement(sql,
543: resultSetType, resultSetConcurrency,
544: resultSetHoldability), sql, null);
545: } catch (SQLException se) {
546: notifyException(se);
547: throw se;
548: }
549: }
550:
551: /**
552: * Get the holdability for statements created by this connection
553: * when holdability is not passed in.
554: */
555: public final int getHoldability() throws SQLException {
556: try {
557: return getRealConnection().getHoldability();
558: } catch (SQLException se) {
559: notifyException(se);
560: throw se;
561: }
562: }
563:
564: /*
565: ** Methods private to the class.
566: */
567:
568: /**
569: * Check the result set holdability when creating a statement
570: * object. Section 16.1.3.1 of JDBC 4.0 (proposed final draft)
571: * says the driver may change the holdabilty and add a SQLWarning
572: * to the Connection object.
573: *
574: * This work-in-progress implementation throws an exception
575: * to match the old behaviour just as part of incremental development.
576: */
577: final int statementHoldabilityCheck(int resultSetHoldability)
578: throws SQLException {
579: int holdability = control.checkHoldCursors(
580: resultSetHoldability, true);
581: if (holdability != resultSetHoldability) {
582: SQLWarning w = EmbedSQLWarning
583: .newEmbedSQLWarning(SQLState.HOLDABLE_RESULT_SET_NOT_AVAILABLE);
584:
585: addWarning(w);
586: }
587:
588: return holdability;
589:
590: }
591: }
|