Skip to content

Commit

Permalink
Fixed issue #23769 - no need for reflection
Browse files Browse the repository at this point in the history
Signed-off-by: David Matějček <david.matejcek@omnifish.ee>
  • Loading branch information
dmatej committed Jan 15, 2023
1 parent 7d3c144 commit f664cf1
Show file tree
Hide file tree
Showing 2 changed files with 11 additions and 63 deletions.
@@ -1,4 +1,5 @@
/*
* Copyright (c) 2023 Contributors to the Eclipse Foundation
* Copyright (c) 2012-2018 Oracle and/or its affiliates. All rights reserved.
* Copyright 2004 The Apache Software Foundation
*
Expand Down Expand Up @@ -41,8 +42,8 @@
*/
public class JdbcLeakPrevention {

public List<String> clearJdbcDriverRegistrations() throws SQLException {
List<String> driverNames = new ArrayList<String>();
public List<String> clearJdbcDriverRegistrations(ClassLoader classLoader) throws SQLException {
List<String> driverNames = new ArrayList<>();

/*
* DriverManager.getDrivers() has a nasty side-effect of registering
Expand All @@ -53,7 +54,7 @@ public List<String> clearJdbcDriverRegistrations() throws SQLException {
* ensuring that both original drivers and any loaded as a result of the
* side-effects are all de-registered.
*/
HashSet<Driver> originalDrivers = new HashSet<Driver>();
HashSet<Driver> originalDrivers = new HashSet<>();
Enumeration<Driver> drivers = DriverManager.getDrivers();
while (drivers.hasMoreElements()) {
originalDrivers.add(drivers.nextElement());
Expand All @@ -62,8 +63,7 @@ public List<String> clearJdbcDriverRegistrations() throws SQLException {
while (drivers.hasMoreElements()) {
Driver driver = drivers.nextElement();
// Only unload the drivers this web app loaded
if (driver.getClass().getClassLoader() !=
this.getClass().getClassLoader()) {
if (driver.getClass().getClassLoader() != classLoader) {
continue;
}
// Only report drivers that were originally registered. Skip any
Expand Down
Expand Up @@ -1495,9 +1495,8 @@ public void close() throws IOException {
}

// FIXME: close is called twice = unclear dependencies and order.
super.close();

started = false;
super.close();

int length = files.length;
for (int i = 0; i < length; i++) {
Expand Down Expand Up @@ -1603,72 +1602,21 @@ protected void clearReferences() {
java.beans.Introspector.flushCaches();
}

/**
* Deregister any JDBC drivers registered by the webapp that the webapp
* forgot. This is made unnecessary complex because a) DriverManager
* checks the class loader of the calling class (it would be much easier
* if it checked the context class loader) b) using reflection would
* create a dependency on the DriverManager implementation which can,
* and has, changed.
*
* We can't just create an instance of JdbcLeakPrevention as it will be
* loaded by the common class loader (since it's .class file is in the
* $CATALINA_HOME/lib directory). This would fail DriverManager's check
* on the class loader of the calling class. So, we load the bytes via
* our parent class loader but define the class with this class loader
* so the JdbcLeakPrevention looks like a webapp class to the
* DriverManager.
*
* If only apps cleaned up after themselves...
*/
private final void clearReferencesJdbc() {
InputStream is = getResourceAsStream(
"org/glassfish/web/loader/JdbcLeakPrevention.class");
// We know roughly how big the class will be (~ 1K) so allow 2k as a
// starting point
byte[] classBytes = new byte[2048];
int offset = 0;

private void clearReferencesJdbc() {
try {
int read = is.read(classBytes, offset, classBytes.length-offset);
while (read > -1) {
offset += read;
if (offset == classBytes.length) {
// Buffer full - double size
byte[] tmp = new byte[classBytes.length * 2];
System.arraycopy(classBytes, 0, tmp, 0, classBytes.length);
classBytes = tmp;
}
read = is.read(classBytes, offset, classBytes.length-offset);
}
Class<?> lpClass =
defineClass("org.glassfish.web.loader.JdbcLeakPrevention",
classBytes, 0, offset, this.getClass().getProtectionDomain());
Object obj = lpClass.getDeclaredConstructor().newInstance();
@SuppressWarnings("unchecked") // clearJdbcDriverRegistrations() returns List<String>
List<String> driverNames = (List<String>) obj.getClass().getMethod(
"clearJdbcDriverRegistrations").invoke(obj);
List<String> driverNames = new JdbcLeakPrevention().clearJdbcDriverRegistrations(this);
String msg = rb.getString(LogFacade.CLEAR_JDBC);
for (String name : driverNames) {
LOG.log(WARNING, MessageFormat.format(msg, contextName, name));
}
} catch (Exception e) {
// So many things to go wrong above...
Throwable t = ExceptionUtils.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(t);
LOG.log(WARNING, getString(LogFacade.JDBC_REMOVE_FAILED, contextName), t);
} finally {
if (is != null) {
try {
is.close();
} catch (IOException ioe) {
LOG.log(WARNING, getString(LogFacade.JDBC_REMOVE_STREAM_ERROR, contextName), ioe);
}
}
LOG.log(WARNING, getString(LogFacade.JDBC_REMOVE_FAILED, contextName), e);
}
}


private final void clearReferencesStaticFinal() {
private void clearReferencesStaticFinal() {

Collection<ResourceEntry> values = resourceEntries.values();
Iterator<ResourceEntry> loadedClasses = values.iterator();
Expand Down

0 comments on commit f664cf1

Please sign in to comment.