001: /*
002: * Distributed as part of c3p0 v.0.9.1.2
003: *
004: * Copyright (C) 2005 Machinery For Change, Inc.
005: *
006: * Author: Steve Waldman <swaldman@mchange.com>
007: *
008: * This library is free software; you can redistribute it and/or modify
009: * it under the terms of the GNU Lesser General Public License version 2.1, as
010: * published by the Free Software Foundation.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public License
018: * along with this software; see the file LICENSE. If not, write to the
019: * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
020: * Boston, MA 02111-1307, USA.
021: */
022:
023: package com.mchange.v2.c3p0.util;
024:
025: import java.sql.*;
026: import javax.sql.*;
027: import java.lang.reflect.*;
028: import java.util.Random;
029: import com.mchange.v2.sql.SqlUtils;
030: import com.mchange.v2.c3p0.C3P0ProxyConnection;
031:
032: public final class TestUtils {
033: private final static Method OBJECT_EQUALS;
034: private final static Method IDENTITY_HASHCODE;
035: private final static Method IPCFP;
036:
037: static {
038: try {
039: OBJECT_EQUALS = Object.class.getMethod("equals",
040: new Class[] { Object.class });
041: IDENTITY_HASHCODE = System.class.getMethod(
042: "identityHashCode", new Class[] { Object.class });
043:
044: // is this okay? getting a method from a class while it is still being initialized?
045: IPCFP = TestUtils.class.getMethod(
046: "isPhysicalConnectionForProxy",
047: new Class[] { Connection.class,
048: C3P0ProxyConnection.class });
049: } catch (Exception e) {
050: e.printStackTrace();
051: throw new RuntimeException(
052: "Huh? Can't reflectively get ahold of expected methods?");
053: }
054: }
055:
056: /**
057: * In general, if this method returns true for two distinct C3P0ProxyConnections, it indicates a c3p0 bug.
058: * Once a proxy Connection is close()ed, it should not permit any sort of operation. Prior to Connection
059: * close(), there should be at most one valid proxy Connection associated with a given physical Connection.
060: */
061: public static boolean samePhysicalConnection(
062: C3P0ProxyConnection con1, C3P0ProxyConnection con2)
063: throws SQLException {
064: try {
065: Object out = con1.rawConnectionOperation(IPCFP, null,
066: new Object[] { C3P0ProxyConnection.RAW_CONNECTION,
067: con2 });
068: return ((Boolean) out).booleanValue();
069: } catch (Exception e) {
070: e.printStackTrace();
071: throw SqlUtils.toSQLException(e);
072: }
073: }
074:
075: public static boolean isPhysicalConnectionForProxy(
076: Connection physicalConnection, C3P0ProxyConnection proxy)
077: throws SQLException {
078: try {
079: Object out = proxy
080: .rawConnectionOperation(
081: OBJECT_EQUALS,
082: physicalConnection,
083: new Object[] { C3P0ProxyConnection.RAW_CONNECTION });
084: return ((Boolean) out).booleanValue();
085: } catch (Exception e) {
086: e.printStackTrace();
087: throw SqlUtils.toSQLException(e);
088: }
089: }
090:
091: public static int physicalConnectionIdentityHashCode(
092: C3P0ProxyConnection conn) throws SQLException {
093: try {
094: Object out = conn
095: .rawConnectionOperation(
096: IDENTITY_HASHCODE,
097: null,
098: new Object[] { C3P0ProxyConnection.RAW_CONNECTION });
099: return ((Integer) out).intValue();
100: } catch (Exception e) {
101: e.printStackTrace();
102: throw SqlUtils.toSQLException(e);
103: }
104: }
105:
106: public static DataSource unreliableCommitDataSource(DataSource ds)
107: throws Exception {
108: return (DataSource) Proxy.newProxyInstance(TestUtils.class
109: .getClassLoader(), new Class[] { DataSource.class },
110: new StupidDataSourceInvocationHandler(ds));
111: }
112:
113: private TestUtils() {
114: }
115:
116: static class StupidDataSourceInvocationHandler implements
117: InvocationHandler {
118: DataSource ds;
119: Random r = new Random();
120:
121: StupidDataSourceInvocationHandler(DataSource ds) {
122: this .ds = ds;
123: }
124:
125: public Object invoke(Object proxy, Method method, Object[] args)
126: throws Throwable {
127: if ("getConnection".equals(method.getName())) {
128: Connection conn = (Connection) method.invoke(ds, args);
129: return Proxy.newProxyInstance(TestUtils.class
130: .getClassLoader(),
131: new Class[] { Connection.class },
132: new StupidConnectionInvocationHandler(conn));
133: } else
134: return method.invoke(ds, args);
135: }
136: }
137:
138: static class StupidConnectionInvocationHandler implements
139: InvocationHandler {
140: Connection conn;
141: Random r = new Random();
142:
143: boolean invalid = false;
144:
145: StupidConnectionInvocationHandler(Connection conn) {
146: this .conn = conn;
147: }
148:
149: public Object invoke(Object proxy, Method method, Object[] args)
150: throws Throwable {
151: if ("close".equals(method.getName())) {
152: if (invalid) {
153: new Exception(
154: "Duplicate close() called on Connection!!!")
155: .printStackTrace();
156: } else {
157: //new Exception("CLOSE CALLED ON UNPOOLED DATASOURCE CONNECTION!").printStackTrace();
158: invalid = true;
159: }
160: return null;
161: } else if (invalid)
162: throw new SQLException("Connection closed -- cannot "
163: + method.getName());
164: else if ("commit".equals(method.getName())
165: && r.nextInt(100) == 0) {
166: conn.rollback();
167: throw new SQLException("Random commit exception!!!");
168: } else if (r.nextInt(200) == 0) {
169: conn.rollback();
170: conn.close();
171: throw new SQLException(
172: "Random Fatal Exception Occurred!!!");
173: } else
174: return method.invoke(conn, args);
175: }
176: }
177:
178: }
|