| /* |
| * Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0, |
| * or the Eclipse Distribution License v. 1.0 which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause |
| */ |
| |
| // Contributors: |
| // ailitchev - Bug 256296: Reconnect fails when session loses connectivity; |
| // Bug 256284: Closing anEMF where the database is unavailable results in deployment exception on redeploy. |
| package org.eclipse.persistence.testing.framework; |
| |
| import java.sql.Connection; |
| import java.sql.Driver; |
| import java.sql.DriverManager; |
| import java.sql.DriverPropertyInfo; |
| import java.sql.SQLException; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.logging.Logger; |
| /* |
| * DriverWrapper works together with ConnectionWrapper. |
| * This pair of classes allows to intercept calls to Driver and Connection methods. |
| * DriverWrapper can imitate both the db going down (or losing network connection to it), |
| * and coming back up (or network connection restored). |
| * ConnectionWrapper have the same functionality, but applied to a single connection. |
| * |
| * There's an example of ConnectionWrapper usage in EntityManagerJUnitTestSuite: |
| * testEMCloseAndOpen and testEMFactoryCloseAndOpen. |
| * |
| * To use DriverWrapper in jpa, initialize DriverWrapper - all methods are static - with the original driver name. |
| * |
| * Then create EMF, using PersistenceUnit properties, substitute: |
| * the original driver class for DriverWrapper (optional) and |
| * the original url for "coded" (':' substituted for'*') url (otherwise DriverWrapper would not be called - the original driver would). |
| * If created EMF uses DriverWrapper it will print out connection string that looks like "jdbc*oracle*thin*@localhost*1521*orcl". |
| * |
| * DriverWrapper just passes all the calls to the wrapped driver (the one passed to initialize method): |
| * connect method wraps the created connection into ConnectionWrapper and caches them. |
| * |
| * Unless any of "break" methods called it should function in exactly the same way as original driver, the same for connections, too. |
| * |
| * But of course real fun is in breaking: |
| * breakDriver breaks all the methods (that throw SQLException) of the Driver; |
| * brealOldConnections breaks all connections produced so far; |
| * breakNewConnections ensures that all newly produced connections are broken. |
| * |
| * Any method called on broken connection results in SQLException. |
| * |
| * The simple scenarios used in both EntityManagerJUnitTestSuite tests is imitation of db going down, then coming back: |
| * going down: call breakDriver and breakOldConnections (calling breakNewConnections is possible but won't add anything - |
| * as long as driver is broken there will be no new connections); |
| * coming back: call repairDriver - now new functional new connections could be created, but all old connections are still broken. |
| * |
| * Also you can also break / repair individual connection. |
| * If, say breakOldConnections was performed on DriwerWrapper and repair on ConnectionWrapper the chronologically last call wins. |
| * There's no harm in breaking (or repairing) several times in a row. |
| * |
| * You can pass custom exception string to each break method, otherwise defaults used (the string will be in SQLException, also visible in debugger). |
| * |
| * Another usage that seems useful: stepping through the code in debugger you can trigger SQLException |
| * to be thrown by any Connection method at will |
| * be setting broken flag on ConnectionWrapper to true. |
| * |
| * After the EMF using DriverWrapper is closed, call DriverWrapper.clear() to forget the wrapped driver and clear all the cached ConnectionWrappers. |
| */ |
| public class DriverWrapper implements Driver { |
| |
| // the wrapped driver name |
| static String driverName; |
| // the wrapped driver |
| static Driver driver; |
| |
| // if set to true then methods called on the driver throw exception |
| static boolean driverBroken; |
| static String driverBrokenExceptionString; |
| public static String defaultDriverBrokenExceptionString = "DriverWrapper: driver is broken"; |
| |
| // if set to true then methods called on the connections already acquired throw exception |
| static boolean oldConnectionsBroken; |
| static String oldConnectionsBrokenExceptionString; |
| public static String defaultOldConnectionsBrokenExceptionString = "DriverWrapper: old connections are broken"; |
| |
| // if set to true then methods called on the newly acquired connections will throw exception |
| static boolean newConnectionsBroken; |
| static String newConnectionsBrokenExceptionString; |
| public static String defaultNewConnectionsBrokenExceptionString = "DriverWrapper: new connections are broken"; |
| |
| // all created ConnectionWrappers are cached |
| static HashSet<ConnectionWrapper> connections = new HashSet(); |
| |
| // register with DriverManager |
| static { |
| try { |
| DriverManager.registerDriver(new DriverWrapper()); |
| } catch (SQLException ex) { |
| throw new TestProblemException("registerDriver failed for DriverWrapper", ex); |
| } |
| } |
| |
| public static String codeUrl(String url) { |
| return url.replace(':', '*'); |
| } |
| public static String decodeUrl(String url) { |
| return url.replace('*', ':'); |
| } |
| public static void initialize(String newDriverName) { |
| clear(); |
| driverName = newDriverName; |
| } |
| |
| public static void breakDriver() { |
| breakDriver(defaultDriverBrokenExceptionString); |
| } |
| public static void breakDriver(String exceptionString) { |
| driverBroken = true; |
| driverBrokenExceptionString = exceptionString; |
| } |
| public static void repairDriver() { |
| driverBroken = false; |
| driverBrokenExceptionString = null; |
| } |
| |
| public static void breakOldConnections() { |
| breakOldConnections(defaultOldConnectionsBrokenExceptionString); |
| } |
| public static void breakOldConnections(String exceptionString) { |
| oldConnectionsBroken = true; |
| oldConnectionsBrokenExceptionString = exceptionString; |
| Iterator<ConnectionWrapper> it = connections.iterator(); |
| while(it.hasNext()) { |
| it.next().breakConnection(oldConnectionsBrokenExceptionString); |
| } |
| } |
| public static void repairOldConnections() { |
| oldConnectionsBroken = false; |
| oldConnectionsBrokenExceptionString = null; |
| Iterator<ConnectionWrapper> it = connections.iterator(); |
| while(it.hasNext()) { |
| it.next().repairConnection(); |
| } |
| } |
| |
| public static void breakNewConnections() { |
| breakNewConnections(defaultNewConnectionsBrokenExceptionString); |
| } |
| public static void breakNewConnections(String exceptionString) { |
| newConnectionsBroken = true; |
| newConnectionsBrokenExceptionString = exceptionString; |
| } |
| public static void repairNewConnections() { |
| newConnectionsBroken = false; |
| newConnectionsBrokenExceptionString = null; |
| } |
| |
| public static void breakAll() { |
| breakDriver(); |
| breakNewConnections(); |
| breakOldConnections(); |
| } |
| |
| public static void repairAll() { |
| repairDriver(); |
| repairNewConnections(); |
| repairOldConnections(); |
| } |
| |
| public static void clear() { |
| repairAll(); |
| Iterator<ConnectionWrapper> it = connections.iterator(); |
| while(it.hasNext()) { |
| try { |
| it.next().close(); |
| } catch (SQLException ex) { |
| //ignore |
| } |
| } |
| connections.clear(); |
| driver = null; |
| driverName = null; |
| } |
| |
| static Driver getDriver() { |
| if(driver == null) { |
| try { |
| driver = (Driver)Class.forName(driverName, true, Thread.currentThread().getContextClassLoader()).getConstructor().newInstance(); |
| } catch (Exception ex) { |
| throw new TestProblemException("DriverWrapper: failed to instantiate " + driverName, ex); |
| } |
| } |
| return driver; |
| } |
| |
| public static boolean driverBroken() { |
| return driverBroken; |
| } |
| public static String getDriverBrokenExceptionString() { |
| return driverBrokenExceptionString; |
| } |
| |
| public static boolean oldConnectionsBroken() { |
| return oldConnectionsBroken; |
| } |
| public static String getOldConnectionsBrokenExceptionString() { |
| return oldConnectionsBrokenExceptionString; |
| } |
| |
| public static boolean newConnectionsBroken() { |
| return newConnectionsBroken; |
| } |
| public static String getNewConnectionsBrokenExceptionString() { |
| return newConnectionsBrokenExceptionString; |
| } |
| |
| /* |
| * The following methods implement Driver interface |
| */ |
| @Override |
| public Connection connect(String url, java.util.Properties info) throws SQLException { |
| if(driverBroken) { |
| throw new SQLException(getDriverBrokenExceptionString()); |
| } |
| String decodedUrl = decodeUrl(url); |
| if(driverName != null) { |
| Connection internalConn = getDriver().connect(decodedUrl, info); |
| if (internalConn == null) { |
| // The driver should return "null" if it realizes it is the wrong kind of driver to connect to the given URL. |
| return null; |
| } |
| ConnectionWrapper conn = new ConnectionWrapper(internalConn); |
| connections.add(conn); |
| return conn; |
| } else { |
| // non-initialized DriverWrapper should be ignored by DriverManager. |
| return null; |
| } |
| } |
| |
| @Override |
| public boolean acceptsURL(String url) throws SQLException { |
| if(driverName != null) { |
| if(driverBroken) { |
| throw new SQLException(getDriverBrokenExceptionString()); |
| } |
| String decodedUrl = decodeUrl(url); |
| return getDriver().acceptsURL(decodedUrl); |
| } else { |
| // non-initialized DriverWrapper should be ignored by DriverManager. |
| return false; |
| } |
| } |
| |
| @Override |
| public DriverPropertyInfo[] getPropertyInfo(String url, java.util.Properties info) throws SQLException { |
| if(driverBroken) { |
| throw new SQLException(getDriverBrokenExceptionString()); |
| } |
| return getDriver().getPropertyInfo(url, info); |
| } |
| |
| @Override |
| public int getMajorVersion() { |
| return getDriver().getMajorVersion(); |
| } |
| |
| @Override |
| public int getMinorVersion() { |
| return getDriver().getMinorVersion(); |
| } |
| |
| @Override |
| public boolean jdbcCompliant() { |
| return getDriver().jdbcCompliant(); |
| } |
| |
| @Override |
| public Logger getParentLogger() {return null;} |
| } |