Showing preview only (390K chars total). Download the full file or copy to clipboard to get everything.
Repository: mjiderhamn/classloader-leak-prevention
Branch: master
Commit: dbf68c5fc129
Files: 153
Total size: 329.3 KB
Directory structure:
gitextract_ovazmkyr/
├── .gitignore
├── .mvn/
│ └── wrapper/
│ ├── MavenWrapperDownloader.java
│ └── maven-wrapper.properties
├── .travis.yml
├── LICENSE.txt
├── README.md
├── classloader-leak-prevention/
│ ├── classloader-leak-prevention-core/
│ │ ├── README.md
│ │ ├── pom.xml
│ │ └── src/
│ │ ├── main/
│ │ │ └── java/
│ │ │ └── se/
│ │ │ └── jiderhamn/
│ │ │ └── classloader/
│ │ │ └── leak/
│ │ │ └── prevention/
│ │ │ ├── ClassLoaderLeakPreventor.java
│ │ │ ├── ClassLoaderLeakPreventorFactory.java
│ │ │ ├── ClassLoaderPreMortemCleanUp.java
│ │ │ ├── JULLogger.java
│ │ │ ├── Logger.java
│ │ │ ├── MustBeAfter.java
│ │ │ ├── PreClassLoaderInitiator.java
│ │ │ ├── ReplaceDOMNormalizerSerializerAbortException.java
│ │ │ ├── StdLogger.java
│ │ │ ├── cleanup/
│ │ │ │ ├── ApacheCommonsLoggingCleanUp.java
│ │ │ │ ├── BeanELResolverCleanUp.java
│ │ │ │ ├── BeanIntrospectorCleanUp.java
│ │ │ │ ├── BeanValidationCleanUp.java
│ │ │ │ ├── DefaultAuthenticatorCleanUp.java
│ │ │ │ ├── DriverManagerCleanUp.java
│ │ │ │ ├── GeoToolsCleanUp.java
│ │ │ │ ├── IIOServiceProviderCleanUp.java
│ │ │ │ ├── IntrospectionUtilsCleanUp.java
│ │ │ │ ├── JDK8151486CleanUp.java
│ │ │ │ ├── JacksonCleanUp.java
│ │ │ │ ├── JavaServerFaces2746CleanUp.java
│ │ │ │ ├── JavaUtilLoggingLevelCleanUp.java
│ │ │ │ ├── JavaxSecurityAuthLoginConfigurationCleanUp.java
│ │ │ │ ├── JceSecurityCleanUp.java
│ │ │ │ ├── KeepAliveTimerCacheCleanUp.java
│ │ │ │ ├── MBeanCleanUp.java
│ │ │ │ ├── MXBeanNotificationListenersCleanUp.java
│ │ │ │ ├── MoxyCleanUp.java
│ │ │ │ ├── MultiThreadedHttpConnectionManagerCleanUp.java
│ │ │ │ ├── ObjectStreamClassCleanup.java
│ │ │ │ ├── PropertyEditorCleanUp.java
│ │ │ │ ├── ProxySelectorCleanUp.java
│ │ │ │ ├── ReactorNettyHttpResourcesCleanUp.java
│ │ │ │ ├── ResourceBundleCleanUp.java
│ │ │ │ ├── RmiTargetsCleanUp.java
│ │ │ │ ├── SAAJEnvelopeFactoryParserPoolCleanUp.java
│ │ │ │ ├── SecurityProviderCleanUp.java
│ │ │ │ ├── ShutdownHookCleanUp.java
│ │ │ │ ├── StopThreadsCleanUp.java
│ │ │ │ ├── ThreadGroupCleanUp.java
│ │ │ │ ├── ThreadGroupContextCleanUp.java
│ │ │ │ ├── ThreadLocalCleanUp.java
│ │ │ │ ├── WarningThreadLocalCleanUp.java
│ │ │ │ └── X509TrustManagerImplUnparseableExtensionCleanUp.java
│ │ │ └── preinit/
│ │ │ ├── AwtToolkitInitiator.java
│ │ │ ├── DatatypeConverterImplInitiator.java
│ │ │ ├── DocumentBuilderFactoryInitiator.java
│ │ │ ├── JarUrlConnectionInitiator.java
│ │ │ ├── Java2dDisposerInitiator.java
│ │ │ ├── Java2dRenderQueueInitiator.java
│ │ │ ├── JavaxSecurityLoginConfigurationInitiator.java
│ │ │ ├── JdbcDriversInitiator.java
│ │ │ ├── LdapPoolManagerInitiator.java
│ │ │ ├── OracleJdbcThreadInitiator.java
│ │ │ ├── SecurityPolicyInitiator.java
│ │ │ ├── SecurityProvidersInitiator.java
│ │ │ ├── SunAwtAppContextInitiator.java
│ │ │ └── SunGCInitiator.java
│ │ └── test/
│ │ ├── java/
│ │ │ └── se/
│ │ │ └── jiderhamn/
│ │ │ └── classloader/
│ │ │ └── leak/
│ │ │ └── prevention/
│ │ │ ├── ClassLoaderLeakPreventorFactoryTest.java
│ │ │ ├── PreventionsTestBase.java
│ │ │ ├── StopThreadsCleanUp_TimerTest.java
│ │ │ ├── cleanup/
│ │ │ │ ├── BeanELResolverCleanUpTest.java
│ │ │ │ ├── BeanIntrospectorCleanUpTest.java
│ │ │ │ ├── BeanValidationCleanUpTest.java
│ │ │ │ ├── ClassLoaderPreMortemCleanUpTestBase.java
│ │ │ │ ├── DefaultAuthenticatorCleanUpTest.java
│ │ │ │ ├── DriverManagerCleanUpTest.java
│ │ │ │ ├── GeoToolsCleanUpTest.java
│ │ │ │ ├── IIOServiceProviderCleanUpTest.java
│ │ │ │ ├── ImageIOMockImageInputStreamSPI.java
│ │ │ │ ├── JDK8151486CleanUpTest.java
│ │ │ │ ├── JacksonCleanUpTest.java
│ │ │ │ ├── JavaServerFaces2746CleanUpTest.java
│ │ │ │ ├── JavaUtilLoggingLevelCleanUpTest.java
│ │ │ │ ├── JavaxSecurityAuthLoginConfigurationCleanUpTest.java
│ │ │ │ ├── JceSecurityCleanUpTest.java
│ │ │ │ ├── MBeanCleanUpTest.java
│ │ │ │ ├── MXBeanNotificationListenersCleanUpTest.java
│ │ │ │ ├── MXBeanNotificationListenersCleanUp_ListenerWrapperTest.java
│ │ │ │ ├── MoxyCleanUpTest.java
│ │ │ │ ├── MultiThreadedHttpConnectionManagerCleanUpTest.java
│ │ │ │ ├── ObjectStreamClassCleanupTest.java
│ │ │ │ ├── PropertyEditorCleanUpTest.java
│ │ │ │ ├── ProxySelectorCleanUpTest.java
│ │ │ │ ├── ReplaceDOMNormalizerSerializerAbortExceptionCleanUpTest.java
│ │ │ │ ├── SAAJEnvelopeFactoryParserPoolCleanUpTest.java
│ │ │ │ ├── SecurityProviderCleanUpTest.java
│ │ │ │ ├── ShutdownHookCleanUpTest.java
│ │ │ │ ├── StopThreadsCleanUp_MultiThreadedHttpConnectionManagerTest.java
│ │ │ │ ├── StopThreadsCleanUp_PostgresqlJdbcTest.java
│ │ │ │ ├── StopThreadsCleanUp_Runnable.java
│ │ │ │ ├── StopThreadsClenup_ExecutorTest.java
│ │ │ │ ├── ThreadGroupCleanUpTest.java
│ │ │ │ ├── ThreadLocalCleanUpTest.java
│ │ │ │ ├── ThreadLocalWithNestedRefValueCleanUpTest.java
│ │ │ │ ├── ThreadLocalWithRefValueCleanUpTest.java
│ │ │ │ ├── X509TrustManagerImplUnparseableExtensionCleanUpTest.java
│ │ │ │ └── package-info.java
│ │ │ ├── package-info.java
│ │ │ └── preinit/
│ │ │ ├── AwtToolkitInitiatorTest.java
│ │ │ ├── DatatypeConverterImplInitiatorTest.java
│ │ │ ├── DocumentBuilderFactoryInitiatorTest.java
│ │ │ ├── Java2dDisposerInitiatorTest.java
│ │ │ ├── Java2dRenderQueueInitiatorTest.java
│ │ │ ├── JavaxSecurityLoginConfigurationInitiatorTest.java
│ │ │ ├── JdbcDriversInitiatorTest.java
│ │ │ ├── LdapPoolManagerInitiatorTest.java
│ │ │ ├── OracleJdbcThreadInitiatorTest.java
│ │ │ ├── PreClassLoaderInitiatorTestBase.java
│ │ │ ├── ReplaceDOMNormalizerSerializerAbortExceptionInitiatorTest.java
│ │ │ ├── SecurityPolicyInitiatorTest.java
│ │ │ ├── SecurityProvidersInitiatorTest.java
│ │ │ ├── SunAwtAppContextInitiatorTest.java
│ │ │ └── SunGCInitiatorTest.java
│ │ └── resources/
│ │ ├── META-INF/
│ │ │ └── services/
│ │ │ └── javax.imageio.spi.ImageInputStreamSpi
│ │ ├── spi-cacert-2008.crt
│ │ └── spi-cacert-2008.keystore
│ ├── classloader-leak-prevention-servlet/
│ │ ├── pom.xml
│ │ └── src/
│ │ └── main/
│ │ └── java/
│ │ └── se/
│ │ └── jiderhamn/
│ │ └── classloader/
│ │ └── leak/
│ │ └── prevention/
│ │ └── ClassLoaderLeakPreventorListener.java
│ ├── classloader-leak-prevention-servlet3/
│ │ ├── pom.xml
│ │ └── src/
│ │ └── main/
│ │ ├── java/
│ │ │ └── se/
│ │ │ └── jiderhamn/
│ │ │ └── classloader/
│ │ │ └── leak/
│ │ │ └── prevention/
│ │ │ └── ClassLoaderLeakPreventionContainerInitializer.java
│ │ └── resources/
│ │ └── META-INF/
│ │ ├── services/
│ │ │ └── javax.servlet.ServletContainerInitializer
│ │ └── web-fragment.xml
│ └── pom.xml
├── classloader-leak-test-framework/
│ ├── README.md
│ ├── pom.xml
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── se/
│ │ └── jiderhamn/
│ │ ├── HeapDumper.java
│ │ └── classloader/
│ │ ├── PackagesLoadedOutsideClassLoader.java
│ │ ├── RedefiningClassLoader.java
│ │ ├── ZombieMarker.java
│ │ └── leak/
│ │ ├── JUnitClassloaderRunner.java
│ │ ├── LeakPreventor.java
│ │ └── Leaks.java
│ └── test/
│ └── java/
│ ├── com/
│ │ └── classloader/
│ │ └── test/
│ │ └── CustomClass.java
│ └── se/
│ └── jiderhamn/
│ └── classloader/
│ ├── RedefiningClassLoaderTest.java
│ └── leak/
│ ├── JUnitClassloaderRunnerTest.java
│ ├── NonLeakingTest.java
│ ├── accused/
│ │ ├── CustomThreadLocalTest.java
│ │ └── package-info.java
│ └── known/
│ ├── CustomThreadLocalCustomValueTest.java
│ ├── JEditorPaneTest.java
│ └── package-info.java
├── mvnw
├── mvnw.cmd
└── pom.xml
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
target
.classpath
.project
.settings
.mvn/wrapper/maven-wrapper.jar
================================================
FILE: .mvn/wrapper/MavenWrapperDownloader.java
================================================
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import java.io.IOException;
import java.io.InputStream;
import java.net.Authenticator;
import java.net.PasswordAuthentication;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.util.Properties;
public final class MavenWrapperDownloader
{
private static final String WRAPPER_VERSION = "3.1.1";
private static final boolean VERBOSE = Boolean.parseBoolean( System.getenv( "MVNW_VERBOSE" ) );
/**
* Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
*/
private static final String DEFAULT_DOWNLOAD_URL =
"https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/" + WRAPPER_VERSION
+ "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
/**
* Path to the maven-wrapper.properties file, which might contain a downloadUrl property to use instead of the
* default one.
*/
private static final String MAVEN_WRAPPER_PROPERTIES_PATH = ".mvn/wrapper/maven-wrapper.properties";
/**
* Path where the maven-wrapper.jar will be saved to.
*/
private static final String MAVEN_WRAPPER_JAR_PATH = ".mvn/wrapper/maven-wrapper.jar";
/**
* Name of the property which should be used to override the default download url for the wrapper.
*/
private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
public static void main( String[] args )
{
if ( args.length == 0 )
{
System.err.println( " - ERROR projectBasedir parameter missing" );
System.exit( 1 );
}
log( " - Downloader started" );
final String dir = args[0].replace( "..", "" ); // Sanitize path
final Path projectBasedir = Paths.get( dir ).toAbsolutePath().normalize();
if ( !Files.isDirectory( projectBasedir, LinkOption.NOFOLLOW_LINKS ) )
{
System.err.println( " - ERROR projectBasedir not exists: " + projectBasedir );
System.exit( 1 );
}
log( " - Using base directory: " + projectBasedir );
// If the maven-wrapper.properties exists, read it and check if it contains a custom
// wrapperUrl parameter.
Path mavenWrapperPropertyFile = projectBasedir.resolve( MAVEN_WRAPPER_PROPERTIES_PATH );
String url = readWrapperUrl( mavenWrapperPropertyFile );
try
{
Path outputFile = projectBasedir.resolve( MAVEN_WRAPPER_JAR_PATH );
createDirectories( outputFile.getParent() );
downloadFileFromURL( url, outputFile );
log( "Done" );
System.exit( 0 );
}
catch ( IOException e )
{
System.err.println( "- Error downloading" );
e.printStackTrace();
System.exit( 1 );
}
}
private static void downloadFileFromURL( String urlString, Path destination ) throws IOException
{
log( " - Downloading to: " + destination );
if ( System.getenv( "MVNW_USERNAME" ) != null && System.getenv( "MVNW_PASSWORD" ) != null )
{
final String username = System.getenv( "MVNW_USERNAME" );
final char[] password = System.getenv( "MVNW_PASSWORD" ).toCharArray();
Authenticator.setDefault( new Authenticator()
{
@Override
protected PasswordAuthentication getPasswordAuthentication()
{
return new PasswordAuthentication( username, password );
}
} );
}
URL website = new URL( urlString );
try ( InputStream inStream = website.openStream() ) {
Files.copy( inStream, destination, StandardCopyOption.REPLACE_EXISTING );
}
log( " - Downloader complete" );
}
private static void createDirectories(Path outputPath) throws IOException
{
if ( !Files.isDirectory( outputPath, LinkOption.NOFOLLOW_LINKS ) ) {
Path createDirectories = Files.createDirectories( outputPath );
log( " - Directories created: " + createDirectories );
}
}
private static String readWrapperUrl( Path mavenWrapperPropertyFile )
{
String url = DEFAULT_DOWNLOAD_URL;
if ( Files.exists( mavenWrapperPropertyFile, LinkOption.NOFOLLOW_LINKS ) )
{
log( " - Reading property file: " + mavenWrapperPropertyFile );
try ( InputStream in = Files.newInputStream( mavenWrapperPropertyFile, StandardOpenOption.READ ) )
{
Properties mavenWrapperProperties = new Properties();
mavenWrapperProperties.load( in );
url = mavenWrapperProperties.getProperty( PROPERTY_NAME_WRAPPER_URL, DEFAULT_DOWNLOAD_URL );
}
catch ( IOException e )
{
System.err.println( " - ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'" );
}
}
log( " - Downloading from: " + url );
return url;
}
private static void log( String msg )
{
if ( VERBOSE )
{
System.out.println( msg );
}
}
}
================================================
FILE: .mvn/wrapper/maven-wrapper.properties
================================================
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.5/apache-maven-3.8.5-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar
================================================
FILE: .travis.yml
================================================
dist: trusty
language: java
jdk:
- oraclejdk8
- openjdk11
# Uncomment to upload heapdumps to S3 bucket; https://docs.travis-ci.com/user/uploading-artifacts/
#addons:
# artifacts:
# paths:
# - $(ls $HOME/build/mjiderhamn/classloader-leak-prevention/classloader-leak-prevention/classloader-leak-prevention-core/target/surefire-reports/*.hprof | tr "\n" ":")
cache:
directories:
- $HOME/.m2
================================================
FILE: LICENSE.txt
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: README.md
================================================
# Classloader Leak Prevention library
[](http://travis-ci.org/mjiderhamn/classloader-leak-prevention)
[](https://maven-badges.herokuapp.com/maven-central/se.jiderhamn.classloader-leak-prevention/classloader-leak-prevention-servlet3/)
[](https://github.com/mjiderhamn/classloader-leak-prevention/blob/master/LICENSE.txt)
If you want to avoid the dreaded `java.lang.OutOfMemoryError: Metaspace` / `PermGen space`,
just include this library into your Java EE application and it should take care of the rest!
To learn more about classloader leaks, their causes, types, ways to find them and known offenders, see blog series here: http://java.jiderhamn.se/category/classloader-leaks/
## Servlet 3.0+
In a Servlet 3.0+ environment, all you need to do is include this Maven
dependency in your `.war`:
```xml
<dependency>
<groupId>se.jiderhamn.classloader-leak-prevention</groupId>
<artifactId>classloader-leak-prevention-servlet3</artifactId>
<version>2.7.0</version>
</dependency>
```
If you run into problems with the Servlet 3.0 module, try the Servlet 2.5 alternative below.
Since the [Servlet spec does not guarantee the order of `ServletContainerInitializer`s](https://java.net/jira/browse/SERVLET_SPEC-79),
it means this library may not initialize first and clean up last in case you have other Servlet 3.0 dependencies, which
could lead to unexpected behaviour.
## Servlet 2.5 (and earlier)
For Servlet 2.5 (and earlier) environments, you need to use a different
Maven dependency (notice the difference in `artifactId`):
```xml
<dependency>
<groupId>se.jiderhamn.classloader-leak-prevention</groupId>
<artifactId>classloader-leak-prevention-servlet</artifactId>
<version>2.7.0</version>
</dependency>
```
You also have to add this to your `web.xml`:
```xml
<listener>
<listener-class>se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventorListener</listener-class>
</listener>
```
_Note that the name of the listener class has changed since 1.x!_
It makes sense to keep this listener "outermost" (initializing first,
destroying last), so you should normally declare it before any other
listeners in `web.xml`.
## Configuration
The context listener used in both cases has a number of settings that can be configured with context parameters in `web.xml`:
```xml
<context-param>
<param-name>ClassLoaderLeakPreventor.stopThreads</param-name>
<param-value>false</param-value>
</context-param>
```
The available settings are
<table border="1">
<tr>
<th>Parameter name</th>
<th>Default value</th>
<th>Description</th>
</tr>
<tr>
<td><code>ClassLoaderLeakPreventor.stopThreads</code></td>
<td><code>true</code></td>
<td>Should threads tied to the web app classloader be forced to stop at application shutdown?</td>
</tr>
<tr>
<td><code>ClassLoaderLeakPreventor.stopTimerThreads</code></td>
<td><code>true</code></td>
<td>Should Timer threads tied to the web app classloader be forced to stop at application shutdown?</td>
</tr>
<tr>
<td><code>ClassLoaderLeakPreventor.executeShutdownHooks</td>
<td><code>true</code></td>
<td>Should shutdown hooks registered from the application be executed at application shutdown?</td>
</tr>
<tr>
<td><code>ClassLoaderLeakPreventor.startOracleTimeoutThread</td>
<td><code>true</code></td>
<td>
Should the <code>oracle.jdbc.driver.OracleTimeoutPollingThread</code> thread be forced to start with system ClassLoader,
in case Oracle JDBC driver is present? This is normally a good idea, but can be disabled in case the Oracle JDBC
driver is not used even though it is on the classpath.
</td>
</tr>
<tr>
<td><code>ClassLoaderLeakPreventor.threadWaitMs</td>
<td nowrap="nowrap"><code>5000</code><br />(5 seconds)</td>
<td>No of milliseconds to wait for threads to finish execution, before stopping them.</td>
</tr>
<tr>
<td><code>ClassLoaderLeakPreventor.shutdownHookWaitMs</code></td>
<td nowrap="nowrap"><code>10000</code><br />(10 seconds)</td>
<td>
No of milliseconds to wait for shutdown hooks to finish execution, before stopping them.
If set to -1 there will be no waiting at all, but Thread is allowed to run until finished.
</td>
</tr>
</table>
## Classloader leak detection / test framework
The test framework has its own Maven module and its own documentation, see [classloader-leak-test-framework](classloader-leak-test-framework).
## Integration
For non-servlet environments, please see the documentation for the [core module](classloader-leak-prevention/classloader-leak-prevention-core).
## License
This project is licensed under the [Apache 2 license](http://www.apache.org/licenses/LICENSE-2.0), which allows you to include modified versions of the code in your distributed software, without having to release your source code.
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/README.md
================================================
# Classloader Leak Prevention library integration
_This document is about using the Classloader Leak Prevention library in
a non-servlet environment. For general information and use in servlet
environments, please see the [root README.md](../../README.md)_
Version 2.x of the Classloader Leak Prevention library has been refactored
to allow for use outside a servlet environment, or by all means in a
servlet container (Java EE application server).
# Setting up
What you will want to do is first create a [ClassLoaderLeakPreventorFactory](src/main/java/se/jiderhamn/classloader/leak/prevention/ClassLoaderLeakPreventorFactory.java)
instance, either by using the default constructor that will configure the system
`ClassLoader` (`ClassLoader.getSystemClassLoader()`) to be used for pre-inits,
or provide your own leak safe `ClassLoader` to the constructor.
Make any configurations on the factory instance, i.e. add or remove any
[cleanup](src/main/java/se/jiderhamn/classloader/leak/prevention/ClassLoaderPreMortemCleanUp.java)
or [pre-init](https://github.com/mjiderhamn/classloader-leak-prevention/blob/master/classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/PreClassLoaderInitiator.java)
plugins, or change parameters of any of the default plugins.
# Protect ClassLoader
Then for every `ClassLoader` that needs leak protection, create a new
[ClassLoaderLeakPreventor](src/main/java/se/jiderhamn/classloader/leak/prevention/ClassLoaderLeakPreventor.java)
using
```java
classLoaderLeakPreventor = classLoaderLeakPreventorFactory.newLeakPreventor(classLoader);
```
Before letting any code execute inside the `ClassLoader` (or at least as
soon as possible), invoke
```java
classLoaderLeakPreventor.runPreClassLoaderInitiators();
```
You can reuse the same `ClassLoaderLeakPreventorFactory` for multiple
`ClassLoaders`, but please be aware that any configuration changes made
to plugins of the factory will affect all `ClassLoaderLeakPreventor`s
created by the factory - both future and existing. If however you add
or remove plugins, that will only affect new `ClassLoaderLeakPreventor`s.
# Shutting down
When you believe the `ClassLoader` should no longer be used, but be ready
for Garbage Collection, invoke
```java
classLoaderLeakPreventor.runCleanUps();
```
on the `ClassLoaderLeakPreventor` that corresponds to the `ClassLoader`.
# Example
For an example how to use the framework, feel free to study the
[ClassLoaderLeakPreventorListener](../classloader-leak-prevention-servlet/src/main/java/se/jiderhamn/classloader/leak/prevention/ClassLoaderLeakPreventorListener.java)
in the `classloader-leak-prevention-servlet` module.
# Maven
The module is available in Maven as
```xml
<dependency>
<groupId>se.jiderhamn.classloader-leak-prevention</groupId>
<artifactId>classloader-leak-prevention-core</artifactId>
<version>2.7.0</version>
</dependency>
```
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/pom.xml
================================================
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>se.jiderhamn.classloader-leak-prevention</groupId>
<artifactId>classloader-leak-prevention-parent</artifactId>
<version>2.7.1-SNAPSHOT</version>
</parent>
<artifactId>classloader-leak-prevention-core</artifactId>
<packaging>jar</packaging>
<name>ClassLoader Leak Prevention library</name>
<description>Library that prevents ClassLoader leaks / java.lang.OutOfMemoryError: PermGen space</description>
<url>https://github.com/mjiderhamn/classloader-leak-prevention</url>
<dependencies>
<!-- Dependency on test framework -->
<dependency>
<groupId>se.jiderhamn</groupId>
<artifactId>classloader-leak-test-framework</artifactId>
<version>1.1.2</version>
<scope>test</scope>
</dependency>
<!-- Required by some of the tested APIs -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.3</version>
<scope>test</scope>
</dependency>
<!-- Validation API needed for testing leak -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.0.0.GA</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.2.0.Final</version>
<scope>test</scope>
</dependency>
<!-- Apache Axis 1.4 for leak test -->
<dependency>
<groupId>org.apache.axis</groupId>
<artifactId>axis</artifactId>
<version>1.4</version>
<scope>test</scope>
</dependency>
<!-- Required by Axis -->
<dependency>
<groupId>commons-discovery</groupId>
<artifactId>commons-discovery</artifactId>
<version>0.5</version>
<scope>test</scope>
</dependency>
<!-- JDBC driver for DriverManager test -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.18</version>
<scope>test</scope>
</dependency>
<!-- Test leak in EL implementation cache -->
<dependency>
<groupId>javax.el</groupId>
<artifactId>el-api</artifactId>
<version>2.2.1-b04</version>
<scope>test</scope>
</dependency>
<!-- MultiThreadedHttpConnectionManagerCleanUpTest -->
<dependency>
<groupId>com.sun.jersey.contribs</groupId>
<artifactId>jersey-apache-client</artifactId>
<version>1.19</version>
<scope>test</scope>
</dependency>
<!-- Test leak in CXF custom authenticator -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>2.6.10</version>
<scope>test</scope>
</dependency>
<!-- Test that GeoTools leaks are prevented -->
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-metadata</artifactId>
<version>2.6.2</version>
<scope>test</scope>
</dependency>
<!-- Test leak in JSF api -->
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-api</artifactId>
<version>2.1.19</version>
<scope>test</scope>
</dependency>
<!-- Could be removed if Mockito was used to mock ELContext -->
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-impl</artifactId>
<version>2.1.19</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.4-1201-jdbc41</version>
<scope>test</scope>
</dependency>
<!-- Test that MOXy leaks are prevented -->
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.moxy</artifactId>
<version>2.7.0</version>
<scope>test</scope>
</dependency>
<!-- Test that Jackson leaks are prevented -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.2</version>
<scope>test</scope>
</dependency>
<!-- Example dependency for test Oracle JDBC -->
<!--
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc</artifactId>
<version>12.2.0.1</version>
<scope>test</scope>
</dependency>
-->
<!-- Internal APIs that have been removed from the JDK in java 11. -->
<!-- Used for testing SAAJEnvelopeFactoryParserPoolCleanUp. -->
<dependency>
<groupId>com.sun.xml.messaging.saaj</groupId>
<artifactId>saaj-impl</artifactId>
<version>1.4.0</version>
<scope>provided</scope>
</dependency>
<!-- Internal APIs that have been removed from the JDK in java 11. Used for MoxyCleanUpTest -->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.2.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
<version>2.2.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.2.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<!-- Create artifact (jar) with test classes https://maven.apache.org/guides/mini/guide-attached-tests.html -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/ClassLoaderLeakPreventor.java
================================================
package se.jiderhamn.classloader.leak.prevention;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* This class helps prevent classloader leaks.
* @author Mattias Jiderhamn
*/
@SuppressWarnings("WeakerAccess")
public class ClassLoaderLeakPreventor {
/** Default no of milliseconds to wait for threads to finish execution */
public static final int THREAD_WAIT_MS_DEFAULT = 5 * 1000; // 5 seconds
private static final ProtectionDomain[] NO_DOMAINS = new ProtectionDomain[0];
private static final AccessControlContext NO_DOMAINS_ACCESS_CONTROL_CONTEXT = new AccessControlContext(NO_DOMAINS);
/** {@link ClassLoader#isAncestor(ClassLoader)} */
private final Method java_lang_ClassLoader_isAncestor;
/** {@link ClassLoader#isAncestorOf(ClassLoader)} of IBM JRE */
private final Method java_lang_ClassLoader_isAncestorOf;
private final Field java_security_AccessControlContext$combiner;
private final Field java_security_AccessControlContext$parent;
private final Field java_security_AccessControlContext$privilegedContext;
/**
* {@link ClassLoader} to be used when invoking the {@link PreClassLoaderInitiator}s.
* This will normally be the {@link ClassLoader#getSystemClassLoader()}, but could be any other framework or
* app server classloader. Normally, but not necessarily, a parent of {@link #classLoader}.
*/
private final ClassLoader leakSafeClassLoader;
/** The {@link ClassLoader} we want to avoid leaking */
private final ClassLoader classLoader;
private final Logger logger;
private final Collection<PreClassLoaderInitiator> preClassLoaderInitiators;
private final Collection<ClassLoaderPreMortemCleanUp> cleanUps;
/** {@link DomainCombiner} that filters any {@link ProtectionDomain}s loaded by our classloader */
private final DomainCombiner domainCombiner;
public ClassLoaderLeakPreventor(ClassLoader leakSafeClassLoader, ClassLoader classLoader, Logger logger,
Collection<PreClassLoaderInitiator> preClassLoaderInitiators,
Collection<ClassLoaderPreMortemCleanUp> cleanUps) {
this.leakSafeClassLoader = leakSafeClassLoader;
this.classLoader = classLoader;
this.logger = logger;
this.preClassLoaderInitiators = preClassLoaderInitiators;
this.cleanUps = cleanUps;
final String javaVendor = System.getProperty("java.vendor");
if(javaVendor != null && javaVendor.startsWith("IBM")) { // IBM
java_lang_ClassLoader_isAncestor = null;
java_lang_ClassLoader_isAncestorOf = findMethod(ClassLoader.class, "isAncestorOf", ClassLoader.class);
}
else { // Oracle
java_lang_ClassLoader_isAncestor = findMethod(ClassLoader.class, "isAncestor", ClassLoader.class);
java_lang_ClassLoader_isAncestorOf = null;
}
NestedProtectionDomainCombinerException.class.getName(); // Should be loaded before switching to leak safe classloader
this.domainCombiner = createDomainCombiner();
// Reflection inits
java_security_AccessControlContext$combiner = findField(AccessControlContext.class, "combiner");
java_security_AccessControlContext$parent = findField(AccessControlContext.class, "parent");
java_security_AccessControlContext$privilegedContext = findField(AccessControlContext.class, "privilegedContext");
}
/** Invoke all the registered {@link PreClassLoaderInitiator}s in the {@link #leakSafeClassLoader} */
public void runPreClassLoaderInitiators() {
info("Initializing by loading some known offenders with leak safe classloader");
doInLeakSafeClassLoader(new Runnable() {
@Override
public void run() {
for(PreClassLoaderInitiator preClassLoaderInitiator : preClassLoaderInitiators) {
preClassLoaderInitiator.doOutsideClassLoader(ClassLoaderLeakPreventor.this);
}
}
});
}
/**
* Perform action in the provided ClassLoader (normally system ClassLoader, that may retain references to the
* {@link Thread#contextClassLoader}.
* The motive for the custom {@link AccessControlContext} is to avoid spawned threads from inheriting all the
* {@link java.security.ProtectionDomain}s of the running code, since that may include the classloader we want to
* avoid leaking. This however means the {@link AccessControlContext} will have a {@link DomainCombiner} referencing the
* classloader, which will be taken care of in {@link #runCleanUps()}.
*/
protected void doInLeakSafeClassLoader(final Runnable runnable) {
final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(leakSafeClassLoader);
// Use doPrivileged() not to perform secured actions, but to avoid threads spawned inheriting the
// AccessControlContext of the current thread, since among the ProtectionDomains there will be one
// (the top one) whose classloader is the web app classloader
AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
runnable.run();
return null; // Nothing to return
}
}, createAccessControlContext());
}
finally {
// Reset original classloader
Thread.currentThread().setContextClassLoader(contextClassLoader);
}
}
/**
* Create {@link AccessControlContext} that is used in {@link #doInLeakSafeClassLoader(Runnable)}.
* The motive is to avoid spawned threads from inheriting all the {@link java.security.ProtectionDomain}s of the
* running code, since that will include the web app classloader.
*/
public AccessControlContext createAccessControlContext() {
try { // Try the normal way
return new AccessControlContext(NO_DOMAINS_ACCESS_CONTROL_CONTEXT, domainCombiner);
}
catch (SecurityException e) { // createAccessControlContext not granted
try { // Try reflection
Constructor<AccessControlContext> constructor =
AccessControlContext.class.getDeclaredConstructor(ProtectionDomain[].class, DomainCombiner.class);
constructor.setAccessible(true);
return constructor.newInstance(NO_DOMAINS, domainCombiner);
}
catch (Exception e1) {
logger.error("createAccessControlContext not granted and AccessControlContext could not be created via reflection");
return AccessController.getContext();
}
}
}
/** {@link DomainCombiner} that filters any {@link ProtectionDomain}s loaded by our classloader */
private DomainCombiner createDomainCombiner() {
return new DomainCombiner() {
/** Flag to detected recursive calls */
private final ThreadLocal<Boolean> isExecuting = new ThreadLocal<Boolean>();
@Override
public ProtectionDomain[] combine(ProtectionDomain[] currentDomains, ProtectionDomain[] assignedDomains) {
if(assignedDomains != null && assignedDomains.length > 0) {
logger.error("Unexpected assignedDomains - please report to developer of this library!");
}
if(isExecuting.get() == Boolean.TRUE)
throw new NestedProtectionDomainCombinerException();
try {
isExecuting.set(Boolean.TRUE); // Throw NestedProtectionDomainCombinerException on nested calls
// Keep all ProtectionDomain not involving the web app classloader
final List<ProtectionDomain> output = new ArrayList<ProtectionDomain>();
for(ProtectionDomain protectionDomain : currentDomains) {
if(protectionDomain.getClassLoader() == null ||
! isClassLoaderOrChild(protectionDomain.getClassLoader())) {
output.add(protectionDomain);
}
}
return output.toArray(new ProtectionDomain[output.size()]);
}
finally {
isExecuting.remove();
}
}
};
}
/**
* Recursively unset our custom {@link DomainCombiner} (loaded in the web app) from the {@link AccessControlContext}
* and any parents or privilegedContext thereof.
*/
@Deprecated
public void removeDomainCombiner(Thread thread, AccessControlContext accessControlContext) {
removeDomainCombiner("thread " + thread, accessControlContext);
}
/**
* Recursively unset our custom {@link DomainCombiner} (loaded in the web app) from the {@link AccessControlContext}
* and any parents or privilegedContext thereof.
*/
public void removeDomainCombiner(String owner, AccessControlContext accessControlContext) {
if(accessControlContext != null && java_security_AccessControlContext$combiner != null) {
if(getFieldValue(java_security_AccessControlContext$combiner, accessControlContext) == this.domainCombiner) {
warn(AccessControlContext.class.getSimpleName() + " of " + owner + " used custom combiner - unsetting");
try {
java_security_AccessControlContext$combiner.set(accessControlContext, null);
}
catch (Exception e) {
error(e);
}
}
// Recurse
if(java_security_AccessControlContext$parent != null) {
removeDomainCombiner(owner, (AccessControlContext) getFieldValue(java_security_AccessControlContext$parent, accessControlContext));
}
if(java_security_AccessControlContext$privilegedContext != null) {
removeDomainCombiner(owner, (AccessControlContext) getFieldValue(java_security_AccessControlContext$privilegedContext, accessControlContext));
}
}
}
/** Invoke all the registered {@link ClassLoaderPreMortemCleanUp}s */
public void runCleanUps() {
if(isJvmShuttingDown()) {
info("JVM is shutting down - skip cleanup");
// Don't do anything more
}
else {
final Field inheritedAccessControlContext = this.findField(Thread.class, "inheritedAccessControlContext");
if(inheritedAccessControlContext != null) {
// Check if threads have been started in doInLeakSafeClassLoader() and need fixed ACC
for(Thread thread : getAllThreads()) { // (We actually only need to do this for threads not running in web app, as per StopThreadsCleanUp)
final AccessControlContext accessControlContext = getFieldValue(inheritedAccessControlContext, thread);
removeDomainCombiner("thread " + thread , accessControlContext);
}
}
for(ClassLoaderPreMortemCleanUp cleanUp : cleanUps) {
cleanUp.cleanUp(this);
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Utility methods
public ClassLoader getClassLoader() {
return classLoader;
}
/**
* Get {@link ClassLoader} to be used when invoking the {@link PreClassLoaderInitiator}s.
* This will normally be the {@link ClassLoader#getSystemClassLoader()}, but could be any other framework or
* app server classloader. Normally, but not necessarily, a parent of {@link #classLoader}.
*/
public ClassLoader getLeakSafeClassLoader() {
return leakSafeClassLoader;
}
/** Test if provided object is loaded by {@link #classLoader} */
public boolean isLoadedInClassLoader(Object o) {
return (o instanceof Class) && isLoadedByClassLoader((Class<?>)o) || // Object is a java.lang.Class instance
o != null && isLoadedByClassLoader(o.getClass());
}
/** Test if provided class is loaded wby {@link #classLoader} */
public boolean isLoadedByClassLoader(Class<?> clazz) {
return clazz != null && isClassLoaderOrChild(clazz.getClassLoader());
}
/** Test if provided ClassLoader is the {@link #classLoader}, or a child thereof */
public boolean isClassLoaderOrChild(ClassLoader cl) {
if(cl == null) {
return false;
}
else if(cl == classLoader) {
return true;
}
else { // It could be a child of the webapp classloader
if(java_lang_ClassLoader_isAncestor != null) { // Primarily use ClassLoader.isAncestor()
try {
return (Boolean) java_lang_ClassLoader_isAncestor.invoke(cl, classLoader);
}
catch (Exception e) {
error(e);
}
}
if(java_lang_ClassLoader_isAncestorOf != null) { // Secondarily use IBM ClassLoader.isAncestorOf()
try {
return (Boolean) java_lang_ClassLoader_isAncestorOf.invoke(classLoader, cl);
}
catch (Exception e) {
error(e);
}
}
// We were unable to use ClassLoader.isAncestor() or isAncestorOf()
try {
while(cl != null) {
if(cl == classLoader)
return true;
cl = cl.getParent();
}
}
catch (NestedProtectionDomainCombinerException e) {
return false; // Since we needed permission to call getParent(), it is unlikely it is a descendant
}
return false;
}
}
/**
* Is the {@link Thread} ties do the protected classloader, either by being a custom {@link Thread} class, having a
* custom {@link ThreadGroup} or having the protected classloader as its {@link Thread#contextClassLoader}?
*/
public boolean isThreadInClassLoader(Thread thread) {
return isLoadedInClassLoader(thread) || // Custom Thread class in classloader
isLoadedInClassLoader(thread.getThreadGroup()) || // Custom ThreadGroup class in classloader
isClassLoaderOrChild(thread.getContextClassLoader()); // Running in classloader
}
/**
* Make the provided Thread stop sleep(), wait() or join() and then give it the provided no of milliseconds to finish
* executing.
* @param thread The thread to wake up and wait for
* @param waitMs The no of milliseconds to wait. If <= 0 this method does nothing.
* @param interrupt Should {@link Thread#interrupt()} be called first, to make thread stop sleep(), wait() or join()?
*/
public void waitForThread(Thread thread, long waitMs, boolean interrupt) {
if(waitMs > 0) {
if(interrupt) {
try {
thread.interrupt(); // Make Thread stop waiting in sleep(), wait() or join()
}
catch (SecurityException e) {
error(e);
}
}
try {
thread.join(waitMs); // Wait for thread to run
}
catch (InterruptedException e) {
// Do nothing
}
}
}
/** Get current stack trace or provided thread as string. Returns {@code "unavailable"} if stack trace could not be acquired. */
public String getStackTrace(Thread thread) {
try {
final StackTraceElement[] stackTrace = thread.getStackTrace();
if(stackTrace.length == 0)
return "Thread state: " + thread.getState();
final StringBuilder output = new StringBuilder("Thread stack trace: ");
for(StackTraceElement stackTraceElement : stackTrace) {
// if(output.length() > 0) // Except first
output.append("\n\tat ");
output.append(stackTraceElement.toString());
}
return output.toString().trim(); //
}
catch (Throwable t) { // SecurityException
return "Thread details unavailable";
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public <E> E getStaticFieldValue(Class<?> clazz, String fieldName) {
Field staticField = findField(clazz, fieldName);
return (staticField != null) ? (E) getStaticFieldValue(staticField) : null;
}
public <E> E getStaticFieldValue(String className, String fieldName) {
return (E) getStaticFieldValue(className, fieldName, false);
}
public <E> E getStaticFieldValue(String className, String fieldName, boolean trySystemCL) {
Field staticField = findFieldOfClass(className, fieldName, trySystemCL);
return (staticField != null) ? (E) getStaticFieldValue(staticField) : null;
}
public Field findFieldOfClass(String className, String fieldName) {
return findFieldOfClass(className, fieldName, false);
}
public Field findFieldOfClass(String className, String fieldName, boolean trySystemCL) {
Class<?> clazz = findClass(className, trySystemCL);
if(clazz != null) {
return findField(clazz, fieldName);
}
else
return null;
}
public Class<?> findClass(String className) {
return findClass(className, false);
}
public Class<?> findClass(String className, boolean trySystemCL) {
try {
return Class.forName(className);
}
// catch (NoClassDefFoundError e) {
// // Silently ignore
// return null;
// }
catch (ClassNotFoundException e) {
if (trySystemCL) {
try {
return Class.forName(className, true, ClassLoader.getSystemClassLoader());
} catch (ClassNotFoundException e1) {
// Silently ignore
return null;
}
}
// Silently ignore
return null;
}
catch (Exception ex) { // Example SecurityException
warn(ex);
return null;
}
}
public Field findField(Class<?> clazz, String fieldName) {
if(clazz == null)
return null;
try {
final Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true); // (Field is probably private)
return field;
}
catch (NoSuchFieldException ex) {
// Silently ignore
return null;
}
catch (Exception ex) { // Example SecurityException
warn(ex);
return null;
}
}
public <T> T getStaticFieldValue(Field field) {
try {
if(! Modifier.isStatic(field.getModifiers())) {
warn(field.toString() + " is not static");
return null;
}
return (T) field.get(null);
}
catch (Exception ex) {
warn(ex);
// Silently ignore
return null;
}
}
public <T> T getFieldValue(Object obj, String fieldName) {
final Field field = findField(obj.getClass(), fieldName);
return (T) getFieldValue(field, obj);
}
public <T> T getFieldValue(Field field, Object obj) {
try {
return (T) field.get(obj);
}
catch (Exception ex) {
warn(ex);
// Silently ignore
return null;
}
}
public void setFinalStaticField(Field field, Object newValue) {
// Allow modification of final field
try {
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
}
catch (NoSuchFieldException e) {
warn("Unable to get 'modifiers' field of java.lang.Field");
}
catch (IllegalAccessException e) {
warn("Unable to set 'modifiers' field of java.lang.Field");
}
catch (Throwable t) {
warn(t);
}
// Update the field
try {
field.set(null, newValue);
}
catch (Throwable e) {
error("Error setting value of " + field + " to " + newValue);
}
}
public Method findMethod(String className, String methodName, Class... parameterTypes) {
Class<?> clazz = findClass(className);
if(clazz != null) {
return findMethod(clazz, methodName, parameterTypes);
}
else
return null;
}
public Method findMethod(Class<?> clazz, String methodName, Class... parameterTypes) {
if(clazz == null)
return null;
try {
final Method method = clazz.getDeclaredMethod(methodName, parameterTypes);
method.setAccessible(true);
return method;
}
catch (NoSuchMethodException ex) {
warn(ex);
// Silently ignore
return null;
}
}
/** Get a Collection with all Threads.
* This method is heavily inspired by org.apache.catalina.loader.WebappClassLoader.getThreads() */
public Collection<Thread> getAllThreads() {
// This is some orders of magnitude slower...
// return Thread.getAllStackTraces().keySet();
// Find root ThreadGroup
ThreadGroup tg = Thread.currentThread().getThreadGroup();
while(tg.getParent() != null)
tg = tg.getParent();
// Note that ThreadGroup.enumerate() silently ignores all threads that does not fit into array
int guessThreadCount = tg.activeCount() + 50;
Thread[] threads = new Thread[guessThreadCount];
int actualThreadCount = tg.enumerate(threads);
while(actualThreadCount == guessThreadCount) { // Map was filled, there may be more
guessThreadCount *= 2;
threads = new Thread[guessThreadCount];
actualThreadCount = tg.enumerate(threads);
}
// Filter out nulls
final List<Thread> output = new ArrayList<Thread>();
for(Thread t : threads) {
if(t != null) {
output.add(t);
}
}
return output;
}
/**
* Override this method if you want to customize how we determine if we're running in
* JBoss WildFly (a.k.a JBoss AS).
*/
public boolean isJBoss() {
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
try {
// If package org.jboss is found, we may be running under JBoss
return (contextClassLoader.getResource("org/jboss") != null);
}
catch(Exception ex) {
return false;
}
}
/**
* Are we running in the Oracle/Sun Java Runtime Environment?
* Override this method if you want to customize how we determine if this is a Oracle/Sun
* Java Runtime Environment.
*/
public boolean isOracleJRE() {
String javaVendor = System.getProperty("java.vendor");
return javaVendor.startsWith("Oracle") || javaVendor.startsWith("Sun");
}
/**
* Unlike <code>{@link System#gc()}</code> this method guarantees that garbage collection has been performed before
* returning.
*/
public static void gc() {
if (isDisableExplicitGCEnabled()) {
System.err.println(ClassLoaderLeakPreventor.class.getSimpleName() + ": "
+ "Skipping GC call since -XX:+DisableExplicitGC is supplied as VM option.");
return;
}
Object obj = new Object();
WeakReference<Object> ref = new WeakReference<Object>(obj);
//noinspection UnusedAssignment
obj = null;
while(ref.get() != null) {
System.gc();
}
}
/**
* Check is "-XX:+DisableExplicitGC" enabled.
*
* @return true is "-XX:+DisableExplicitGC" is set als vm argument, false otherwise.
*/
private static boolean isDisableExplicitGCEnabled() {
RuntimeMXBean bean = ManagementFactory.getRuntimeMXBean();
List<String> aList = bean.getInputArguments();
return aList.contains("-XX:+DisableExplicitGC");
}
/** Is the JVM currently shutting down? */
public boolean isJvmShuttingDown() {
try {
final Thread dummy = new Thread(); // Will never be started
Runtime.getRuntime().removeShutdownHook(dummy);
return false;
}
catch (IllegalStateException isex) {
return true; // Shutting down
}
catch (Throwable t) { // Any other Exception, assume we are not shutting down
return false;
}
}
/**
* Exception thrown when {@link DomainCombiner#combine(ProtectionDomain[], ProtectionDomain[])} is called recursively
* during the execution of that same method.
*/
private static class NestedProtectionDomainCombinerException extends RuntimeException {
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Delegate methods for Logger
public void debug(String msg) {
logger.debug(msg);
}
public void warn(Throwable t) {
logger.warn(t);
}
public void error(Throwable t) {
logger.error(t);
}
public void warn(String msg) {
logger.warn(msg);
}
public void error(String msg) {
logger.error(msg);
}
public void info(String msg) {
logger.info(msg);
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/ClassLoaderLeakPreventorFactory.java
================================================
package se.jiderhamn.classloader.leak.prevention;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Map;
import se.jiderhamn.classloader.leak.prevention.cleanup.*;
import se.jiderhamn.classloader.leak.prevention.preinit.*;
import static java.util.Collections.synchronizedMap;
/**
* Orchestrator class responsible for invoking the preventative and cleanup measures.
* Contains the configuration and can be reused for multiple classloaders (assume it is not itself loaded by the
* classloader which we want to avoid leaking). In that case, the {@link #logger} may need to be thread safe.
* @author Mattias Jiderhamn
*/
public class ClassLoaderLeakPreventorFactory {
/**
* {@link ClassLoader} to be used when invoking the {@link PreClassLoaderInitiator}s.
* Defaults to {@link ClassLoader#getSystemClassLoader()}, but could be any other framework or
* app server classloader.
*/
protected final ClassLoader leakSafeClassLoader;
/**
* The {@link Logger} that will be passed on to the different {@link PreClassLoaderInitiator}s and
* {@link ClassLoaderPreMortemCleanUp}s
*/
protected Logger logger = new JULLogger();
/**
* Map from name to {@link PreClassLoaderInitiator}s with all the actions to invoke in the
* {@link #leakSafeClassLoader}. Maintains insertion order. Thread safe.
*/
protected final Map<String, PreClassLoaderInitiator> preInitiators =
synchronizedMap(new LinkedHashMap<String, PreClassLoaderInitiator>());
/**
* Map from name to {@link ClassLoaderPreMortemCleanUp}s with all the actions to invoke to make a
* {@link ClassLoader} ready for Garbage Collection. Maintains insertion order. Thread safe.
*/
protected final Map<String, ClassLoaderPreMortemCleanUp> cleanUps =
synchronizedMap(new LinkedHashMap<String, ClassLoaderPreMortemCleanUp>());
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Constructors
/**
* Create new {@link ClassLoaderLeakPreventorFactory} with {@link ClassLoader#getSystemClassLoader()} as the
* {@link #leakSafeClassLoader} and default {@link PreClassLoaderInitiator}s and {@link ClassLoaderPreMortemCleanUp}s.
*/
public ClassLoaderLeakPreventorFactory() {
this(ClassLoader.getSystemClassLoader());
}
/**
* Create new {@link ClassLoaderLeakPreventorFactory} with supplied {@link ClassLoader} as the
* {@link #leakSafeClassLoader} and default {@link PreClassLoaderInitiator}s and {@link ClassLoaderPreMortemCleanUp}s.
*/
public ClassLoaderLeakPreventorFactory(ClassLoader leakSafeClassLoader) {
this.leakSafeClassLoader = leakSafeClassLoader;
configureDefaults();
}
/** Configure default {@link PreClassLoaderInitiator}s and {@link ClassLoaderPreMortemCleanUp}s */
public void configureDefaults() {
// The pre-initiators part is heavily inspired by Tomcats JreMemoryLeakPreventionListener
// See http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/JreMemoryLeakPreventionListener.java?view=markup
this.addPreInitiator(new AwtToolkitInitiator());
// initSecurityProviders()
this.addPreInitiator(new JdbcDriversInitiator());
this.addPreInitiator(new SunAwtAppContextInitiator());
this.addPreInitiator(new SecurityPolicyInitiator());
this.addPreInitiator(new SecurityProvidersInitiator());
this.addPreInitiator(new DocumentBuilderFactoryInitiator());
this.addPreInitiator(new ReplaceDOMNormalizerSerializerAbortException());
this.addPreInitiator(new DatatypeConverterImplInitiator());
this.addPreInitiator(new JavaxSecurityLoginConfigurationInitiator());
this.addPreInitiator(new JarUrlConnectionInitiator());
// Load Sun specific classes that may cause leaks
this.addPreInitiator(new LdapPoolManagerInitiator());
this.addPreInitiator(new Java2dDisposerInitiator());
this.addPreInitiator(new Java2dRenderQueueInitiator());
this.addPreInitiator(new SunGCInitiator());
this.addPreInitiator(new OracleJdbcThreadInitiator());
this.addCleanUp(new BeanIntrospectorCleanUp());
this.addCleanUp(new ObjectStreamClassCleanup());
// Apache Commons Pool can leave unfinished threads. Anything specific we can do?
this.addCleanUp(new BeanELResolverCleanUp());
this.addCleanUp(new BeanValidationCleanUp());
this.addCleanUp(new JacksonCleanUp());
this.addCleanUp(new JavaServerFaces2746CleanUp());
this.addCleanUp(new GeoToolsCleanUp());
// Can we do anything about Google Guice ?
// Can we do anything about Groovy http://jira.codehaus.org/browse/GROOVY-4154 ?
this.addCleanUp(new IntrospectionUtilsCleanUp());
// Can we do anything about Logback http://jira.qos.ch/browse/LBCORE-205 ?
this.addCleanUp(new IIOServiceProviderCleanUp()); // clear ImageIO registry
this.addCleanUp(new MoxyCleanUp());
this.addCleanUp(new ReactorNettyHttpResourcesCleanUp());
this.addCleanUp(new ThreadGroupContextCleanUp());
this.addCleanUp(new X509TrustManagerImplUnparseableExtensionCleanUp());
this.addCleanUp(new SAAJEnvelopeFactoryParserPoolCleanUp());
////////////////////
// Fix generic leaks
this.addCleanUp(new DriverManagerCleanUp());
this.addCleanUp(new DefaultAuthenticatorCleanUp());
this.addCleanUp(new MBeanCleanUp());
this.addCleanUp(new MXBeanNotificationListenersCleanUp());
this.addCleanUp(new ShutdownHookCleanUp());
this.addCleanUp(new PropertyEditorCleanUp());
this.addCleanUp(new SecurityProviderCleanUp());
this.addCleanUp(new JceSecurityCleanUp()); // (Probably best to do after deregistering the providers)
this.addCleanUp(new ProxySelectorCleanUp());
this.addCleanUp(new RmiTargetsCleanUp());
this.addCleanUp(new StopThreadsCleanUp());
this.addCleanUp(new ThreadGroupCleanUp());
this.addCleanUp(new ThreadLocalCleanUp()); // This must be done after threads have been stopped, or new ThreadLocals may be added by those threads
this.addCleanUp(new KeepAliveTimerCacheCleanUp());
this.addCleanUp(new ResourceBundleCleanUp());
this.addCleanUp(new JDK8151486CleanUp());
this.addCleanUp(new JavaUtilLoggingLevelCleanUp()); // Do this last, in case other shutdown procedures want to log something.
this.addCleanUp(new ApacheCommonsLoggingCleanUp()); // Do this last, in case other shutdown procedures want to log something.
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Factory methods
/**
* Create new {@link ClassLoaderLeakPreventor} used to prevent the provided {@link Thread#contextClassLoader} of the
* {@link Thread#currentThread()} from leaking.
*
* Please be aware that {@link ClassLoaderLeakPreventor}s created by the same factory share the same
* {@link PreClassLoaderInitiator} and {@link ClassLoaderPreMortemCleanUp} instances, in case their config is changed.
*/
public ClassLoaderLeakPreventor newLeakPreventor() {
return newLeakPreventor(Thread.currentThread().getContextClassLoader());
}
/** Create new {@link ClassLoaderLeakPreventor} used to prevent the provided {@link ClassLoader} from leaking */
public ClassLoaderLeakPreventor newLeakPreventor(ClassLoader classLoader) {
return new ClassLoaderLeakPreventor(leakSafeClassLoader, classLoader, logger,
new ArrayList<PreClassLoaderInitiator>(preInitiators.values()), // Snapshot
new ArrayList<ClassLoaderPreMortemCleanUp>(cleanUps.values())); // Snapshot
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Methods for configuring the factory
/** Set logger */
public void setLogger(Logger logger) {
this.logger = logger;
}
/** Add a new {@link PreClassLoaderInitiator}, using the class name as name */
public void addPreInitiator(PreClassLoaderInitiator preClassLoaderInitiator) {
addConsideringOrder(this.preInitiators, preClassLoaderInitiator);
}
/** Add a new {@link ClassLoaderPreMortemCleanUp}, using the class name as name */
public void addCleanUp(ClassLoaderPreMortemCleanUp classLoaderPreMortemCleanUp) {
addConsideringOrder(this.cleanUps, classLoaderPreMortemCleanUp);
}
/** Add new {@link I} entry to {@code map}, taking {@link MustBeAfter} into account */
private <I> void addConsideringOrder(Map<String, I> map, I newEntry) {
for(Map.Entry<String, I> entry : map.entrySet()) {
if(entry.getValue() instanceof MustBeAfter<?>) {
final Class<? extends ClassLoaderPreMortemCleanUp>[] existingMustBeAfter =
((MustBeAfter<ClassLoaderPreMortemCleanUp>)entry.getValue()).mustBeBeforeMe();
for(Class<? extends ClassLoaderPreMortemCleanUp> clazz : existingMustBeAfter) {
if(clazz.isAssignableFrom(newEntry.getClass())) { // Entry needs to be after new entry
// TODO Resolve order automatically #51
throw new IllegalStateException(clazz.getName() + " must be added after " + newEntry.getClass());
}
}
}
}
map.put(newEntry.getClass().getName(), newEntry);
}
/** Add a new named {@link ClassLoaderPreMortemCleanUp} */
public void addCleanUp(String name, ClassLoaderPreMortemCleanUp classLoaderPreMortemCleanUp) {
this.cleanUps.put(name, classLoaderPreMortemCleanUp);
}
/** Remove all the currently configured {@link PreClassLoaderInitiator}s */
public void clearPreInitiators() {
this.preInitiators.clear();
}
/** Remove all the currently configured {@link ClassLoaderPreMortemCleanUp}s */
public void clearCleanUps() {
this.cleanUps.clear();
}
/**
* Get instance of {@link PreClassLoaderInitiator} for further configuring.
*
* Please be aware that {@link ClassLoaderLeakPreventor}s created by the same factory share the same
* {@link PreClassLoaderInitiator} and {@link ClassLoaderPreMortemCleanUp} instances, in case their config is changed.
*/
public <C extends PreClassLoaderInitiator> C getPreInitiator(Class<C> clazz) {
return (C) this.preInitiators.get(clazz.getName());
}
/**
* Get instance of {@link ClassLoaderPreMortemCleanUp} for further configuring.
*
* Please be aware that {@link ClassLoaderLeakPreventor}s created by the same factory share the same
* {@link PreClassLoaderInitiator} and {@link ClassLoaderPreMortemCleanUp} instances, in case their config is changed.
*/
public <C extends ClassLoaderPreMortemCleanUp> C getCleanUp(Class<C> clazz) {
return (C) this.cleanUps.get(clazz.getName());
}
/** Get instance of {@link PreClassLoaderInitiator} for further configuring */
public <C extends PreClassLoaderInitiator> void removePreInitiator(Class<C> clazz) {
this.preInitiators.remove(clazz.getName());
}
/** Get instance of {@link ClassLoaderPreMortemCleanUp} for further configuring */
public <C extends ClassLoaderPreMortemCleanUp> void removeCleanUp(Class<C> clazz) {
this.cleanUps.remove(clazz.getName());
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/ClassLoaderPreMortemCleanUp.java
================================================
package se.jiderhamn.classloader.leak.prevention;
/**
* Interface for cleanup actions that should be performed as part of the preparations to make a {@link ClassLoader} available
* for garbage collection.
* @author Mattias Jiderhamn
*/
public interface ClassLoaderPreMortemCleanUp {
/**
* Perform cleanup actions needed to make provided {@link ClassLoaderLeakPreventor#classLoader}
* ready for garbage collection.
*/
void cleanUp(ClassLoaderLeakPreventor preventor);
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/JULLogger.java
================================================
package se.jiderhamn.classloader.leak.prevention;
import java.util.logging.Level;
/**
* Implementation of {@link Logger} interface, that uses {@link java.util.logging}.
*
* @author Mattias Jiderhamn
*/
public class JULLogger implements Logger {
private static final java.util.logging.Logger LOG =
java.util.logging.Logger.getLogger(ClassLoaderLeakPreventor.class.getName());
@Override
public void debug(String msg) {
LOG.config(msg);
}
@Override
public void info(String msg) {
LOG.info(msg);
}
@Override
public void warn(String msg) {
LOG.warning(msg);
}
@Override
public void warn(Throwable t) {
LOG.log(Level.WARNING, t.getMessage(), t);
}
@Override
public void error(String msg) {
LOG.severe(msg);
}
@Override
public void error(Throwable t) {
LOG.log(Level.SEVERE, t.getMessage(), t);
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/Logger.java
================================================
package se.jiderhamn.classloader.leak.prevention;
/**
* Interface for logging, with similarities to common logging frameworks. If you want to plug in the leak preventor
* into an existing architecture (such as an application server), you may want to use a custom implementation of this
* interface.
*
* If the {@link ClassLoaderLeakPreventorFactory} is beeing reused, the {@link Logger} implementation may need to be
* thread safe.
*
* @author Mattias Jiderhamn
*/
public interface Logger {
/** Log debug level message */
void debug(String msg);
/** Log info level message */
void info(String msg);
/** Log a warning message */
void warn(String msg);
/** Log a {@link Throwable} as a warning message */
void warn(Throwable t);
/** Log an error message */
void error(String msg);
/** Log a {@link Throwable} as an error message */
void error(Throwable t);
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/MustBeAfter.java
================================================
package se.jiderhamn.classloader.leak.prevention;
/**
* Interface to be implemented by {@link PreClassLoaderInitiator}s and {@link ClassLoaderPreMortemCleanUp}s when order
* is important. The class implementing this interface will define what other implementations it needs to be invoked
* *after* for correct behaviour. It is the responsibility of {@link ClassLoaderLeakPreventorFactory} to make sure
* the implementations are ordered correctly. Currently an {@link IllegalStateException} will be thrown (TODO #51)
* @param <I> The interface that both this class and the dependent classes implements,
* i.e. either {@link PreClassLoaderInitiator} or {@link ClassLoaderPreMortemCleanUp}.
*
* @author Mattias Jiderhamn
*/
public interface MustBeAfter<I> {
/**
* Returns an array of classes that, if part of they or any subclass of them are part of the list of
* {@link PreClassLoaderInitiator}s/{@link ClassLoaderPreMortemCleanUp}s, needs to be prior to this element in the list.
*/
Class<? extends I>[] mustBeBeforeMe();
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/PreClassLoaderInitiator.java
================================================
package se.jiderhamn.classloader.leak.prevention;
/**
* Interface for preventative actions that should be executed in the system (or other parent) classloader before they
* may be triggered within the classloader that is about to be launched, and thereby may trigger leaks.
* @author Mattias Jiderhamn
*/
public interface PreClassLoaderInitiator {
/**
* Perform action that needs to be done outside the leak susceptible classloader, i.e. in the system or other parent
* classloader. Assume that the system or parent classloader is the {@link Thread#contextClassLoader} of the current
* thread when method is invoked.
* Must NOT have modified {@link Thread#contextClassLoader} of the current thread when returning.
*/
void doOutsideClassLoader(ClassLoaderLeakPreventor preventor);
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/ReplaceDOMNormalizerSerializerAbortException.java
================================================
package se.jiderhamn.classloader.leak.prevention;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
/**
* As reported at https://github.com/mjiderhamn/classloader-leak-prevention/issues/36, invoking
* {@code DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument().normalizeDocument();} or
* <code>
* Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
* DOMImplementationLS implementation = (DOMImplementationLS)document.getImplementation();
* implementation.createLSSerializer().writeToString(document);
* </code> may trigger leaks caused by the static fields {@link com.sun.org.apache.xerces.internal.dom.DOMNormalizer#abort} and
* {@link com.sun.org.apache.xml.internal.serialize.DOMSerializerImpl#abort} respectively keeping stacktraces/backtraces
* that may include references to classes loaded by our web application.
*
* Since the {@link java.lang.Throwable#backtrace} itself cannot be accessed via reflection (see
* http://bugs.java.com/view_bug.do?bug_id=4496456) we need to replace the with new one without any stack trace.
*
* This can be done either as a {@link PreClassLoaderInitiator} (recommended) or {@link ClassLoaderPreMortemCleanUp}.
*
* @author Mattias Jiderhamn
*/
public class ReplaceDOMNormalizerSerializerAbortException implements PreClassLoaderInitiator, ClassLoaderPreMortemCleanUp {
@Override
public void doOutsideClassLoader(ClassLoaderLeakPreventor preventor) {
replaceDOMNormalizerSerializerAbortException(preventor);
}
@Override
public void cleanUp(ClassLoaderLeakPreventor preventor) {
replaceDOMNormalizerSerializerAbortException(preventor);
}
@SuppressWarnings("WeakerAccess")
protected void replaceDOMNormalizerSerializerAbortException(ClassLoaderLeakPreventor preventor) {
final RuntimeException abort = constructRuntimeExceptionWithoutStackTrace(preventor, "abort", null);
if(abort != null) {
final Field normalizerAbort = preventor.findFieldOfClass("com.sun.org.apache.xerces.internal.dom.DOMNormalizer", "abort");
if(normalizerAbort != null)
preventor.setFinalStaticField(normalizerAbort, abort);
final Field serializerAbort = preventor.findFieldOfClass("com.sun.org.apache.xml.internal.serialize.DOMSerializerImpl", "abort");
if(serializerAbort != null)
preventor.setFinalStaticField(serializerAbort, abort);
}
}
/** Construct a new {@link RuntimeException} without any stack trace, in order to avoid any references back to this class */
@SuppressWarnings("WeakerAccess")
public static RuntimeException constructRuntimeExceptionWithoutStackTrace(ClassLoaderLeakPreventor preventor,
String message, Throwable cause) {
try {
final Constructor<RuntimeException> constructor =
RuntimeException.class.getDeclaredConstructor(String.class, Throwable.class, Boolean.TYPE, Boolean.TYPE);
constructor.setAccessible(true);
return constructor.newInstance(message, cause, true, false /* disable stack trace */);
}
catch (Throwable e) { // InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException
preventor.warn("Unable to construct RuntimeException without stack trace. The likely reason is that you are using Java <= 1.6. " +
"No worries, except there might be some leaks you're not protected from (https://github.com/mjiderhamn/classloader-leak-prevention/issues/36 , " +
"https://github.com/mjiderhamn/classloader-leak-prevention/issues/69). " +
"If you are already on Java 1.7+, please report issue to developer of this library!");
preventor.warn(e);
return null;
}
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/StdLogger.java
================================================
package se.jiderhamn.classloader.leak.prevention;
/**
* Implementation of {@link Logger} interface, that uses {@link System#out} and {@link System#err}.
* Because log frameworks may themselves cause leaks, we may want to avoid them altogether.
*
* To "turn off" a log level, override the corresponding method(s) with an empty implementation.
* @author Mattias Jiderhamn
*/
public class StdLogger implements Logger {
/** Get prefix to use when logging to {@link System#out}/{@link System#err} */
protected String getLogPrefix() {
return "ClassLoader Leak Preventor: ";
}
@Override
public void debug(String msg) {
System.out.println(getLogPrefix() + msg);
}
@Override
public void info(String s) {
System.out.println(getLogPrefix() + s);
}
@Override
public void warn(String s) {
System.err.println(getLogPrefix() + s);
}
@Override
public void warn(Throwable t) {
t.printStackTrace(System.err);
}
@Override
public void error(String s) {
System.err.println(getLogPrefix() + s);
}
@Override
public void error(Throwable t) {
t.printStackTrace(System.err);
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/ApacheCommonsLoggingCleanUp.java
================================================
package se.jiderhamn.classloader.leak.prevention.cleanup;
import java.security.CodeSource;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderPreMortemCleanUp;
/**
* Release this classloader from Apache Commons Logging (ACL) by calling
* {@code LogFactory.release(getCurrentClassLoader());}
* Use reflection in case ACL is not present.
* Tip: Do this last, in case other shutdown procedures want to log something.
*
* @author Mattias Jiderhamn
*/
public class ApacheCommonsLoggingCleanUp implements ClassLoaderPreMortemCleanUp {
@Override
public void cleanUp(ClassLoaderLeakPreventor preventor) {
final Class<?> logFactory = preventor.findClass("org.apache.commons.logging.LogFactory");
if(logFactory != null) { // Apache Commons Logging present
try {
final CodeSource codeSource = logFactory.getProtectionDomain().getCodeSource();
final String codeSourceStr = codeSource != null ? codeSource.toString() : "";
if (codeSourceStr.contains("spring-jcl")) {
preventor.info("ignore ApacheCommonsLoggingCleanUp for spring-jcl at " + codeSource);
return;
}
} catch (SecurityException ex) {
preventor.error(ex);
}
preventor.info("Releasing web app classloader from Apache Commons Logging");
try {
logFactory.getMethod("release", java.lang.ClassLoader.class)
.invoke(null, preventor.getClassLoader());
}
catch (Exception ex) {
preventor.error(ex);
}
}
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/BeanELResolverCleanUp.java
================================================
package se.jiderhamn.classloader.leak.prevention.cleanup;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderPreMortemCleanUp;
/**
* Clean for the cache of {@link javax.el.BeanELResolver}, which leaks prior to version 2.2.4.
* @author Mattias Jiderhamn
*/
public class BeanELResolverCleanUp implements ClassLoaderPreMortemCleanUp {
@Override
public void cleanUp(ClassLoaderLeakPreventor preventor) {
java.beans.Introspector.flushCaches(); // This must also be done
final Class<?> beanElResolverClass = preventor.findClass("javax.el.BeanELResolver");
if(beanElResolverClass != null) {
boolean cleared = false;
try {
final Method purgeBeanClasses = beanElResolverClass.getDeclaredMethod("purgeBeanClasses", ClassLoader.class);
purgeBeanClasses.setAccessible(true);
purgeBeanClasses.invoke(beanElResolverClass.newInstance(), preventor.getClassLoader());
cleared = true;
}
catch (NoSuchMethodException e) {
// Version of javax.el probably > 2.2; no real need to clear
}
catch (Exception e) {
preventor.error(e);
}
if(! cleared) {
// Fallback, if purgeBeanClasses() could not be called
final Field propertiesField = preventor.findField(beanElResolverClass, "properties");
if(propertiesField != null) {
try {
final Map<?, ?> properties = (Map<?, ?>) propertiesField.get(null);
properties.clear();
}
catch (Exception e) {
preventor.error(e);
}
}
}
}
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/BeanIntrospectorCleanUp.java
================================================
package se.jiderhamn.classloader.leak.prevention.cleanup;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderPreMortemCleanUp;
/**
* Clear {@link java.beans.Introspector} cache
* @author Mattias Jiderhamn
*/
public class BeanIntrospectorCleanUp implements ClassLoaderPreMortemCleanUp {
@Override
public void cleanUp(ClassLoaderLeakPreventor preventor) {
java.beans.Introspector.flushCaches(); // Clear cache of strong references
clearClassInfoCache(preventor);
}
/**
* Clears the BeanInfo SoftReference-based cache introduced in JDK 9
*
* References:
* * Clear explanation of the root cause: https://bugs.openjdk.java.net/browse/JDK-8207331
* * Issue which triggered the JDK fix: https://bugs.openjdk.java.net/browse/JDK-8231454
* * Fix commit (JDK16+ only as of now): https://github.com/openjdk/jdk/commit/2ee2b4ae
*/
private void clearClassInfoCache(ClassLoaderLeakPreventor preventor) {
try {
final Class<?> classInfoClass = preventor.findClass("com.sun.beans.introspect.ClassInfo");
if (classInfoClass == null) {
return;
}
Field cacheField = preventor.findField(classInfoClass, "CACHE");
if (cacheField == null) {
return; // Either pre-JDK9 or exception occurred (should have been logged as warn at this point)
}
Object cacheInstance = cacheField.get(null);
if (cacheInstance == null) {
return;
}
Method clearMethod = cacheInstance.getClass().getSuperclass().getDeclaredMethod("clear");
clearMethod.invoke(cacheInstance);
}
catch (Exception e) {
preventor.warn(e);
}
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/BeanValidationCleanUp.java
================================================
package se.jiderhamn.classloader.leak.prevention.cleanup;
import java.lang.reflect.Field;
import java.util.Map;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderPreMortemCleanUp;
/**
* Clean up leak caused by cache in {@link javax.validation.Validation}
* @author Mattias Jiderhamn
*/
public class BeanValidationCleanUp implements ClassLoaderPreMortemCleanUp {
@Override
public void cleanUp(ClassLoaderLeakPreventor preventor) {
final Class<?> offendingClass =
preventor.findClass("javax.validation.Validation$DefaultValidationProviderResolver");
if(offendingClass != null) { // Class is present on class path
Field offendingField = preventor.findField(offendingClass, "providersPerClassloader");
if(offendingField != null) {
final Object providersPerClassloader = preventor.getStaticFieldValue(offendingField);
if(providersPerClassloader instanceof Map) { // Map<ClassLoader, List<ValidationProvider<?>>> in offending code
//noinspection SynchronizationOnLocalVariableOrMethodParameter
synchronized (providersPerClassloader) {
// Fix the leak!
((Map<?, ?>)providersPerClassloader).remove(preventor.getClassLoader());
}
}
}
}
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/DefaultAuthenticatorCleanUp.java
================================================
package se.jiderhamn.classloader.leak.prevention.cleanup;
import java.lang.ref.Reference;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.Authenticator;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderPreMortemCleanUp;
/**
* Clear the default {@link java.net.Authenticator} (in case current one is loaded by protected ClassLoader).
* Includes special workaround for CXF issue https://issues.apache.org/jira/browse/CXF-5442
* @author Mattias Jiderhamn
*/
public class DefaultAuthenticatorCleanUp implements ClassLoaderPreMortemCleanUp {
@Override
public void cleanUp(ClassLoaderLeakPreventor preventor) {
final Authenticator defaultAuthenticator = getDefaultAuthenticator(preventor);
if(defaultAuthenticator == null || // Can both mean not set, or error retrieving, so unset anyway to be safe
preventor.isLoadedInClassLoader(defaultAuthenticator)) {
if(defaultAuthenticator != null) // Log warning only if a default was actually found
preventor.warn("Unsetting default " + Authenticator.class.getName() + ": " + defaultAuthenticator);
Authenticator.setDefault(null);
}
else {
if("org.apache.cxf.transport.http.ReferencingAuthenticator".equals(defaultAuthenticator.getClass().getName())) {
/*
Needed since org.apache.cxf.transport.http.ReferencingAuthenticator is loaded by dummy classloader that
references protected classloader via AccessControlContext + ProtectionDomain.
See https://issues.apache.org/jira/browse/CXF-5442
*/
final Class<?> cxfAuthenticator = preventor.findClass("org.apache.cxf.transport.http.CXFAuthenticator");
if(cxfAuthenticator != null && preventor.isLoadedByClassLoader(cxfAuthenticator)) { // CXF loaded in this application
final Object cxfAuthenticator$instance = preventor.getStaticFieldValue(cxfAuthenticator, "instance");
if(cxfAuthenticator$instance != null) { // CXF authenticator has been initialized in protected ClassLoader
final Object authReference = preventor.getFieldValue(defaultAuthenticator, "auth");
if(authReference instanceof Reference) { // WeakReference
final Reference<?> reference = (Reference<?>) authReference;
final Object referencedAuth = reference.get();
if(referencedAuth == cxfAuthenticator$instance) { // References CXFAuthenticator of this classloader
preventor.warn("Default " + Authenticator.class.getName() + " was " + defaultAuthenticator + " that referenced " +
cxfAuthenticator$instance + " loaded by protected ClassLoader");
// Let CXF unwrap in it's own way (in case there are multiple CXF webapps in the container)
reference.clear(); // Remove the weak reference before calling check()
try {
final Method check = defaultAuthenticator.getClass().getMethod("check");
check.setAccessible(true);
check.invoke(defaultAuthenticator);
}
catch (Exception e) {
preventor.error(e);
}
}
}
}
}
}
removeWrappedAuthenticators(preventor, defaultAuthenticator);
preventor.info("Default " + Authenticator.class.getName() + " not loaded by protected ClassLoader: " + defaultAuthenticator);
}
}
/** Find default {@link Authenticator} */
@SuppressWarnings("WeakerAccess")
protected Authenticator getDefaultAuthenticator(ClassLoaderLeakPreventor preventor) {
// Normally Corresponds to getStaticFieldValue(Authenticator.class, "theAuthenticator");
for(final Field f : Authenticator.class.getDeclaredFields()) {
if (f.getType().equals(Authenticator.class)) { // Supposedly "theAuthenticator"
try {
f.setAccessible(true);
return (Authenticator)f.get(null);
} catch (Exception e) {
preventor.error(e);
}
}
}
return null;
}
/**
* Recursively removed wrapped {@link Authenticator} loaded in protected ClassLoader.
* May be needed in case there are multiple CXF applications within the same container.
*/
@SuppressWarnings("WeakerAccess")
protected void removeWrappedAuthenticators(final ClassLoaderLeakPreventor preventor,
final Authenticator authenticator) {
if(authenticator == null)
return;
try {
Class<?> authenticatorClass = authenticator.getClass();
do {
for(final Field f : authenticator.getClass().getDeclaredFields()) {
if(Authenticator.class.isAssignableFrom(f.getType())) {
try {
final boolean isStatic = Modifier.isStatic(f.getModifiers()); // In CXF case this should be false
final Authenticator owner = isStatic ? null : authenticator;
f.setAccessible(true);
final Authenticator wrapped = (Authenticator)f.get(owner);
if(preventor.isLoadedInClassLoader(wrapped)) {
preventor.warn(Authenticator.class.getName() + ": " + wrapped + ", wrapped by " + authenticator +
", is loaded by protected ClassLoader");
f.set(owner, null); // For added safety
}
else {
removeWrappedAuthenticators(preventor, wrapped); // Recurse
}
} catch (Exception e) {
preventor.error(e);
}
}
}
authenticatorClass = authenticatorClass.getSuperclass();
} while (authenticatorClass != null && authenticatorClass != Object.class);
}
catch (Exception e) { // Should never happen
preventor.error(e);
}
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/DriverManagerCleanUp.java
================================================
package se.jiderhamn.classloader.leak.prevention.cleanup;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderPreMortemCleanUp;
/**
* Deregister JDBC drivers loaded by classloader
* @author Mattias Jiderhamn
*/
public class DriverManagerCleanUp implements ClassLoaderPreMortemCleanUp {
@Override
public void cleanUp(ClassLoaderLeakPreventor preventor) {
final List<Driver> driversToDeregister = new ArrayList<Driver>();
final Enumeration<Driver> allDrivers = DriverManager.getDrivers();
while(allDrivers.hasMoreElements()) {
final Driver driver = allDrivers.nextElement();
if(preventor.isLoadedInClassLoader(driver)) // Should be true for all returned by DriverManager.getDrivers()
driversToDeregister.add(driver);
}
for(Driver driver : driversToDeregister) {
try {
preventor.warn("JDBC driver loaded by protected ClassLoader deregistered: " + driver.getClass());
DriverManager.deregisterDriver(driver);
}
catch (SQLException e) {
preventor.error(e);
}
}
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/GeoToolsCleanUp.java
================================================
package se.jiderhamn.classloader.leak.prevention.cleanup;
import java.lang.reflect.Field;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderPreMortemCleanUp;
/**
* Shutdown GeoTools cleaner thread as of https://osgeo-org.atlassian.net/browse/GEOT-2742
* @author Mattias Jiderhamn
*/
public class GeoToolsCleanUp implements ClassLoaderPreMortemCleanUp {
@Override
public void cleanUp(ClassLoaderLeakPreventor preventor) {
final Class<?> weakCollectionCleanerClass = preventor.findClass("org.geotools.util.WeakCollectionCleaner");
if(weakCollectionCleanerClass != null) {
try {
final Field DEFAULT = preventor.findField(weakCollectionCleanerClass, "DEFAULT");
weakCollectionCleanerClass.getMethod("exit").invoke(DEFAULT.get(null));
}
catch (Exception ex) {
preventor.error(ex);
}
}
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/IIOServiceProviderCleanUp.java
================================================
package se.jiderhamn.classloader.leak.prevention.cleanup;
import java.lang.reflect.Field;
import java.security.AccessControlContext;
import java.util.*;
import javax.imageio.spi.IIORegistry;
import javax.imageio.spi.IIOServiceProvider;
import javax.imageio.spi.ServiceRegistry;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderPreMortemCleanUp;
/**
* Unregister ImageIO Service Provider loaded by the protected ClassLoader
* @author Thomas Scheffler (1.x version)
* @author Mattias Jiderhamn
*/
public class IIOServiceProviderCleanUp implements ClassLoaderPreMortemCleanUp {
@Override
public void cleanUp(final ClassLoaderLeakPreventor preventor) {
final IIORegistry registry = IIORegistry.getDefaultInstance();
Iterator<Class<?>> categories = registry.getCategories();
ServiceRegistry.Filter classLoaderFilter = new ServiceRegistry.Filter() {
@Override
public boolean filter(Object provider) {
//remove all service provider loaded by the current ClassLoader
return preventor.isLoadedInClassLoader(provider);
}
};
while (categories.hasNext()) {
@SuppressWarnings("unchecked")
Class<IIOServiceProvider> category = (Class<IIOServiceProvider>) categories.next();
Iterator<IIOServiceProvider> serviceProviders = registry.getServiceProviders(
category,
classLoaderFilter, true);
if (serviceProviders.hasNext()) {
//copy to list
List<IIOServiceProvider> serviceProviderList = new ArrayList<IIOServiceProvider>();
while (serviceProviders.hasNext()) {
serviceProviderList.add(serviceProviders.next());
}
for (IIOServiceProvider serviceProvider : serviceProviderList) {
preventor.warn("ImageIO " + category.getSimpleName() + " service provider deregistered: "
+ serviceProvider.getDescription(Locale.ROOT));
registry.deregisterServiceProvider(serviceProvider);
}
}
}
// Leak as of Java 1.8u141, see https://github.com/mjiderhamn/classloader-leak-prevention/issues/71
// The providers are probably registered by SunAwtAppContextInitiator
final Field accMapField = preventor.findFieldOfClass("javax.imageio.spi.SubRegistry", "accMap");
if(accMapField != null) {
final Field categoryMapField = preventor.findField(ServiceRegistry.class, "categoryMap");
if(categoryMapField != null) {
final Map categoryMap = preventor.getFieldValue(categoryMapField, registry);
if(categoryMap != null) {
for(/*SubRegistry*/ Object subRegistry : categoryMap.values()) {
final Map<Class<?>, AccessControlContext> accMap = preventor.getFieldValue(accMapField, subRegistry);
if(accMap != null) {
for(AccessControlContext acc : accMap.values()) {
preventor.removeDomainCombiner(IIORegistry.class.getName(), acc);
}
}
}
}
}
}
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/IntrospectionUtilsCleanUp.java
================================================
package se.jiderhamn.classloader.leak.prevention.cleanup;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderPreMortemCleanUp;
/**
* Clear IntrospectionUtils caches of Tomcat and Apache Commons Modeler
* @author Mattias Jiderhamn
*/
public class IntrospectionUtilsCleanUp implements ClassLoaderPreMortemCleanUp {
@Override
public void cleanUp(ClassLoaderLeakPreventor preventor) {
// Tomcat
final Class<?> tomcatIntrospectionUtils = preventor.findClass("org.apache.tomcat.util.IntrospectionUtils");
if(tomcatIntrospectionUtils != null) {
try {
tomcatIntrospectionUtils.getMethod("clear").invoke(null);
}
catch (Exception ex) {
if(! preventor.isJBoss()) // JBoss includes this class, but no cache and no clear() method
preventor.error(ex);
}
}
// Apache Commons Modeler
final Class<?> modelIntrospectionUtils = preventor.findClass("org.apache.commons.modeler.util.IntrospectionUtils");
if(modelIntrospectionUtils != null && ! preventor.isClassLoaderOrChild(modelIntrospectionUtils.getClassLoader())) { // Loaded outside protected ClassLoader
try {
modelIntrospectionUtils.getMethod("clear").invoke(null);
}
catch (Exception ex) {
preventor.warn("org.apache.commons.modeler.util.IntrospectionUtils needs to be cleared but there was an error, " +
"consider upgrading Apache Commons Modeler");
preventor.error(ex);
}
}
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/JDK8151486CleanUp.java
================================================
package se.jiderhamn.classloader.leak.prevention.cleanup;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderPreMortemCleanUp;
import java.lang.reflect.Field;
import java.util.Set;
/**
* Clear the "domains" field of the parent ClassLoader.
*
* See <a href="https://bugs.openjdk.java.net/browse/JDK-8151486">JDK-8151486</a>
*/
public class JDK8151486CleanUp implements ClassLoaderPreMortemCleanUp {
@Override
public void cleanUp(ClassLoaderLeakPreventor preventor) {
Field field = preventor.findField(ClassLoader.class, "domains");
if (field == null) {
// field only exists in JDK versions [8u25, 9u140)
return;
}
for (ClassLoader cl = preventor.getClassLoader().getParent(); cl != null; cl = cl.getParent()) {
Set<?> domains = preventor.getFieldValue(field, cl);
if (domains != null) {
domains.clear();
}
}
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/JacksonCleanUp.java
================================================
package se.jiderhamn.classloader.leak.prevention.cleanup;
import java.lang.reflect.Method;
import java.util.Map;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderPreMortemCleanUp;
/**
* Clear Jackson TypeFactory cache as per https://github.com/FasterXML/jackson-databind/issues/1363
* @author Mattias Jiderhamn
*/
public class JacksonCleanUp implements ClassLoaderPreMortemCleanUp {
@Override
public void cleanUp(ClassLoaderLeakPreventor preventor) {
final Class<?> typeFactoryClass = preventor.findClass("com.fasterxml.jackson.databind.type.TypeFactory");
if(typeFactoryClass != null && ! preventor.isLoadedInClassLoader(typeFactoryClass)) {
try {
final Method defaultInstance = preventor.findMethod(typeFactoryClass, "defaultInstance");
if(defaultInstance != null) {
final Object defaultTypeFactory = defaultInstance.invoke(null);
if(defaultTypeFactory != null) {
final Method clearCache = preventor.findMethod(typeFactoryClass, "clearCache");
if(clearCache != null) {
clearCache.invoke(defaultTypeFactory);
}
else { // Version < 2.4.1
final Object typeCache = preventor.getFieldValue(defaultTypeFactory, "_typeCache");
if(typeCache instanceof Map) {
//noinspection SynchronizationOnLocalVariableOrMethodParameter
synchronized (typeCache) {
((Map) typeCache).clear();
}
}
}
}
}
}
catch (Exception e) {
preventor.error(e);
}
}
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/JavaServerFaces2746CleanUp.java
================================================
package se.jiderhamn.classloader.leak.prevention.cleanup;
import java.util.HashSet;
import java.util.Set;
import java.util.WeakHashMap;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderPreMortemCleanUp;
/**
* Workaround for leak caused by Mojarra JSF implementation if included in the container.
* See <a href="http://java.net/jira/browse/JAVASERVERFACES-2746">JAVASERVERFACES-2746</a>
* @author Mattias Jiderhamn
*/
public class JavaServerFaces2746CleanUp implements ClassLoaderPreMortemCleanUp {
@Override
public void cleanUp(ClassLoaderLeakPreventor preventor) {
/*
Note that since a WeakHashMap is used, it is not the map key that is the problem. However the value is a
Map with java.beans.PropertyDescriptor as value, and java.beans.PropertyDescriptor has a Hashtable in which
a class is put with "type" as key. This class may have been loaded by the protected ClassLoader.
One example case is the class org.primefaces.component.menubutton.MenuButton that points to a Map with a key
"model" whose PropertyDescriptor.table has key "type" with the class org.primefaces.model.MenuModel as its value.
For performance reasons however, we'll only look at the top level key and remove any that has been loaded by
protected ClassLoader.
*/
Object o = preventor.getStaticFieldValue("javax.faces.component.UIComponentBase", "descriptors"); // Non-static as of JSF 2.2.5
if(o instanceof WeakHashMap) {
WeakHashMap<?, ?> descriptors = (WeakHashMap<?, ?>) o;
final Set<Class<?>> toRemove = new HashSet<Class<?>>();
for(Object key : descriptors.keySet()) {
if(key instanceof Class && preventor.isLoadedByClassLoader((Class<?>)key)) {
// For performance reasons, remove all classes loaded by protected ClassLoader
toRemove.add((Class<?>) key);
// This would be more correct, but presumably slower
/*
Map<String, PropertyDescriptor> m = (Map<String, PropertyDescriptor>) descriptors.get(key);
for(Map.Entry<String,PropertyDescriptor> entry : m.entrySet()) {
Object type = entry.getValue().getValue("type"); // Key constant javax.el.ELResolver.TYPE
if(type instanceof Class && isLoadedByWebApplication((Class)type)) {
toRemove.add((Class) key);
}
}
*/
}
}
if(! toRemove.isEmpty()) {
preventor.info("Removing " + toRemove.size() + " classes from Mojarra descriptors cache");
for(Class<?> clazz : toRemove) {
descriptors.remove(clazz);
}
}
}
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/JavaUtilLoggingLevelCleanUp.java
================================================
package se.jiderhamn.classloader.leak.prevention.cleanup;
import java.lang.reflect.Field;
import java.util.*;
import java.util.logging.Level;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderPreMortemCleanUp;
/**
* Cleanup for removing custom {@link java.util.logging.Level}s loaded within the protected class loader.
* @author Mattias Jiderhamn
*/
public class JavaUtilLoggingLevelCleanUp implements ClassLoaderPreMortemCleanUp {
@Override
public void cleanUp(ClassLoaderLeakPreventor preventor) {
final Class<?> knownLevelClass = preventor.findClass("java.util.logging.Level$KnownLevel");
if(knownLevelClass != null) {
final Field levelObjectField = preventor.findField(knownLevelClass, "levelObject");
if(levelObjectField != null) {
//noinspection SynchronizationOnLocalVariableOrMethodParameter
synchronized (knownLevelClass) {
final Map<?, List/*<KnownLevel>*/> nameToLevels = preventor.getStaticFieldValue(knownLevelClass, "nameToLevels");
final Map<?, List/*<KnownLevel>*/> intToLevels = preventor.getStaticFieldValue(knownLevelClass, "intToLevels");
if(nameToLevels != null) {
final Set/*<KnownLevel>*/ removed = process(preventor, knownLevelClass, levelObjectField, nameToLevels);
if(intToLevels != null) {
for(List/*<KnownLevel>*/ knownLevels : intToLevels.values()) {
knownLevels.removeAll(removed);
}
}
}
else if(intToLevels != null) { // Use intToLevels as fallback; both should contain same values
process(preventor, knownLevelClass, levelObjectField, intToLevels);
}
}
}
else
preventor.warn("Found " + knownLevelClass + " but not levelObject field");
}
}
private Set/*<KnownLevel>*/ process(ClassLoaderLeakPreventor preventor, Class<?> knownLevelClass,
Field levelObjectField, Map<?, List/*<KnownLevel>*/> levelsMaps) {
final Set/*<KnownLevel>*/ output = new HashSet<Object>();
for(List/*<KnownLevel>*/ knownLevels : levelsMaps.values()) {
for(Iterator/*<KnownLevel>*/ iter = knownLevels.listIterator(); iter.hasNext(); ) {
final Object /* KnownLevel */ knownLevel = iter.next();
final Level levelObject = preventor.getFieldValue(levelObjectField, knownLevel);
if(preventor.isLoadedInClassLoader(levelObject)) {
preventor.warn(Level.class.getName() + " subclass loaded by protected ClassLoader: " +
levelObject.getClass() + "; removing from " + knownLevelClass);
iter.remove();
output.add(knownLevel);
}
}
}
return output;
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/JavaxSecurityAuthLoginConfigurationCleanUp.java
================================================
package se.jiderhamn.classloader.leak.prevention.cleanup;
import javax.security.auth.login.Configuration;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderPreMortemCleanUp;
/**
* Cleanup for removing custom {@link javax.security.auth.login.Configuration}s loaded within the protected class loader.
* @author Nikos Epping
*/
public class JavaxSecurityAuthLoginConfigurationCleanUp implements ClassLoaderPreMortemCleanUp {
@Override
public void cleanUp(ClassLoaderLeakPreventor preventor) {
if (preventor.isLoadedInClassLoader(Configuration.getConfiguration())) {
Configuration.setConfiguration(null);
}
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/JceSecurityCleanUp.java
================================================
package se.jiderhamn.classloader.leak.prevention.cleanup;
import java.net.URL;
import java.security.Provider;
import java.util.Map;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderPreMortemCleanUp;
/**
* Clean up for the static caches of {@link javax.crypto.JceSecurity}
* @author Mattias Jiderhamn
*/
public class JceSecurityCleanUp implements ClassLoaderPreMortemCleanUp {
@SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter")
@Override
public void cleanUp(ClassLoaderLeakPreventor preventor) {
final Class<?> javax_crypto_JceSecurity = preventor.findClass("javax.crypto.JceSecurity");
if(javax_crypto_JceSecurity != null) {
synchronized (javax_crypto_JceSecurity) { // synchronized methods are used for querying and updating the caches
final Map<Provider, Object> verificationResults = preventor.getStaticFieldValue(javax_crypto_JceSecurity, "verificationResults");
final Map<Provider, Object> verifyingProviders = preventor.getStaticFieldValue(javax_crypto_JceSecurity, "verifyingProviders");
final Map<Class<?>, URL> codeBaseCacheRef = preventor.getStaticFieldValue(javax_crypto_JceSecurity, "codeBaseCacheRef");
if(verificationResults != null) {
verificationResults.clear();
}
if(verifyingProviders != null) {
verifyingProviders.clear();
}
if(codeBaseCacheRef != null) {
codeBaseCacheRef.clear();
}
}
}
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/KeepAliveTimerCacheCleanUp.java
================================================
package se.jiderhamn.classloader.leak.prevention.cleanup;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderPreMortemCleanUp;
import se.jiderhamn.classloader.leak.prevention.MustBeAfter;
/**
* Since Keep-Alive-Timer thread may have terminated, but still be referenced, we need to make sure it does not
* reference this classloader.
* @author Mattias Jiderhamn
*/
public class KeepAliveTimerCacheCleanUp implements ClassLoaderPreMortemCleanUp, MustBeAfter<ClassLoaderPreMortemCleanUp> {
/** Needs to be done after {@link StopThreadsCleanUp}, since in there the Keep-Alive-Timer may be stopped. */
@Override
public Class<? extends ClassLoaderPreMortemCleanUp>[] mustBeBeforeMe() {
return new Class[] {StopThreadsCleanUp.class};
}
@Override
public void cleanUp(ClassLoaderLeakPreventor preventor) {
Object keepAliveCache = preventor.getStaticFieldValue("sun.net.www.http.HttpClient", "kac", true);
if(keepAliveCache != null) {
final Thread keepAliveTimer = preventor.getFieldValue(keepAliveCache, "keepAliveTimer");
if(keepAliveTimer != null) {
if(preventor.isClassLoaderOrChild(keepAliveTimer.getContextClassLoader())) {
keepAliveTimer.setContextClassLoader(preventor.getLeakSafeClassLoader());
preventor.error("ContextClassLoader of sun.net.www.http.HttpClient cached Keep-Alive-Timer set to " + preventor.getLeakSafeClassLoader());
}
}
}
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/MBeanCleanUp.java
================================================
package se.jiderhamn.classloader.leak.prevention.cleanup;
import java.lang.management.ManagementFactory;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderPreMortemCleanUp;
/**
* Unregister MBeans loaded by the protected class loader
* @author Mattias Jiderhamn
* @author rapla
*/
public class MBeanCleanUp implements ClassLoaderPreMortemCleanUp {
@Override
public void cleanUp(ClassLoaderLeakPreventor preventor) {
try {
final MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
final Set<ObjectName> allMBeanNames = mBeanServer.queryNames(new ObjectName("*:*"), null);
// Special treatment for Jetty, see https://bugs.eclipse.org/bugs/show_bug.cgi?id=423255
JettyJMXRemover jettyJMXRemover = null;
if(isJettyWithJMX(preventor)) {
try {
jettyJMXRemover = new JettyJMXRemover(preventor);
}
catch (Exception ex) {
preventor.error(ex);
}
}
// Look for custom MBeans
for(ObjectName objectName : allMBeanNames) {
try {
if (jettyJMXRemover != null && jettyJMXRemover.unregisterJettyJMXBean(objectName)) {
continue;
}
final ClassLoader mBeanClassLoader = mBeanServer.getClassLoaderFor(objectName);
if(preventor.isClassLoaderOrChild(mBeanClassLoader)) { // MBean loaded by protected ClassLoader
preventor.warn("MBean '" + objectName + "' was loaded by protected ClassLoader; unregistering");
mBeanServer.unregisterMBean(objectName);
}
/*
else if(... instanceof NotificationBroadcasterSupport) {
unregisterNotificationListeners((NotificationBroadcasterSupport) ...);
}
*/
}
catch(Exception e) { // MBeanRegistrationException / InstanceNotFoundException
preventor.error(e);
}
}
}
catch (Exception e) { // MalformedObjectNameException
preventor.error(e);
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Methods and classes for Jetty, see https://bugs.eclipse.org/bugs/show_bug.cgi?id=423255
/** Are we running in Jetty with JMX enabled? */
@SuppressWarnings("WeakerAccess")
protected boolean isJettyWithJMX(ClassLoaderLeakPreventor preventor) {
final ClassLoader classLoader = preventor.getClassLoader();
try {
// If package org.eclipse.jetty is found, we may be running under jetty
if (classLoader.getResource("org/eclipse/jetty") == null) {
return false;
}
Class.forName("org.eclipse.jetty.jmx.MBeanContainer", false, classLoader.getParent()); // JMX enabled?
Class.forName("org.eclipse.jetty.webapp.WebAppContext", false, classLoader.getParent());
}
catch(Exception ex) { // For example ClassNotFoundException
return false;
}
// Seems we are running in Jetty with JMX enabled
return true;
}
/**
* Inner utility class that helps dealing with Jetty MBeans class.
* If you enable JMX support in Jetty 8 or 9 some MBeans (e.g. for the ServletHolder or SessionManager) are
* instantiated in the web application thread and a reference to the WebappClassloader is stored in a private
* ObjectMBean._loader which is unfortunately not the classloader that loaded the class. Therefore we need to access
* the MBeanContainer class of the Jetty container and unregister the MBeans.
*/
private class JettyJMXRemover {
private final ClassLoaderLeakPreventor preventor;
/** List of objects that may be wrapped in MBean by Jetty. Should be allowed to contain null. */
private List<Object> objectsWrappedWithMBean;
/** The org.eclipse.jetty.jmx.MBeanContainer instance */
private Object beanContainer;
/** org.eclipse.jetty.jmx.MBeanContainer.findBean() */
private Method findBeanMethod;
/** org.eclipse.jetty.jmx.MBeanContainer.removeBean() */
private Method removeBeanMethod;
@SuppressWarnings("WeakerAccess")
public JettyJMXRemover(ClassLoaderLeakPreventor preventor) throws Exception {
this.preventor = preventor;
// First we need access to the MBeanContainer to access the beans
// WebAppContext webappContext = (WebAppContext)servletContext;
final Object webappContext = findJettyClass("org.eclipse.jetty.webapp.WebAppClassLoader")
.getMethod("getContext").invoke(preventor.getClassLoader());
if(webappContext == null)
return;
// Server server = (Server)webappContext.getServer();
final Class<?> webAppContextClass = findJettyClass("org.eclipse.jetty.webapp.WebAppContext");
final Object server = webAppContextClass.getMethod("getServer").invoke(webappContext);
if(server == null)
return;
// MBeanContainer beanContainer = (MBeanContainer)server.getBean(MBeanContainer.class);
final Class<?> mBeanContainerClass = findJettyClass("org.eclipse.jetty.jmx.MBeanContainer");
beanContainer = findJettyClass("org.eclipse.jetty.server.Server")
.getMethod("getBean", Class.class).invoke(server, mBeanContainerClass);
// Now we store all objects that belong to the web application and that will be wrapped by MBeans in a list
if (beanContainer != null) {
findBeanMethod = mBeanContainerClass.getMethod("findBean", ObjectName.class);
try {
removeBeanMethod = mBeanContainerClass.getMethod("removeBean", Object.class);
} catch (NoSuchMethodException e) {
preventor.warn("MBeanContainer.removeBean() method can not be found. giving up");
return;
}
objectsWrappedWithMBean = new ArrayList<Object>();
// SessionHandler sessionHandler = webappContext.getSessionHandler();
final Object sessionHandler = webAppContextClass.getMethod("getSessionHandler").invoke(webappContext);
if(sessionHandler != null) {
objectsWrappedWithMBean.add(sessionHandler);
// SessionManager sessionManager = sessionHandler.getSessionManager();
final Object sessionManager = findJettyClass("org.eclipse.jetty.server.session.SessionHandler")
.getMethod("getSessionManager").invoke(sessionHandler);
if(sessionManager != null) {
objectsWrappedWithMBean.add(sessionManager);
// SessionIdManager sessionIdManager = sessionManager.getSessionIdManager();
final Object sessionIdManager = findJettyClass("org.eclipse.jetty.server.SessionManager")
.getMethod("getSessionIdManager").invoke(sessionManager);
objectsWrappedWithMBean.add(sessionIdManager);
}
}
// SecurityHandler securityHandler = webappContext.getSecurityHandler();
objectsWrappedWithMBean.add(webAppContextClass.getMethod("getSecurityHandler").invoke(webappContext));
// ServletHandler servletHandler = webappContext.getServletHandler();
final Object servletHandler = webAppContextClass.getMethod("getServletHandler").invoke(webappContext);
if(servletHandler != null) {
objectsWrappedWithMBean.add(servletHandler);
final Class<?> servletHandlerClass = findJettyClass("org.eclipse.jetty.servlet.ServletHandler");
// Object[] servletMappings = servletHandler.getServletMappings();
objectsWrappedWithMBean.add(Arrays.asList((Object[]) servletHandlerClass.getMethod("getServletMappings").invoke(servletHandler)));
// Object[] servlets = servletHandler.getServlets();
objectsWrappedWithMBean.add(Arrays.asList((Object[]) servletHandlerClass.getMethod("getServlets").invoke(servletHandler)));
}
}
}
/**
* Test if objectName denotes a wrapping Jetty MBean and if so unregister it.
* @return {@code true} if Jetty MBean was unregistered, otherwise {@code false}
*/
boolean unregisterJettyJMXBean(ObjectName objectName) {
if (objectsWrappedWithMBean == null || ! objectName.getDomain().contains("org.eclipse.jetty")) {
return false;
}
else { // Possibly a Jetty MBean that needs to be unregistered
try {
final Object bean = findBeanMethod.invoke(beanContainer, objectName);
if(bean == null)
return false;
// Search suspect list
for (Object wrapped : objectsWrappedWithMBean) {
if (wrapped == bean) {
preventor.warn("Jetty MBean '" + objectName + "' is a suspect in causing memory leaks; unregistering");
removeBeanMethod.invoke(beanContainer, bean); // Remove it via the MBeanContainer
return true;
}
}
}
catch (Exception ex) {
preventor.error(ex);
}
return false;
}
}
Class findJettyClass(String className) throws ClassNotFoundException {
try {
return Class.forName(className, false, preventor.getClassLoader());
} catch (ClassNotFoundException e1) {
try {
return Class.forName(className);
} catch (ClassNotFoundException e2) {
e2.addSuppressed(e1);
throw e2;
}
}
}
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/MXBeanNotificationListenersCleanUp.java
================================================
package se.jiderhamn.classloader.leak.prevention.cleanup;
import java.lang.management.ManagementFactory;
import java.lang.management.PlatformManagedObject;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Set;
import javax.management.*;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderPreMortemCleanUp;
/**
* Unregister MBeans, MXBean {@link NotificationListener}s/{@link NotificationFilter}s/handbacks loaded by the
* protected class loader
* @author Mattias Jiderhamn
*/
public class MXBeanNotificationListenersCleanUp implements ClassLoaderPreMortemCleanUp {
@Override
public void cleanUp(ClassLoaderLeakPreventor preventor) {
final Class<?> notificationEmitterSupportClass = preventor.findClass("sun.management.NotificationEmitterSupport");
final Field listenerListField = preventor.findField(notificationEmitterSupportClass, "listenerList");
final Class<?> listenerInfoClass = preventor.findClass("sun.management.NotificationEmitterSupport$ListenerInfo");
final Field listenerField = preventor.findField(listenerInfoClass, "listener");
final Field filterField = preventor.findField(listenerInfoClass, "filter");
final Field handbackField = preventor.findField(listenerInfoClass, "handback");
final Class<?> listenerWrapperClass = preventor.findClass("com.sun.jmx.interceptor.DefaultMBeanServerInterceptor$ListenerWrapper");
final boolean canProcessNotificationEmitterSupport = listenerListField != null && listenerInfoClass != null && listenerField != null && filterField != null && handbackField != null;
if (!canProcessNotificationEmitterSupport) {
preventor.warn("Unable to unregister NotificationEmitterSupport listeners, because details could not be found using reflection");
}
final Set<Class<? extends PlatformManagedObject>> platformInterfaces = ManagementFactory.getPlatformManagementInterfaces();
if (platformInterfaces != null) {
for (Class<? extends PlatformManagedObject> platformInterface : platformInterfaces) {
for (Object mxBean : ManagementFactory.getPlatformMXBeans(platformInterface)) {
if (mxBean instanceof NotificationEmitter) { // The MXBean may have NotificationListeners
if (canProcessNotificationEmitterSupport && notificationEmitterSupportClass.isAssignableFrom(mxBean.getClass())) {
final List<? /* NotificationEmitterSupport.ListenerInfo */> listenerList = preventor.getFieldValue(listenerListField, mxBean);
if (listenerList != null) {
for (Object listenerInfo : listenerList) { // Loop all listeners
final NotificationListener listener = preventor.getFieldValue(listenerField, listenerInfo);
final NotificationListener rawListener = unwrap(preventor, listenerWrapperClass, listener);
final NotificationFilter filter = preventor.getFieldValue(filterField, listenerInfo);
final Object handback = preventor.getFieldValue(handbackField, listenerInfo);
if (preventor.isLoadedInClassLoader(rawListener) || preventor.isLoadedInClassLoader(filter) || preventor.isLoadedInClassLoader(handback)) {
preventor.warn(((listener == rawListener) ? "Listener '" : "Wrapped listener '") + listener +
"' (or its filter or handback) of MXBean " + mxBean +
" of PlatformManagedObject " + platformInterface + " was loaded in protected ClassLoader; removing");
// This is safe, as the implementation (as of this writing) works with a copy,
// not altering the original
try {
((NotificationEmitter) mxBean).removeNotificationListener(listener, filter, handback);
}
catch (ListenerNotFoundException e) { // Should never happen
preventor.error(e);
}
}
}
}
}
else if(mxBean instanceof NotificationBroadcasterSupport) { // Unlikely case
unregisterNotificationListeners(preventor, (NotificationBroadcasterSupport) mxBean, listenerWrapperClass);
}
}
}
}
}
}
/**
* Unregister {@link NotificationListener}s from subclass of {@link NotificationBroadcasterSupport}, if listener,
* filter or handback is loaded by the protected ClassLoader.
*/
protected void unregisterNotificationListeners(ClassLoaderLeakPreventor preventor, NotificationBroadcasterSupport mBean,
final Class<?> listenerWrapperClass) {
final Field listenerListField = preventor.findField(NotificationBroadcasterSupport.class, "listenerList");
if(listenerListField != null) {
final Class<?> listenerInfoClass = preventor.findClass("javax.management.NotificationBroadcasterSupport$ListenerInfo");
final List<? /*javax.management.NotificationBroadcasterSupport.ListenerInfo*/> listenerList =
preventor.getFieldValue(listenerListField, mBean);
if(listenerList != null) {
final Field listenerField = preventor.findField(listenerInfoClass, "listener");
final Field filterField = preventor.findField(listenerInfoClass, "filter");
final Field handbackField = preventor.findField(listenerInfoClass, "handback");
for(Object listenerInfo : listenerList) {
final NotificationListener listener = preventor.getFieldValue(listenerField, listenerInfo);
final NotificationListener rawListener = unwrap(preventor, listenerWrapperClass, listener);
final NotificationFilter filter = preventor.getFieldValue(filterField, listenerInfo);
final Object handback = preventor.getFieldValue(handbackField, listenerInfo);
if(preventor.isLoadedInClassLoader(rawListener) || preventor.isLoadedInClassLoader(filter) || preventor.isLoadedInClassLoader(handback)) {
preventor.warn(((listener == rawListener) ? "Listener '" : "Wrapped listener '") + listener +
"' (or its filter or handback) of MBean " + mBean +
" was loaded in protected ClassLoader; removing");
// This is safe, as the implementation works with a copy, not altering the original
try {
mBean.removeNotificationListener(listener, filter, handback);
}
catch (ListenerNotFoundException e) { // Should never happen
preventor.error(e);
}
}
}
}
}
}
/** Unwrap {@link NotificationListener} wrapped by {@link com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.ListenerWrapper} */
private NotificationListener unwrap(ClassLoaderLeakPreventor preventor, Class<?> listenerWrapperClass, NotificationListener listener) {
if(listenerWrapperClass != null && listenerWrapperClass.isInstance(listener)) {
return preventor.getFieldValue(listener, "listener"); // Unwrap
}
else
return listener;
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/MoxyCleanUp.java
================================================
package se.jiderhamn.classloader.leak.prevention.cleanup;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ConcurrentModificationException;
import java.util.Map;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderPreMortemCleanUp;
/**
* Cleanup for leak caused by EclipseLink MOXy
* See https://bugs.eclipse.org/bugs/show_bug.cgi?id=529270
* @author Mattias Jiderhamn
*/
public class MoxyCleanUp implements ClassLoaderPreMortemCleanUp {
@Override
public void cleanUp(ClassLoaderLeakPreventor preventor) {
final Class<?> helperClass = findClass(preventor, "org.eclipse.persistence.jaxb.javamodel.Helper");
if(helperClass != null) {
unsetField(preventor, helperClass, "COLLECTION_CLASS");
unsetField(preventor, helperClass, "LIST_CLASS");
unsetField(preventor, helperClass, "SET_CLASS");
unsetField(preventor, helperClass, "MAP_CLASS");
unsetField(preventor, helperClass, "JAXBELEMENT_CLASS");
unsetField(preventor, helperClass, "OBJECT_CLASS");
}
final Class<?> propertyClass = findClass(preventor, "org.eclipse.persistence.jaxb.compiler.Property");
if(propertyClass != null) {
unsetField(preventor, propertyClass, "OBJECT_CLASS");
unsetField(preventor, propertyClass, "XML_ADAPTER_CLASS");
}
}
public Class<?> findClass(ClassLoaderLeakPreventor preventor, String className) {
try {
return Class.forName(className, true, preventor.getLeakSafeClassLoader());
}
catch (ClassNotFoundException e) {
// Silently ignore
return null;
}
catch (Exception ex) { // Example SecurityException
preventor.warn(ex);
return null;
}
}
private void unsetField(ClassLoaderLeakPreventor preventor,
Class<?> clazz, String fieldName) {
final Field field = preventor.findField(clazz, fieldName);
if(field != null) {
try {
final Object /* org.eclipse.persistence.jaxb.javamodel.reflection.JavaClassImpl */ javaClass = field.get(null);
if(javaClass != null) {
final Object /* org.eclipse.persistence.jaxb.javamodel.reflection.JavaModelImpl */ javaModelImpl =
preventor.getFieldValue(javaClass, "javaModelImpl");
if(javaModelImpl != null) {
final Method getClassLoader = preventor.findMethod(javaModelImpl.getClass(), "getClassLoader");
if(getClassLoader != null) {
final ClassLoader classLoader = (ClassLoader) getClassLoader.invoke(javaModelImpl);
if(preventor.isClassLoaderOrChild(classLoader)) {
preventor.info("Changing ClassLoader of " + field);
preventor.findMethod(javaModelImpl.getClass(), "setClassLoader", ClassLoader.class)
.invoke(javaModelImpl, preventor.getLeakSafeClassLoader());
final Field isJaxbClassLoader = preventor.findField(javaModelImpl.getClass(), "isJaxbClassLoader");
if(isJaxbClassLoader != null) {
isJaxbClassLoader.set(javaModelImpl, false);
}
}
}
else
preventor.error("Cannot get ClassLoader of " + javaModelImpl);
// Clear cachedJavaClasses
final Map cachedJavaClasses = preventor.getFieldValue(javaModelImpl, "cachedJavaClasses");
if(cachedJavaClasses != null) {
try {
cachedJavaClasses.clear();
}
catch (ConcurrentModificationException e) {
preventor.error("Unable to clear " + javaModelImpl + ".cachedJavaClasses");
}
}
}
else {
preventor.error("Cannot get javaModelImpl of " + javaClass);
field.set(null, null);
}
}
}
catch (Exception e) {
preventor.warn(e);
}
}
else
preventor.warn("Unable to find field " + fieldName + " of class " + clazz);
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/MultiThreadedHttpConnectionManagerCleanUp.java
================================================
package se.jiderhamn.classloader.leak.prevention.cleanup;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderPreMortemCleanUp;
/**
* Invokes static method org.apache.commons.httpclient.MultiThreadedHttpConnectionManager.shutdownAll() to close connections left out by com.sun.jersey.client.apache.ApacheHttpClient.
*
* @author Marian Petrik
*/
public class MultiThreadedHttpConnectionManagerCleanUp implements ClassLoaderPreMortemCleanUp {
@Override
public void cleanUp(ClassLoaderLeakPreventor preventor) {
final Class<?> connManager = preventor.findClass("org.apache.commons.httpclient.MultiThreadedHttpConnectionManager");
if(connManager != null && preventor.isLoadedByClassLoader(connManager)) {
try {
connManager.getMethod("shutdownAll").invoke(null);
}
catch (Throwable t) {
preventor.warn(t);
}
}
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/ObjectStreamClassCleanup.java
================================================
package se.jiderhamn.classloader.leak.prevention.cleanup;
import java.util.concurrent.ConcurrentHashMap;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderPreMortemCleanUp;
/**
* Clean up for the static caches of {@link java.io.ObjectStreamClass}
*/
public class ObjectStreamClassCleanup implements ClassLoaderPreMortemCleanUp {
@Override
public void cleanUp(ClassLoaderLeakPreventor preventor) {
try {
final Class<?> cacheClass = preventor.findClass("java.io.ObjectStreamClass$Caches");
if (cacheClass == null) { return; }
Object localDescsCache = preventor.getStaticFieldValue(cacheClass, "localDescs");
clearIfConcurrentHashMap(localDescsCache, preventor);
Object reflectorsCache = preventor.getStaticFieldValue(cacheClass, "reflectors");
clearIfConcurrentHashMap(reflectorsCache, preventor);
}
catch (Exception e) {
preventor.error(e);
}
}
protected void clearIfConcurrentHashMap(Object object, ClassLoaderLeakPreventor preventor) {
if (!(object instanceof ConcurrentHashMap)) { return; }
ConcurrentHashMap<?,?> map = (ConcurrentHashMap<?,?>) object;
int nbOfEntries=map.size();
map.clear();
preventor.info("Detected and fixed leak situation for java.io.ObjectStreamClass ("+nbOfEntries+" entries were flushed).");
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/PropertyEditorCleanUp.java
================================================
package se.jiderhamn.classloader.leak.prevention.cleanup;
import java.beans.PropertyEditorManager;
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderPreMortemCleanUp;
/**
* Deregister custom property editors.
* This has been fixed in Java 7.
* @author Mattias Jiderhamn
*/
public class PropertyEditorCleanUp implements ClassLoaderPreMortemCleanUp {
@Override
public void cleanUp(ClassLoaderLeakPreventor preventor) {
final Field registryField = preventor.findField(PropertyEditorManager.class, "registry");
if(registryField == null) { // We're probably on a newer JDK
preventor.info("Internal registry of " + PropertyEditorManager.class.getName() + " not found");
}
else {
try {
synchronized (PropertyEditorManager.class) {
final Map<Class<?>, Class<?>> registry = (Map<Class<?>, Class<?>>) registryField.get(null);
if(registry != null) { // Initialized
final Set<Class<?>> toRemove = new HashSet<Class<?>>();
for(Map.Entry<Class<?>, Class<?>> entry : registry.entrySet()) {
if(preventor.isLoadedByClassLoader(entry.getKey()) ||
preventor.isLoadedByClassLoader(entry.getValue())) { // More likely
toRemove.add(entry.getKey());
}
}
for(Class<?> clazz : toRemove) {
preventor.warn("Property editor for type " + clazz + " = " + registry.get(clazz) + " needs to be deregistered");
PropertyEditorManager.registerEditor(clazz, null); // Deregister
}
}
}
}
catch (Exception e) { // Such as IllegalAccessException
preventor.error(e);
}
}
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/ProxySelectorCleanUp.java
================================================
package se.jiderhamn.classloader.leak.prevention.cleanup;
import java.net.ProxySelector;
import java.security.AccessController;
import java.security.PrivilegedAction;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderPreMortemCleanUp;
/**
* If default {@link java.net.ProxySelector} is loaded by protected ClassLoader it needs to be unset
* @author Mattias Jiderhamn
*/
public class ProxySelectorCleanUp implements ClassLoaderPreMortemCleanUp {
@Override
public void cleanUp(final ClassLoaderLeakPreventor preventor) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run() {
ProxySelector selector = ProxySelector.getDefault();
if(preventor.isLoadedInClassLoader(selector)) {
ProxySelector.setDefault(null);
preventor.warn("Removing default java.net.ProxySelector: " + selector);
}
return null;
}
});
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/ReactorNettyHttpResourcesCleanUp.java
================================================
package se.jiderhamn.classloader.leak.prevention.cleanup;
import java.lang.reflect.Method;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderPreMortemCleanUp;
/**
* Clean up Reactor Netty resources
* @author Mattias Jiderhamn
*/
public class ReactorNettyHttpResourcesCleanUp implements ClassLoaderPreMortemCleanUp {
@Override
public void cleanUp(ClassLoaderLeakPreventor preventor) {
Class<?> clazz = preventor.findClass("reactor.ipc.netty.http.HttpResources");
if(preventor.isLoadedByClassLoader(clazz)) {
final Method shutdown = preventor.findMethod(clazz, "shutdown");
if(shutdown != null) {
try {
shutdown.invoke(null);
}
catch (Throwable e) {
preventor.warn(e);
}
}
}
clazz = preventor.findClass("reactor.netty.http.HttpResources");
if(preventor.isLoadedByClassLoader(clazz)) {
final Method shutdown = preventor.findMethod(clazz, "shutdown");
if(shutdown != null) {
try {
shutdown.invoke(null);
}
catch (Throwable e) {
preventor.warn(e);
}
}
}
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/ResourceBundleCleanUp.java
================================================
package se.jiderhamn.classloader.leak.prevention.cleanup;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.Map;
import java.util.ResourceBundle;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderPreMortemCleanUp;
/**
* Clean up caches in {@link ResourceBundle}
* @author Mattias Jiderhamn
*/
public class ResourceBundleCleanUp implements ClassLoaderPreMortemCleanUp {
@Override
public void cleanUp(ClassLoaderLeakPreventor preventor) {
try {
try { // First try Java 1.6 method
final Method clearCache16 = ResourceBundle.class.getMethod("clearCache", ClassLoader.class);
preventor.debug("Since Java 1.6+ is used, we can call " + clearCache16);
clearCache16.invoke(null, preventor.getClassLoader());
}
catch (NoSuchMethodException e) {
// Not Java 1.6+, we have to clear manually
final Map<?,?> cacheList = preventor.getStaticFieldValue(ResourceBundle.class, "cacheList"); // Java 5: SoftCache extends AbstractMap
final Iterator<?> iter = cacheList.keySet().iterator();
Field loaderRefField = null;
while(iter.hasNext()) {
Object key = iter.next(); // CacheKey
if(loaderRefField == null) { // First time
loaderRefField = key.getClass().getDeclaredField("loaderRef");
loaderRefField.setAccessible(true);
}
WeakReference<ClassLoader> loaderRef = (WeakReference<ClassLoader>) loaderRefField.get(key); // LoaderReference extends WeakReference
ClassLoader classLoader = loaderRef.get();
if(preventor.isClassLoaderOrChild(classLoader)) {
preventor.info("Removing ResourceBundle from cache: " + key);
iter.remove();
}
}
}
}
catch(Exception ex) {
preventor.error(ex);
}
// (CacheKey of java.util.ResourceBundle.NONEXISTENT_BUNDLE will point to first referring classloader...)
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/RmiTargetsCleanUp.java
================================================
package se.jiderhamn.classloader.leak.prevention.cleanup;
import java.lang.reflect.Field;
import java.util.Iterator;
import java.util.Map;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderPreMortemCleanUp;
/**
* Heavily inspired by org.apache.catalina.loader.WebappClassLoader.clearReferencesRmiTargets()
* @author Mattias Jiderhamn
*/
public class RmiTargetsCleanUp implements ClassLoaderPreMortemCleanUp {
@Override
public void cleanUp(ClassLoaderLeakPreventor preventor) {
try {
final Class<?> objectTableClass = preventor.findClass("sun.rmi.transport.ObjectTable");
if(objectTableClass != null) {
clearRmiTargetsMap(preventor, (Map<?, ?>) preventor.getStaticFieldValue(objectTableClass, "objTable"));
clearRmiTargetsMap(preventor, (Map<?, ?>) preventor.getStaticFieldValue(objectTableClass, "implTable"));
}
}
catch (Exception ex) {
preventor.error(ex);
}
}
/** Iterate RMI Targets Map and remove entries loaded by protected ClassLoader */
@SuppressWarnings("WeakerAccess")
protected void clearRmiTargetsMap(ClassLoaderLeakPreventor preventor, Map<?, ?> rmiTargetsMap) {
try {
final Field cclField = preventor.findFieldOfClass("sun.rmi.transport.Target", "ccl");
preventor.debug("Looping " + rmiTargetsMap.size() + " RMI Targets to find leaks");
for(Iterator<?> iter = rmiTargetsMap.values().iterator(); iter.hasNext(); ) {
Object target = iter.next(); // sun.rmi.transport.Target
ClassLoader ccl = (ClassLoader) cclField.get(target);
if(preventor.isClassLoaderOrChild(ccl)) {
preventor.warn("Removing RMI Target: " + target);
iter.remove();
}
}
}
catch (Exception ex) {
preventor.error(ex);
}
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/SAAJEnvelopeFactoryParserPoolCleanUp.java
================================================
package se.jiderhamn.classloader.leak.prevention.cleanup;
import java.lang.reflect.Field;
import java.util.Map;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderPreMortemCleanUp;
/**
* Clean up leak caused by {@link javax.xml.parsers.SAXParser} attribute/property being loaded by protected class loader
* and cached in {@link com.sun.xml.internal.messaging.saaj.soap.EnvelopeFactory#parserPool}.
* See <a href="https://issues.apache.org/jira/browse/XALANJ-2600">here</a>.
* @author Mattias Jiderhamn
*/
public class SAAJEnvelopeFactoryParserPoolCleanUp implements ClassLoaderPreMortemCleanUp {
@Override
public void cleanUp(ClassLoaderLeakPreventor preventor) {
// Internal class from the JDK (Removed in JDK11)
cleanupWithFactoryClass(preventor, preventor.findClass("com.sun.xml.internal.messaging.saaj.soap.EnvelopeFactory"));
// Maven dependency
cleanupWithFactoryClass(preventor, preventor.findClass("com.sun.xml.messaging.saaj.soap.EnvelopeFactory"));
}
private void cleanupWithFactoryClass(final ClassLoaderLeakPreventor preventor, Class<?> factoryClass) {
final Object parserPool = preventor.getStaticFieldValue(factoryClass, "parserPool");
if(parserPool != null) {
final Field CACHE = preventor.findField(parserPool.getClass().getSuperclass(), "CACHE");
if(CACHE != null) {
final Object cache = preventor.getFieldValue(CACHE, parserPool);
if(cache instanceof Map) { // WeakHashMap
((Map) cache).remove(preventor.getClassLoader());
}
}
}
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/SecurityProviderCleanUp.java
================================================
package se.jiderhamn.classloader.leak.prevention.cleanup;
import java.util.HashSet;
import java.util.Set;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderPreMortemCleanUp;
/**
* Deregister custom security providers
* @author Mattias Jiderhamn
*/
public class SecurityProviderCleanUp implements ClassLoaderPreMortemCleanUp {
@Override
public void cleanUp(ClassLoaderLeakPreventor preventor) {
final Set<String> providersToRemove = new HashSet<String>();
for(java.security.Provider provider : java.security.Security.getProviders()) {
if(preventor.isLoadedInClassLoader(provider)) {
providersToRemove.add(provider.getName());
}
}
if(! providersToRemove.isEmpty()) {
preventor.warn("Removing security providers loaded by protected ClassLoader: " + providersToRemove);
for(String providerName : providersToRemove) {
java.security.Security.removeProvider(providerName);
}
}
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/ShutdownHookCleanUp.java
================================================
package se.jiderhamn.classloader.leak.prevention.cleanup;
import java.util.ArrayList;
import java.util.Map;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderPreMortemCleanUp;
/**
* Find and deregister shutdown hooks. Will by default execute the hooks immediately after removing them.
* @author Mattias Jiderhamn
*/
public class ShutdownHookCleanUp implements ClassLoaderPreMortemCleanUp {
/** Default no of milliseconds to wait for shutdown hook to finish execution */
public static final int SHUTDOWN_HOOK_WAIT_MS_DEFAULT = 10 * 1000; // 10 seconds
/** Should shutdown hooks registered from the application be executed at application shutdown? */
@SuppressWarnings("WeakerAccess")
protected boolean executeShutdownHooks = true;
/**
* No of milliseconds to wait for shutdown hooks to finish execution, before stopping them.
* If set to -1 there will be no waiting at all, but Thread is allowed to run until finished.
*/
@SuppressWarnings("WeakerAccess")
protected int shutdownHookWaitMs = SHUTDOWN_HOOK_WAIT_MS_DEFAULT;
/** Constructor for test case */
@SuppressWarnings("unused")
public ShutdownHookCleanUp() {
this(true, SHUTDOWN_HOOK_WAIT_MS_DEFAULT);
}
public ShutdownHookCleanUp(boolean executeShutdownHooks, int shutdownHookWaitMs) {
this.executeShutdownHooks = executeShutdownHooks;
this.shutdownHookWaitMs = shutdownHookWaitMs;
}
public void setExecuteShutdownHooks(boolean executeShutdownHooks) {
this.executeShutdownHooks = executeShutdownHooks;
}
public void setShutdownHookWaitMs(int shutdownHookWaitMs) {
this.shutdownHookWaitMs = shutdownHookWaitMs;
}
@Override
public void cleanUp(ClassLoaderLeakPreventor preventor) {
// We will not remove known shutdown hooks, since loading the owning class of the hook,
// may register the hook if previously unregistered
final Map<Thread, Thread> shutdownHooks = preventor.getStaticFieldValue("java.lang.ApplicationShutdownHooks", "hooks");
if(shutdownHooks != null) { // Could be null during JVM shutdown, which we already avoid, but be extra precautious
// Iterate copy to avoid ConcurrentModificationException
for(Thread shutdownHook : new ArrayList<Thread>(shutdownHooks.keySet())) {
if(preventor.isThreadInClassLoader(shutdownHook)) { // Planned to run in protected ClassLoader
removeShutdownHook(preventor, shutdownHook);
}
}
}
}
/** Deregister shutdown hook and execute it immediately */
@SuppressWarnings({"deprecation", "WeakerAccess"})
protected void removeShutdownHook(ClassLoaderLeakPreventor preventor, Thread shutdownHook) {
final String displayString = "'" + shutdownHook + "' of type " + shutdownHook.getClass().getName();
preventor.error("Removing shutdown hook: " + displayString);
Runtime.getRuntime().removeShutdownHook(shutdownHook);
if(executeShutdownHooks) { // Shutdown hooks should be executed
preventor.info("Executing shutdown hook now: " + displayString);
// Make sure it's from protected ClassLoader
shutdownHook.start(); // Run cleanup immediately
if(shutdownHookWaitMs > 0) { // Wait for shutdown hook to finish
try {
shutdownHook.join(shutdownHookWaitMs); // Wait for thread to run
}
catch (InterruptedException e) {
// Do nothing
}
if(shutdownHook.isAlive()) {
preventor.warn(shutdownHook + "still running after " + shutdownHookWaitMs + " ms - Stopping!");
shutdownHook.stop();
}
}
}
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/StopThreadsCleanUp.java
================================================
package se.jiderhamn.classloader.leak.prevention.cleanup;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.security.AccessControlContext;
import java.util.List;
import java.util.concurrent.ThreadPoolExecutor;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderPreMortemCleanUp;
import static se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor.THREAD_WAIT_MS_DEFAULT;
/**
* Check if there are threads running within the protected {@link ClassLoader}, or otherwise referencing it,
* and either warn or stop those threads depending on settings.
* @author Mattias Jiderhamn
*/
@SuppressWarnings("WeakerAccess")
public class StopThreadsCleanUp implements ClassLoaderPreMortemCleanUp {
protected static final String JURT_ASYNCHRONOUS_FINALIZER = "com.sun.star.lib.util.AsynchronousFinalizer";
/** Thread {@link Runnable} for Sun/Oracle JRE i.e. java.lang.Thread.target */
private Field oracleTarget;
/** Thread {@link Runnable} for IBM JRE i.e. java.lang.Thread.runnable */
private Field ibmRunnable;
protected boolean stopThreads;
/**
* No of milliseconds to wait for threads to finish execution, before stopping them.
*/
protected int threadWaitMs = THREAD_WAIT_MS_DEFAULT;
/** Should Timer threads tied to the protected ClassLoader classloader be forced to stop at application shutdown? */
protected boolean stopTimerThreads;
/** Default constructor with {@link #stopThreads} = true and {@link #stopTimerThreads} = true */
@SuppressWarnings("unused")
public StopThreadsCleanUp() {
this(true, true);
}
public StopThreadsCleanUp(boolean stopThreads, boolean stopTimerThreads) {
this.stopThreads = stopThreads;
this.stopTimerThreads = stopTimerThreads;
}
public void setStopThreads(boolean stopThreads) {
this.stopThreads = stopThreads;
}
public void setStopTimerThreads(boolean stopTimerThreads) {
this.stopTimerThreads = stopTimerThreads;
}
public void setThreadWaitMs(int threadWaitMs) {
this.threadWaitMs = threadWaitMs;
}
@Override
public void cleanUp(ClassLoaderLeakPreventor preventor) {
// Force the execution of the cleanup code for JURT; see https://issues.apache.org/ooo/show_bug.cgi?id=122517
forceStartOpenOfficeJurtCleanup(preventor); // (Do this before stopThreads())
////////////////////
// Fix generic leaks
stopThreads(preventor);
}
/**
* The bug detailed at https://issues.apache.org/ooo/show_bug.cgi?id=122517 is quite tricky. This is a try to
* avoid the issues by force starting the threads and it's job queue.
*/
protected void forceStartOpenOfficeJurtCleanup(ClassLoaderLeakPreventor preventor) {
if(stopThreads) {
if(preventor.isLoadedByClassLoader(preventor.findClass(JURT_ASYNCHRONOUS_FINALIZER))) {
/*
The com.sun.star.lib.util.AsynchronousFinalizer class was found and loaded, which means that in case the
static block that starts the daemon thread had not been started yet, it has been started now.
Now let's force Garbage Collection, with the hopes of having the finalize()ers that put Jobs on the
AsynchronousFinalizer queue be executed. Then just leave it, and handle the rest in {@link #stopThreads}.
*/
preventor.info("OpenOffice JURT AsynchronousFinalizer thread started - forcing garbage collection to invoke finalizers");
ClassLoaderLeakPreventor.gc();
}
}
else {
// Check for class existence without loading class and thus executing static block
if(preventor.getClassLoader().getResource("com/sun/star/lib/util/AsynchronousFinalizer.class") != null) {
preventor.warn("OpenOffice JURT AsynchronousFinalizer thread will not be stopped if started, as stopThreads is false");
/*
By forcing Garbage Collection, we'll hopefully start the thread now, in case it would have been started by
GC later, so that at least it will appear in the logs.
*/
ClassLoaderLeakPreventor.gc();
}
}
}
/**
* Partially inspired by org.apache.catalina.loader.WebappClassLoader.clearReferencesThreads()
*/
protected void stopThreads(ClassLoaderLeakPreventor preventor) {
final Class<?> workerClass = preventor.findClass("java.util.concurrent.ThreadPoolExecutor$Worker");
final boolean waitForThreads = threadWaitMs > 0;
for(Thread thread : preventor.getAllThreads()) {
final Runnable runnable = getRunnable(preventor, thread);
final boolean threadLoadedByClassLoader = preventor.isLoadedInClassLoader(thread);
final boolean threadGroupLoadedByClassLoader = preventor.isLoadedInClassLoader(thread.getThreadGroup());
final boolean runnableLoadedByClassLoader = preventor.isLoadedInClassLoader(runnable);
final boolean hasContextClassLoader = preventor.isClassLoaderOrChild(thread.getContextClassLoader());
if(thread != Thread.currentThread() && // Ignore current thread
(threadLoadedByClassLoader || threadGroupLoadedByClassLoader || hasContextClassLoader || // = preventor.isThreadInClassLoader(thread)
runnableLoadedByClassLoader)) {
if (thread.getClass().getName().startsWith(StopThreadsCleanUp.JURT_ASYNCHRONOUS_FINALIZER)) {
// Note, the thread group of this thread may be "system" if it is triggered by the Garbage Collector
// however if triggered by us in forceStartOpenOfficeJurtCleanup() it may depend on the application server
if(stopThreads) {
preventor.info("Found JURT thread " + thread.getName() + "; starting " + JURTKiller.class.getSimpleName());
new JURTKiller(preventor, thread).start();
}
else
preventor.warn("JURT thread " + thread.getName() + " is still running in protected ClassLoader");
}
else if(thread.getThreadGroup() != null &&
("system".equals(thread.getThreadGroup().getName()) || // System thread
"RMI Runtime".equals(thread.getThreadGroup().getName()))) { // RMI thread (honestly, just copied from Tomcat)
if("Keep-Alive-Timer".equals(thread.getName())) {
thread.setContextClassLoader(preventor.getLeakSafeClassLoader());
preventor.debug("Changed contextClassLoader of HTTP keep alive thread");
}
}
else if(thread.isAlive()) { // Non-system, running in protected ClassLoader
if(thread.getClass().getName().startsWith("java.util.Timer")) { // Sun/Oracle = "java.util.TimerThread"; IBM = "java.util.Timer$TimerImpl"
if(thread.getName() != null && thread.getName().startsWith("PostgreSQL-JDBC-SharedTimer-")) { // Postgresql JDBC timer thread
// Replace contextClassLoader, if needed
if(hasContextClassLoader) {
final Class<?> postgresqlDriver = preventor.findClass("org.postgresql.Driver");
final ClassLoader postgresqlCL = (postgresqlDriver != null && ! preventor.isLoadedByClassLoader(postgresqlDriver)) ?
postgresqlDriver.getClassLoader() : // Postgresql driver loaded by other classloader than we want to protect
preventor.getLeakSafeClassLoader();
thread.setContextClassLoader(postgresqlCL);
preventor.warn("Changing contextClassLoader of " + thread + " to " + postgresqlCL);
}
// Replace AccessControlContext
setThreadSafeAccessControlContext(preventor, thread);
}
else if(stopTimerThreads) {
preventor.warn("Stopping Timer thread '" + thread.getName() + "' running in protected ClassLoader. " +
preventor.getStackTrace(thread));
stopTimerThread(preventor, thread);
}
else {
preventor.info("Timer thread is running in protected ClassLoader, but will not be stopped. " +
preventor.getStackTrace(thread));
}
}
else {
final String displayString = "Thread '" + thread + "'" +
(threadLoadedByClassLoader ? " of type " + thread.getClass().getName() + " loaded by protected ClassLoader" : "") +
(runnableLoadedByClassLoader ? " with Runnable of type " + runnable.getClass().getName() + " loaded by protected ClassLoader" : "") +
(threadGroupLoadedByClassLoader ? " with ThreadGroup of type " + thread.getThreadGroup().getClass().getName() + " loaded by protected ClassLoader" : "") +
(hasContextClassLoader ? " with contextClassLoader = protected ClassLoader or child" : "");
// If threads is running an java.util.concurrent.ThreadPoolExecutor.Worker try shutting down the executor
if(workerClass != null && workerClass.isInstance(runnable)) {
try {
// java.util.concurrent.ThreadPoolExecutor, introduced in Java 1.5
final Field workerExecutor = preventor.findField(workerClass, "this$0");
final ThreadPoolExecutor executor = preventor.getFieldValue(workerExecutor, runnable);
if(executor != null) {
if("org.apache.tomcat.util.threads.ThreadPoolExecutor".equals(executor.getClass().getName())) {
// Tomcat pooled thread
preventor.debug(displayString + " is worker of " + executor.getClass().getName());
}
else if(preventor.isLoadedInClassLoader(executor) || preventor.isLoadedInClassLoader(executor.getThreadFactory())) {
if(stopThreads) {
preventor.warn("Shutting down ThreadPoolExecutor of type " + executor.getClass().getName());
executor.shutdownNow();
}
else {
preventor.warn("ThreadPoolExecutor of type " + executor.getClass().getName() +
" should be shut down.");
}
}
else {
preventor.info(displayString + " is a ThreadPoolExecutor.Worker of " + executor.getClass().getName() +
" but found no reason to shut down ThreadPoolExecutor.");
}
}
}
catch (Exception ex) {
preventor.error(ex);
}
}
if(! threadLoadedByClassLoader && ! runnableLoadedByClassLoader && ! threadGroupLoadedByClassLoader) { // Not loaded in protected ClassLoader - just running there
// This would for example be the case with org.apache.tomcat.util.threads.TaskThread
if(waitForThreads) {
preventor.warn(displayString + "; waiting " + threadWaitMs +
" ms. " + preventor.getStackTrace(thread));
preventor.waitForThread(thread, threadWaitMs, false /* No interrupt */);
}
if(thread.isAlive() && preventor.isClassLoaderOrChild(thread.getContextClassLoader())) { // Still running in ClassLoader
preventor.warn(displayString + (waitForThreads ? " still" : "") +
" alive; changing context ClassLoader to leak safe (" +
preventor.getLeakSafeClassLoader() + "). " + preventor.getStackTrace(thread));
thread.setContextClassLoader(preventor.getLeakSafeClassLoader());
// Replace AccessControlContext since we already replaced ClassLoader,
// for test/use cease @see StopThreadsClenup_ExecutorTest
setThreadSafeAccessControlContext(preventor, thread);
}
}
else if(stopThreads) { // Thread/Runnable/ThreadGroup loaded by protected ClassLoader
if(waitForThreads) {
preventor.warn("Waiting for " + displayString + " for " + threadWaitMs + " ms. " +
preventor.getStackTrace(thread));
preventor.waitForThread(thread, threadWaitMs, true /* Interrupt if needed */);
}
// Normally threads should not be stopped (method is deprecated), since it may cause an inconsistent state.
// In this case however, the alternative is a classloader leak, which may or may not be considered worse.
if(thread.isAlive()) {
preventor.warn("Stopping " + displayString + ". " + preventor.getStackTrace(thread));
//noinspection deprecation
thread.stop();
}
else {
preventor.info(displayString + " no longer alive - no action needed.");
}
}
else {
preventor.warn(displayString + " would cause leak. " + preventor.getStackTrace(thread));
}
}
}
}
}
}
/**
* Replace Thread AccessControlContext to allow for Protection Domain GC
*/
private void setThreadSafeAccessControlContext(ClassLoaderLeakPreventor preventor, Thread thread) {
// Replace AccessControlContext
final Field inheritedAccessControlContext = preventor.findField(Thread.class, "inheritedAccessControlContext");
if(inheritedAccessControlContext != null) {
try {
final AccessControlContext acc = preventor.createAccessControlContext();
inheritedAccessControlContext.set(thread, acc);
preventor.removeDomainCombiner("thread " + thread, acc);
}
catch (Exception e) {
preventor.error(e);
}
}
}
/** Get {@link Runnable} of given thread, if any */
private Runnable getRunnable(ClassLoaderLeakPreventor preventor, Thread thread) {
if(oracleTarget == null && ibmRunnable == null) { // Not yet initialized
oracleTarget = preventor.findField(Thread.class, "target"); // Sun/Oracle JRE
ibmRunnable = preventor.findField(Thread.class, "runnable"); // IBM JRE
}
return (oracleTarget != null) ? (Runnable) preventor.getFieldValue(oracleTarget, thread) : // Sun/Oracle JRE
(Runnable) preventor.getFieldValue(ibmRunnable, thread); // IBM JRE
}
protected void stopTimerThread(ClassLoaderLeakPreventor preventor, Thread thread) {
// Seems it is not possible to access Timer of TimerThread, so we need to mimic Timer.cancel()
/**
try {
Timer timer = (Timer) findField(thread.getClass(), "this$0").get(thread); // This does not work!
warn("Cancelling Timer " + timer + " / TimeThread '" + thread + "'");
timer.cancel();
}
catch (IllegalAccessException iaex) {
error(iaex);
}
*/
try {
final Field newTasksMayBeScheduled = preventor.findField(thread.getClass(), "newTasksMayBeScheduled");
final Object queue = preventor.findField(thread.getClass(), "queue").get(thread); // java.lang.TaskQueue
final Method clear = preventor.findMethod(queue.getClass(), "clear");
// Do what java.util.Timer.cancel() does
//noinspection SynchronizationOnLocalVariableOrMethodParameter
synchronized (queue) {
newTasksMayBeScheduled.set(thread, Boolean.FALSE);
clear.invoke(queue);
queue.notify(); // "In case queue was already empty."
}
// We shouldn't need to join() here, thread will finish soon enough
}
catch (Exception ex) {
preventor.error(ex);
}
}
/**
* Inner class with the sole task of killing JURT finalizer thread after it is done processing jobs.
* We need to postpone the stopping of this thread, since more Jobs may in theory be add()ed when the protected
* ClassLoader is closing down and being garbage collected.
* See https://issues.apache.org/ooo/show_bug.cgi?id=122517
*/
protected class JURTKiller extends Thread {
private final ClassLoaderLeakPreventor preventor;
private final Thread jurtThread;
private final List<?> jurtQueue;
public JURTKiller(ClassLoaderLeakPreventor preventor, Thread jurtThread) {
super("JURTKiller");
this.preventor = preventor;
this.jurtThread = jurtThread;
jurtQueue = preventor.getStaticFieldValue(StopThreadsCleanUp.JURT_ASYNCHRONOUS_FINALIZER, "queue");
// Make sure all classes are loaded from the current app classloader before it executes,
// as it may use them *after* the classloader has been "shutdown" by the container (if any).
State state = State.RUNNABLE;
}
@Override
public void run() {
try {
if(jurtQueue == null || jurtThread == null) {
preventor.error(getName() + ": No queue or thread!?");
return;
}
if(! jurtThread.isAlive()) {
preventor.warn(getName() + ": " + jurtThread.getName() + " is already dead?");
}
boolean queueIsEmpty = false;
while(! queueIsEmpty) {
try {
preventor.debug(getName() + " goes to sleep for " + ClassLoaderLeakPreventor.THREAD_WAIT_MS_DEFAULT + " ms");
Thread.sleep(ClassLoaderLeakPreventor.THREAD_WAIT_MS_DEFAULT);
}
catch (InterruptedException e) {
// Do nothing
}
if(State.RUNNABLE != jurtThread.getState()) { // Unless thread is currently executing a Job
preventor.debug(getName() + " about to force Garbage Collection");
ClassLoaderLeakPreventor.gc(); // Force garbage collection, which may put new items on queue
synchronized (jurtQueue) {
queueIsEmpty = jurtQueue.isEmpty();
preventor.debug(getName() + ": JURT queue is empty? " + queueIsEmpty);
}
}
else
preventor.debug(getName() + ": JURT thread " + jurtThread.getName() + " is executing Job");
}
preventor.info(getName() + " about to kill " + jurtThread);
if(jurtThread.isAlive()) {
//noinspection deprecation
jurtThread.stop();
}
}
catch (Throwable t) {
preventor.error(t);
}
}
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/ThreadGroupCleanUp.java
================================================
package se.jiderhamn.classloader.leak.prevention.cleanup;
import java.lang.reflect.Method;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderPreMortemCleanUp;
import se.jiderhamn.classloader.leak.prevention.MustBeAfter;
/**
* Destroy any {@link ThreadGroup}s that are loaded by the protected classloader
* @author Mattias Jiderhamn
*/
public class ThreadGroupCleanUp implements ClassLoaderPreMortemCleanUp, MustBeAfter {
@Override
public Class[] mustBeBeforeMe() {
return new Class[] {JavaServerFaces2746CleanUp.class};
}
@Override
public void cleanUp(ClassLoaderLeakPreventor preventor) {
boolean threadGroupDestroyed = false;
try {
ThreadGroup systemThreadGroup = Thread.currentThread().getThreadGroup();
while(systemThreadGroup.getParent() != null) {
systemThreadGroup = systemThreadGroup.getParent();
}
// systemThreadGroup should now be the topmost ThreadGroup, "system"
int enumeratedGroups;
ThreadGroup[] allThreadGroups;
int noOfGroups = systemThreadGroup.activeGroupCount(); // Estimate no of groups
do {
noOfGroups += 10; // Make room for 10 extra
allThreadGroups = new ThreadGroup[noOfGroups];
enumeratedGroups = systemThreadGroup.enumerate(allThreadGroups);
} while(enumeratedGroups >= noOfGroups); // If there was not room for all groups, try again
for(ThreadGroup threadGroup : allThreadGroups) {
if(preventor.isLoadedInClassLoader(threadGroup) && ! threadGroup.isDestroyed()) {
preventor.warn("ThreadGroup '" + threadGroup + "' was loaded inside application, needs to be destroyed");
int noOfThreads = threadGroup.activeCount();
if(noOfThreads > 0) {
preventor.warn("There seems to be " + noOfThreads + " running in ThreadGroup '" + threadGroup + "'; interrupting");
try {
threadGroup.interrupt();
}
catch (Exception e) {
preventor.error(e);
}
}
try {
threadGroup.destroy();
threadGroupDestroyed = true;
preventor.info("ThreadGroup '" + threadGroup + "' successfully destroyed");
}
catch (Exception e) {
preventor.error(e);
}
}
}
}
catch (Exception ex) {
preventor.error(ex);
}
try {
final Object contexts = preventor.getStaticFieldValue("java.beans.ThreadGroupContext", "contexts");
if(contexts != null) { // Since Java 1.7
if(threadGroupDestroyed) // At least one ThreadGroup destroyed by this clean up
ClassLoaderLeakPreventor.gc(); // Force GC so WeakIdentityMap turns destroyed ThreadGroups into stale entries
final Method removeStaleEntries = preventor.findMethod("java.beans.WeakIdentityMap", "removeStaleEntries");
if(removeStaleEntries != null)
removeStaleEntries.invoke(contexts);
}
}
catch (Throwable t) { // IllegalAccessException, InvocationTargetException
preventor.warn(t);
}
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/ThreadGroupContextCleanUp.java
================================================
package se.jiderhamn.classloader.leak.prevention.cleanup;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderPreMortemCleanUp;
/**
* Clean all {@link java.beans.ThreadGroupContext#beanInfoCache}s in {@link java.beans.ThreadGroupContext#contexts}
* since they may contain beans/properties loaded in the protected classloader.
* @author Mattias Jiderhamn
*/
public class ThreadGroupContextCleanUp implements ClassLoaderPreMortemCleanUp {
@Override
public void cleanUp(ClassLoaderLeakPreventor preventor) {
final Object /*WeakIdentityMap<ThreadGroupContext>*/ contexts = preventor.getStaticFieldValue("java.beans.ThreadGroupContext", "contexts");
if(contexts != null) { // Since Java 1.7
// final WeakReference/*java.beans.WeakIdentityMap.Entry*/[] table = preventor.getFieldValue(contexts, "table");
final Field tableField = preventor.findField(preventor.findClass("java.beans.WeakIdentityMap"), "table");
if(tableField != null) {
final WeakReference/*java.beans.WeakIdentityMap.Entry*/[] table = preventor.getFieldValue(tableField, contexts);
if(table != null) {
Method clearBeanInfoCache = null;
for(WeakReference entry : table) {
if(entry != null) {
Object /*ThreadGroupContext*/ context = preventor.getFieldValue(entry, "value");
if(context != null) {
if(clearBeanInfoCache == null) { // FirstThreadGroupContext
clearBeanInfoCache = preventor.findMethod(context.getClass(), "clearBeanInfoCache");
}
try {
clearBeanInfoCache.invoke(context);
}
catch (Exception e) {
preventor.error(e);
}
}
}
}
}
}
}
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/ThreadLocalCleanUp.java
================================================
package se.jiderhamn.classloader.leak.prevention.cleanup;
import java.lang.ref.Reference;
import java.lang.reflect.Field;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderPreMortemCleanUp;
import se.jiderhamn.classloader.leak.prevention.MustBeAfter;
/**
* Clear {@link ThreadLocal}s for which {@link ThreadLocal#remove()} has not been called, in case either the
* {@link ThreadLocal} is a custom one (subclassed in the protected ClassLoader), or the value is loaded by (or is)
* the protected ClassLoader.
* This must be done after threads have been stopped, or new ThreadLocals may be added by those threads.
* @author Mattias Jiderhamn
*/
@SuppressWarnings("WeakerAccess")
public class ThreadLocalCleanUp implements ClassLoaderPreMortemCleanUp, MustBeAfter<ClassLoaderPreMortemCleanUp> {
/** Class name for per thread transaction in Caucho Resin transaction manager */
private static final String CAUCHO_TRANSACTION_IMPL = "com.caucho.transaction.TransactionImpl";
protected Field java_lang_Thread_threadLocals;
protected Field java_lang_Thread_inheritableThreadLocals;
protected Field java_lang_ThreadLocal$ThreadLocalMap_table;
protected Field java_lang_ThreadLocal$ThreadLocalMap$Entry_value;
/** Needs to be done after {@link StopThreadsCleanUp}, since new {@link ThreadLocal}s may be added when threads are
* shutting down. */
@Override
public Class<? extends ClassLoaderPreMortemCleanUp>[] mustBeBeforeMe() {
return new Class[] {StopThreadsCleanUp.class};
}
@Override
public void cleanUp(ClassLoaderLeakPreventor preventor) {
initFields(preventor); // Initialize some reflection variables
if(java_lang_Thread_threadLocals == null)
preventor.error("java.lang.Thread.threadLocals not found; something is seriously wrong!");
if(java_lang_Thread_inheritableThreadLocals == null)
preventor.error("java.lang.Thread.inheritableThreadLocals not found; something is seriously wrong!");
if(java_lang_ThreadLocal$ThreadLocalMap_table == null)
preventor.error("java.lang.ThreadLocal$ThreadLocalMap.table not found; something is seriously wrong!");
for(Thread thread : preventor.getAllThreads()) {
forEachThreadLocalInThread(preventor, thread);
}
}
/** Make sure fields are initialized */
private void initFields(ClassLoaderLeakPreventor preventor) {
if(java_lang_Thread_threadLocals == null) { // First invokation of this preventor
java_lang_Thread_threadLocals = preventor.findField(Thread.class, "threadLocals");
java_lang_Thread_inheritableThreadLocals = preventor.findField(Thread.class, "inheritableThreadLocals");
java_lang_ThreadLocal$ThreadLocalMap_table = preventor.findFieldOfClass("java.lang.ThreadLocal$ThreadLocalMap", "table");
}
}
protected void forEachThreadLocalInThread(ClassLoaderLeakPreventor preventor, Thread thread) {
try {
if(java_lang_Thread_threadLocals != null) {
processThreadLocalMap(preventor, thread, java_lang_Thread_threadLocals.get(thread));
}
if(java_lang_Thread_inheritableThreadLocals != null) {
processThreadLocalMap(preventor, thread, java_lang_Thread_inheritableThreadLocals.get(thread));
}
}
catch (/*IllegalAccess*/Exception ex) {
preventor.error(ex);
}
}
protected void processThreadLocalMap(ClassLoaderLeakPreventor preventor,
Thread thread, Object threadLocalMap) throws IllegalAccessException {
if(threadLocalMap != null && java_lang_ThreadLocal$ThreadLocalMap_table != null) {
Field resin_suspendState = null;
Field resin_isSuspended = null;
final Object[] threadLocalMapTable = (Object[]) java_lang_ThreadLocal$ThreadLocalMap_table.get(threadLocalMap); // java.lang.ThreadLocal.ThreadLocalMap.Entry[]
for(Object entry : threadLocalMapTable) {
if(entry != null) {
// Key is kept in WeakReference
Reference<?> reference = (Reference<?>) entry;
final ThreadLocal<?> threadLocal = (ThreadLocal<?>) reference.get();
if(java_lang_ThreadLocal$ThreadLocalMap$Entry_value == null) {
java_lang_ThreadLocal$ThreadLocalMap$Entry_value = preventor.findField(entry.getClass(), "value");
}
// Dereference the value if this is a Reference<T>: all Reference<T> implementations are all loaded using the bootstrap classloader,
// so checking the Reference<T> classloader won't indicate if the held value was itself loaded using the app classloader
// We could have called Reference.clear() directly, which would have fixed the leak even when not allowed to modify the ThreadLocalMap.Entry
final Object value = dereferenceIfApplicable(java_lang_ThreadLocal$ThreadLocalMap$Entry_value.get(entry));
// Workaround for http://bugs.caucho.com/view.php?id=5647
if(value != null && CAUCHO_TRANSACTION_IMPL.equals(value.getClass().getName())) { // Resin transaction
if(resin_suspendState == null && resin_isSuspended == null) { // First thread with Resin transaction, look up fields
resin_suspendState = preventor.findField(value.getClass(), "_suspendState");
resin_isSuspended = preventor.findField(value.getClass(), "_isSuspended");
}
if(resin_suspendState != null && resin_isSuspended != null) { // Both fields exist (as per version 4.0.37)
if(preventor.getFieldValue(resin_suspendState, value) != null) { // There is a suspended state that may cause leaks
// In theory a new transaction can be started and suspended between where we read and write the state,
// and flag, therefore we suspend the thread meanwhile.
try {
//noinspection deprecation
thread.suspend(); // Suspend the thread
if(preventor.getFieldValue(resin_suspendState, value) != null) { // Re-read suspend state when thread is suspended
final Object isSuspended = preventor.getFieldValue(resin_isSuspended, value);
if(!(isSuspended instanceof Boolean)) {
preventor.error(thread.toString() + " has " + CAUCHO_TRANSACTION_IMPL + " but _isSuspended is not boolean: " + isSuspended);
}
else if((Boolean) isSuspended) { // Is currently suspended - suspend state is correct
preventor.debug(thread.toString() + " has " + CAUCHO_TRANSACTION_IMPL + " that is suspended");
}
else { // Is not suspended, and thus should not have suspend state
resin_suspendState.set(value, null);
preventor.error(thread.toString() + " had " + CAUCHO_TRANSACTION_IMPL + " with unused _suspendState that was removed");
}
}
}
catch (Throwable t) { // Such as SecurityException
preventor.error(t);
}
finally {
//noinspection deprecation
thread.resume();
}
}
}
}
final boolean customThreadLocal = preventor.isLoadedInClassLoader(threadLocal); // This is not an actual problem
final boolean valueLoadedInWebApp = preventor.isLoadedInClassLoader(value);
if(customThreadLocal || valueLoadedInWebApp ||
(value instanceof ClassLoader && preventor.isClassLoaderOrChild((ClassLoader) value))) { // The value is classloader (child) itself
// This ThreadLocal is either itself loaded by the web app classloader, or it's value is
// Let's do something about it
StringBuilder message = new StringBuilder();
if(threadLocal != null) {
if(customThreadLocal) {
message.append("Custom ");
}
message.append("ThreadLocal of type ").append(threadLocal.getClass().getName()).append(": ").append(threadLocal);
}
else {
message.append("Unknown ThreadLocal");
}
message.append(" with value ").append(value);
if(value != null) {
message.append(" of type ").append(value.getClass().getName());
if(valueLoadedInWebApp)
message.append(" that is loaded by web app");
}
// Process the detected potential leak
processLeak(preventor, thread, reference, threadLocal, value, message.toString());
}
}
}
}
}
protected Object dereferenceIfApplicable(Object value) {
return value instanceof Reference ? dereferenceIfApplicable(((Reference<?>) value).get()) : value;
}
/**
* After having detected potential ThreadLocal leak, this method is called.
* Default implementation tries to clear the entry to avoid a leak.
*/
protected void processLeak(ClassLoaderLeakPreventor preventor, Thread thread, Reference<?> entry,
ThreadLocal<?> threadLocal, Object value, String message) {
if(threadLocal != null && thread == Thread.currentThread()) { // If running for current thread and we have the ThreadLocal ...
// ... remove properly
preventor.info(message + " will be remove()d from " + thread);
threadLocal.remove();
}
else { // We cannot remove entry properly, so just make it stale
preventor.info(message + " will be made stale for later expunging from " + thread);
}
// It seems like remove() doesn't really do the job, so play it safe and remove references from entry either way
// (Example problem org.infinispan.context.SingleKeyNonTxInvocationContext)
entry.clear(); // Clear the key
if(java_lang_ThreadLocal$ThreadLocalMap$Entry_value == null) {
java_lang_ThreadLocal$ThreadLocalMap$Entry_value = preventor.findField(entry.getClass(), "value");
}
try {
java_lang_ThreadLocal$ThreadLocalMap$Entry_value.set(entry, null); // Clear value to avoid circular references
}
catch (IllegalAccessException iaex) {
preventor.error(iaex);
}
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/WarningThreadLocalCleanUp.java
================================================
package se.jiderhamn.classloader.leak.prevention.cleanup;
import java.lang.ref.Reference;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderPreMortemCleanUp;
/**
* {@link ClassLoaderPreMortemCleanUp} that does not clear {@link ThreadLocal}s to remove the leak, but only logs a
* warning
* @author Mattias Jiderhamn
*/
@SuppressWarnings("unused")
public class WarningThreadLocalCleanUp extends ThreadLocalCleanUp {
/**
* Log not {@link ThreadLocal#remove()}ed leak as a warning.
*/
protected void processLeak(ClassLoaderLeakPreventor preventor, Thread thread, Reference<?> entry,
ThreadLocal<?> threadLocal, Object value, String message) {
preventor.warn(message);
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/X509TrustManagerImplUnparseableExtensionCleanUp.java
================================================
package se.jiderhamn.classloader.leak.prevention.cleanup;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.security.cert.X509Certificate;
import java.util.Map;
import javax.net.ssl.SSLContextSpi;
import javax.net.ssl.X509TrustManager;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderPreMortemCleanUp;
/**
* {@link sun.security.ssl.X509TrustManagerImpl} keeps a list set of trusted certs, which may include
* {@link sun.security.x509.UnparseableExtension} that in turn may include an {@link Exception} with a backtrace
* with references to the classloader that we want to protect
* @author Mattias Jiderhamn
*/
public class X509TrustManagerImplUnparseableExtensionCleanUp implements ClassLoaderPreMortemCleanUp {
private static final String SUN_SECURITY_X509_X509_CERT_IMPL = "sun.security.x509.X509CertImpl";
@Override
public void cleanUp(ClassLoaderLeakPreventor preventor) {
final SSLContextSpi sslContext = preventor.getStaticFieldValue("sun.security.ssl.SSLContextImpl$DefaultSSLContext", "defaultImpl");
if(sslContext != null) {
final Field trustManagerField = preventor.findFieldOfClass("sun.security.ssl.SSLContextImpl", "trustManager");
final Method get = preventor.findMethod(SUN_SECURITY_X509_X509_CERT_IMPL, "get", String.class);
final Method getUnparseableExtensions = preventor.findMethod("sun.security.x509.CertificateExtensions", "getUnparseableExtensions");
final Field why = preventor.findFieldOfClass("sun.security.x509.UnparseableExtension", "why");
if(trustManagerField != null && get != null && getUnparseableExtensions != null && why != null) {
final X509TrustManager/*Impl*/ trustManager = preventor.getFieldValue(trustManagerField, sslContext);
for(X509Certificate x509Certificate : trustManager.getAcceptedIssuers()) {
if(SUN_SECURITY_X509_X509_CERT_IMPL.equals(x509Certificate.getClass().getName())) {
try {
final /* sun.security.x509.CertificateExtensions*/ Object extensions = get.invoke(x509Certificate, "x509.info.extensions");
if(extensions != null) {
Map/*<String, sun.security.x509.Extension>*/ unparseableExtensions = (Map) getUnparseableExtensions.invoke(extensions);
for(Object unparseableExtension : unparseableExtensions.values()) {
if(why.get(unparseableExtension) != null) {
preventor.warn(trustManager + " cached X509Certificate that had unparseable extension; removing 'why': " +
x509Certificate);
why.set(unparseableExtension, null);
}
}
}
}
catch (Exception e) {
preventor.error(e);
}
}
}
}
}
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/preinit/AwtToolkitInitiator.java
================================================
package se.jiderhamn.classloader.leak.prevention.preinit;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
import se.jiderhamn.classloader.leak.prevention.PreClassLoaderInitiator;
/**
* The first call to java.awt.Toolkit.getDefaultToolkit() will spawn a new thread with the
* same contextClassLoader as the caller.
*
* See http://java.jiderhamn.se/2012/02/26/classloader-leaks-v-common-mistakes-and-known-offenders/
* @author Mattias Jiderhamn
*/
public class AwtToolkitInitiator implements PreClassLoaderInitiator {
@Override
public void doOutsideClassLoader(ClassLoaderLeakPreventor preventor) {
try {
java.awt.Toolkit.getDefaultToolkit(); // Will start a Thread
}
catch (Throwable t) {
preventor.error(t);
preventor.warn("Consider adding -Djava.awt.headless=true to your JVM parameters");
}
}
}
================================================
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/preinit/DatatypeConverterImplInitiator.java
================================================
package se.jiderhamn.classloader.leak.prevention.preinit;
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
import se.jiderhamn.classloader.leak.prevention.PreClassLoaderInitiator;
/**
* {@link javax.xml.bind.DatatypeConverterImpl} in the JAXB Reference Implementation shipped with JDK 1.6+ will
* keep a static reference ({@link javax.xml.bind.DatatypeConverterImpl#datatypeFactory}) to a concrete subclass of
* {@link javax.xml.datatype.DatatypeFactory}, that is resolved when the class is loaded (which I believe happens if you
* have custom bindings that reference the static methods in {@link javax.xml.bind.DatatypeConverter}). It seems that if
* for example you have a version of Xerces inside your application, the factory method may resolve {@code
* org.apache.xerces.jaxp.datatype.DatatypeFactoryImpl} as the implementation to use (rather than
* {@code com.sun.org.apache.xerces.internal.jaxp.datatype.DatatypeFactoryImpl} shipped with the JDK), which
* means there will a reference from {@link javax.xml.bind.DatatypeConverterImpl} to your classloader.
*
* See http://java.jiderhamn.se/2012/02/26/classloader
gitextract_ovazmkyr/ ├── .gitignore ├── .mvn/ │ └── wrapper/ │ ├── MavenWrapperDownloader.java │ └── maven-wrapper.properties ├── .travis.yml ├── LICENSE.txt ├── README.md ├── classloader-leak-prevention/ │ ├── classloader-leak-prevention-core/ │ │ ├── README.md │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ └── java/ │ │ │ └── se/ │ │ │ └── jiderhamn/ │ │ │ └── classloader/ │ │ │ └── leak/ │ │ │ └── prevention/ │ │ │ ├── ClassLoaderLeakPreventor.java │ │ │ ├── ClassLoaderLeakPreventorFactory.java │ │ │ ├── ClassLoaderPreMortemCleanUp.java │ │ │ ├── JULLogger.java │ │ │ ├── Logger.java │ │ │ ├── MustBeAfter.java │ │ │ ├── PreClassLoaderInitiator.java │ │ │ ├── ReplaceDOMNormalizerSerializerAbortException.java │ │ │ ├── StdLogger.java │ │ │ ├── cleanup/ │ │ │ │ ├── ApacheCommonsLoggingCleanUp.java │ │ │ │ ├── BeanELResolverCleanUp.java │ │ │ │ ├── BeanIntrospectorCleanUp.java │ │ │ │ ├── BeanValidationCleanUp.java │ │ │ │ ├── DefaultAuthenticatorCleanUp.java │ │ │ │ ├── DriverManagerCleanUp.java │ │ │ │ ├── GeoToolsCleanUp.java │ │ │ │ ├── IIOServiceProviderCleanUp.java │ │ │ │ ├── IntrospectionUtilsCleanUp.java │ │ │ │ ├── JDK8151486CleanUp.java │ │ │ │ ├── JacksonCleanUp.java │ │ │ │ ├── JavaServerFaces2746CleanUp.java │ │ │ │ ├── JavaUtilLoggingLevelCleanUp.java │ │ │ │ ├── JavaxSecurityAuthLoginConfigurationCleanUp.java │ │ │ │ ├── JceSecurityCleanUp.java │ │ │ │ ├── KeepAliveTimerCacheCleanUp.java │ │ │ │ ├── MBeanCleanUp.java │ │ │ │ ├── MXBeanNotificationListenersCleanUp.java │ │ │ │ ├── MoxyCleanUp.java │ │ │ │ ├── MultiThreadedHttpConnectionManagerCleanUp.java │ │ │ │ ├── ObjectStreamClassCleanup.java │ │ │ │ ├── PropertyEditorCleanUp.java │ │ │ │ ├── ProxySelectorCleanUp.java │ │ │ │ ├── ReactorNettyHttpResourcesCleanUp.java │ │ │ │ ├── ResourceBundleCleanUp.java │ │ │ │ ├── RmiTargetsCleanUp.java │ │ │ │ ├── SAAJEnvelopeFactoryParserPoolCleanUp.java │ │ │ │ ├── SecurityProviderCleanUp.java │ │ │ │ ├── ShutdownHookCleanUp.java │ │ │ │ ├── StopThreadsCleanUp.java │ │ │ │ ├── ThreadGroupCleanUp.java │ │ │ │ ├── ThreadGroupContextCleanUp.java │ │ │ │ ├── ThreadLocalCleanUp.java │ │ │ │ ├── WarningThreadLocalCleanUp.java │ │ │ │ └── X509TrustManagerImplUnparseableExtensionCleanUp.java │ │ │ └── preinit/ │ │ │ ├── AwtToolkitInitiator.java │ │ │ ├── DatatypeConverterImplInitiator.java │ │ │ ├── DocumentBuilderFactoryInitiator.java │ │ │ ├── JarUrlConnectionInitiator.java │ │ │ ├── Java2dDisposerInitiator.java │ │ │ ├── Java2dRenderQueueInitiator.java │ │ │ ├── JavaxSecurityLoginConfigurationInitiator.java │ │ │ ├── JdbcDriversInitiator.java │ │ │ ├── LdapPoolManagerInitiator.java │ │ │ ├── OracleJdbcThreadInitiator.java │ │ │ ├── SecurityPolicyInitiator.java │ │ │ ├── SecurityProvidersInitiator.java │ │ │ ├── SunAwtAppContextInitiator.java │ │ │ └── SunGCInitiator.java │ │ └── test/ │ │ ├── java/ │ │ │ └── se/ │ │ │ └── jiderhamn/ │ │ │ └── classloader/ │ │ │ └── leak/ │ │ │ └── prevention/ │ │ │ ├── ClassLoaderLeakPreventorFactoryTest.java │ │ │ ├── PreventionsTestBase.java │ │ │ ├── StopThreadsCleanUp_TimerTest.java │ │ │ ├── cleanup/ │ │ │ │ ├── BeanELResolverCleanUpTest.java │ │ │ │ ├── BeanIntrospectorCleanUpTest.java │ │ │ │ ├── BeanValidationCleanUpTest.java │ │ │ │ ├── ClassLoaderPreMortemCleanUpTestBase.java │ │ │ │ ├── DefaultAuthenticatorCleanUpTest.java │ │ │ │ ├── DriverManagerCleanUpTest.java │ │ │ │ ├── GeoToolsCleanUpTest.java │ │ │ │ ├── IIOServiceProviderCleanUpTest.java │ │ │ │ ├── ImageIOMockImageInputStreamSPI.java │ │ │ │ ├── JDK8151486CleanUpTest.java │ │ │ │ ├── JacksonCleanUpTest.java │ │ │ │ ├── JavaServerFaces2746CleanUpTest.java │ │ │ │ ├── JavaUtilLoggingLevelCleanUpTest.java │ │ │ │ ├── JavaxSecurityAuthLoginConfigurationCleanUpTest.java │ │ │ │ ├── JceSecurityCleanUpTest.java │ │ │ │ ├── MBeanCleanUpTest.java │ │ │ │ ├── MXBeanNotificationListenersCleanUpTest.java │ │ │ │ ├── MXBeanNotificationListenersCleanUp_ListenerWrapperTest.java │ │ │ │ ├── MoxyCleanUpTest.java │ │ │ │ ├── MultiThreadedHttpConnectionManagerCleanUpTest.java │ │ │ │ ├── ObjectStreamClassCleanupTest.java │ │ │ │ ├── PropertyEditorCleanUpTest.java │ │ │ │ ├── ProxySelectorCleanUpTest.java │ │ │ │ ├── ReplaceDOMNormalizerSerializerAbortExceptionCleanUpTest.java │ │ │ │ ├── SAAJEnvelopeFactoryParserPoolCleanUpTest.java │ │ │ │ ├── SecurityProviderCleanUpTest.java │ │ │ │ ├── ShutdownHookCleanUpTest.java │ │ │ │ ├── StopThreadsCleanUp_MultiThreadedHttpConnectionManagerTest.java │ │ │ │ ├── StopThreadsCleanUp_PostgresqlJdbcTest.java │ │ │ │ ├── StopThreadsCleanUp_Runnable.java │ │ │ │ ├── StopThreadsClenup_ExecutorTest.java │ │ │ │ ├── ThreadGroupCleanUpTest.java │ │ │ │ ├── ThreadLocalCleanUpTest.java │ │ │ │ ├── ThreadLocalWithNestedRefValueCleanUpTest.java │ │ │ │ ├── ThreadLocalWithRefValueCleanUpTest.java │ │ │ │ ├── X509TrustManagerImplUnparseableExtensionCleanUpTest.java │ │ │ │ └── package-info.java │ │ │ ├── package-info.java │ │ │ └── preinit/ │ │ │ ├── AwtToolkitInitiatorTest.java │ │ │ ├── DatatypeConverterImplInitiatorTest.java │ │ │ ├── DocumentBuilderFactoryInitiatorTest.java │ │ │ ├── Java2dDisposerInitiatorTest.java │ │ │ ├── Java2dRenderQueueInitiatorTest.java │ │ │ ├── JavaxSecurityLoginConfigurationInitiatorTest.java │ │ │ ├── JdbcDriversInitiatorTest.java │ │ │ ├── LdapPoolManagerInitiatorTest.java │ │ │ ├── OracleJdbcThreadInitiatorTest.java │ │ │ ├── PreClassLoaderInitiatorTestBase.java │ │ │ ├── ReplaceDOMNormalizerSerializerAbortExceptionInitiatorTest.java │ │ │ ├── SecurityPolicyInitiatorTest.java │ │ │ ├── SecurityProvidersInitiatorTest.java │ │ │ ├── SunAwtAppContextInitiatorTest.java │ │ │ └── SunGCInitiatorTest.java │ │ └── resources/ │ │ ├── META-INF/ │ │ │ └── services/ │ │ │ └── javax.imageio.spi.ImageInputStreamSpi │ │ ├── spi-cacert-2008.crt │ │ └── spi-cacert-2008.keystore │ ├── classloader-leak-prevention-servlet/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ └── java/ │ │ └── se/ │ │ └── jiderhamn/ │ │ └── classloader/ │ │ └── leak/ │ │ └── prevention/ │ │ └── ClassLoaderLeakPreventorListener.java │ ├── classloader-leak-prevention-servlet3/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── se/ │ │ │ └── jiderhamn/ │ │ │ └── classloader/ │ │ │ └── leak/ │ │ │ └── prevention/ │ │ │ └── ClassLoaderLeakPreventionContainerInitializer.java │ │ └── resources/ │ │ └── META-INF/ │ │ ├── services/ │ │ │ └── javax.servlet.ServletContainerInitializer │ │ └── web-fragment.xml │ └── pom.xml ├── classloader-leak-test-framework/ │ ├── README.md │ ├── pom.xml │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── se/ │ │ └── jiderhamn/ │ │ ├── HeapDumper.java │ │ └── classloader/ │ │ ├── PackagesLoadedOutsideClassLoader.java │ │ ├── RedefiningClassLoader.java │ │ ├── ZombieMarker.java │ │ └── leak/ │ │ ├── JUnitClassloaderRunner.java │ │ ├── LeakPreventor.java │ │ └── Leaks.java │ └── test/ │ └── java/ │ ├── com/ │ │ └── classloader/ │ │ └── test/ │ │ └── CustomClass.java │ └── se/ │ └── jiderhamn/ │ └── classloader/ │ ├── RedefiningClassLoaderTest.java │ └── leak/ │ ├── JUnitClassloaderRunnerTest.java │ ├── NonLeakingTest.java │ ├── accused/ │ │ ├── CustomThreadLocalTest.java │ │ └── package-info.java │ └── known/ │ ├── CustomThreadLocalCustomValueTest.java │ ├── JEditorPaneTest.java │ └── package-info.java ├── mvnw ├── mvnw.cmd └── pom.xml
SYMBOL INDEX (488 symbols across 126 files)
FILE: .mvn/wrapper/MavenWrapperDownloader.java
class MavenWrapperDownloader (line 33) | public final class MavenWrapperDownloader
method main (line 62) | public static void main( String[] args )
method downloadFileFromURL (line 102) | private static void downloadFileFromURL( String urlString, Path destin...
method createDirectories (line 125) | private static void createDirectories(Path outputPath) throws IOException
method readWrapperUrl (line 133) | private static String readWrapperUrl( Path mavenWrapperPropertyFile )
method log (line 154) | private static void log( String msg )
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/ClassLoaderLeakPreventor.java
class ClassLoaderLeakPreventor (line 19) | @SuppressWarnings("WeakerAccess")
method ClassLoaderLeakPreventor (line 60) | public ClassLoaderLeakPreventor(ClassLoader leakSafeClassLoader, Class...
method runPreClassLoaderInitiators (line 90) | public void runPreClassLoaderInitiators() {
method doInLeakSafeClassLoader (line 111) | protected void doInLeakSafeClassLoader(final Runnable runnable) {
method createAccessControlContext (line 139) | public AccessControlContext createAccessControlContext() {
method createDomainCombiner (line 158) | private DomainCombiner createDomainCombiner() {
method removeDomainCombiner (line 197) | @Deprecated
method removeDomainCombiner (line 206) | public void removeDomainCombiner(String owner, AccessControlContext ac...
method runCleanUps (line 230) | public void runCleanUps() {
method getClassLoader (line 254) | public ClassLoader getClassLoader() {
method getLeakSafeClassLoader (line 263) | public ClassLoader getLeakSafeClassLoader() {
method isLoadedInClassLoader (line 268) | public boolean isLoadedInClassLoader(Object o) {
method isLoadedByClassLoader (line 274) | public boolean isLoadedByClassLoader(Class<?> clazz) {
method isClassLoaderOrChild (line 279) | public boolean isClassLoaderOrChild(ClassLoader cl) {
method isThreadInClassLoader (line 325) | public boolean isThreadInClassLoader(Thread thread) {
method waitForThread (line 338) | public void waitForThread(Thread thread, long waitMs, boolean interrup...
method getStackTrace (line 359) | public String getStackTrace(Thread thread) {
method getStaticFieldValue (line 380) | public <E> E getStaticFieldValue(Class<?> clazz, String fieldName) {
method getStaticFieldValue (line 385) | public <E> E getStaticFieldValue(String className, String fieldName) {
method getStaticFieldValue (line 389) | public <E> E getStaticFieldValue(String className, String fieldName, b...
method findFieldOfClass (line 394) | public Field findFieldOfClass(String className, String fieldName) {
method findFieldOfClass (line 398) | public Field findFieldOfClass(String className, String fieldName, bool...
method findClass (line 407) | public Class<?> findClass(String className) {
method findClass (line 411) | public Class<?> findClass(String className, boolean trySystemCL) {
method findField (line 437) | public Field findField(Class<?> clazz, String fieldName) {
method getStaticFieldValue (line 456) | public <T> T getStaticFieldValue(Field field) {
method getFieldValue (line 472) | public <T> T getFieldValue(Object obj, String fieldName) {
method getFieldValue (line 477) | public <T> T getFieldValue(Field field, Object obj) {
method setFinalStaticField (line 488) | public void setFinalStaticField(Field field, Object newValue) {
method findMethod (line 514) | public Method findMethod(String className, String methodName, Class......
method findMethod (line 523) | public Method findMethod(Class<?> clazz, String methodName, Class... p...
method getAllThreads (line 541) | public Collection<Thread> getAllThreads() {
method isJBoss (line 574) | public boolean isJBoss() {
method isOracleJRE (line 591) | public boolean isOracleJRE() {
method gc (line 601) | public static void gc() {
method isDisableExplicitGCEnabled (line 622) | private static boolean isDisableExplicitGCEnabled() {
method isJvmShuttingDown (line 630) | public boolean isJvmShuttingDown() {
class NestedProtectionDomainCombinerException (line 648) | private static class NestedProtectionDomainCombinerException extends R...
method debug (line 656) | public void debug(String msg) {
method warn (line 660) | public void warn(Throwable t) {
method error (line 664) | public void error(Throwable t) {
method warn (line 668) | public void warn(String msg) {
method error (line 672) | public void error(String msg) {
method info (line 676) | public void info(String msg) {
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/ClassLoaderLeakPreventorFactory.java
class ClassLoaderLeakPreventorFactory (line 18) | public class ClassLoaderLeakPreventorFactory {
method ClassLoaderLeakPreventorFactory (line 54) | public ClassLoaderLeakPreventorFactory() {
method ClassLoaderLeakPreventorFactory (line 62) | public ClassLoaderLeakPreventorFactory(ClassLoader leakSafeClassLoader) {
method configureDefaults (line 68) | public void configureDefaults() {
method newLeakPreventor (line 145) | public ClassLoaderLeakPreventor newLeakPreventor() {
method newLeakPreventor (line 150) | public ClassLoaderLeakPreventor newLeakPreventor(ClassLoader classLoad...
method setLogger (line 160) | public void setLogger(Logger logger) {
method addPreInitiator (line 165) | public void addPreInitiator(PreClassLoaderInitiator preClassLoaderInit...
method addCleanUp (line 170) | public void addCleanUp(ClassLoaderPreMortemCleanUp classLoaderPreMorte...
method addConsideringOrder (line 175) | private <I> void addConsideringOrder(Map<String, I> map, I newEntry) {
method addCleanUp (line 193) | public void addCleanUp(String name, ClassLoaderPreMortemCleanUp classL...
method clearPreInitiators (line 198) | public void clearPreInitiators() {
method clearCleanUps (line 203) | public void clearCleanUps() {
method getPreInitiator (line 213) | public <C extends PreClassLoaderInitiator> C getPreInitiator(Class<C> ...
method getCleanUp (line 223) | public <C extends ClassLoaderPreMortemCleanUp> C getCleanUp(Class<C> c...
method removePreInitiator (line 228) | public <C extends PreClassLoaderInitiator> void removePreInitiator(Cla...
method removeCleanUp (line 233) | public <C extends ClassLoaderPreMortemCleanUp> void removeCleanUp(Clas...
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/ClassLoaderPreMortemCleanUp.java
type ClassLoaderPreMortemCleanUp (line 8) | public interface ClassLoaderPreMortemCleanUp {
method cleanUp (line 14) | void cleanUp(ClassLoaderLeakPreventor preventor);
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/JULLogger.java
class JULLogger (line 10) | public class JULLogger implements Logger {
method debug (line 15) | @Override
method info (line 20) | @Override
method warn (line 25) | @Override
method warn (line 30) | @Override
method error (line 35) | @Override
method error (line 40) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/Logger.java
type Logger (line 13) | public interface Logger {
method debug (line 16) | void debug(String msg);
method info (line 19) | void info(String msg);
method warn (line 22) | void warn(String msg);
method warn (line 25) | void warn(Throwable t);
method error (line 28) | void error(String msg);
method error (line 31) | void error(Throwable t);
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/MustBeAfter.java
type MustBeAfter (line 13) | public interface MustBeAfter<I> {
method mustBeBeforeMe (line 19) | Class<? extends I>[] mustBeBeforeMe();
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/PreClassLoaderInitiator.java
type PreClassLoaderInitiator (line 8) | public interface PreClassLoaderInitiator {
method doOutsideClassLoader (line 16) | void doOutsideClassLoader(ClassLoaderLeakPreventor preventor);
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/ReplaceDOMNormalizerSerializerAbortException.java
class ReplaceDOMNormalizerSerializerAbortException (line 24) | public class ReplaceDOMNormalizerSerializerAbortException implements Pre...
method doOutsideClassLoader (line 26) | @Override
method cleanUp (line 31) | @Override
method replaceDOMNormalizerSerializerAbortException (line 36) | @SuppressWarnings("WeakerAccess")
method constructRuntimeExceptionWithoutStackTrace (line 51) | @SuppressWarnings("WeakerAccess")
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/StdLogger.java
class StdLogger (line 10) | public class StdLogger implements Logger {
method getLogPrefix (line 13) | protected String getLogPrefix() {
method debug (line 17) | @Override
method info (line 22) | @Override
method warn (line 27) | @Override
method warn (line 32) | @Override
method error (line 37) | @Override
method error (line 42) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/ApacheCommonsLoggingCleanUp.java
class ApacheCommonsLoggingCleanUp (line 16) | public class ApacheCommonsLoggingCleanUp implements ClassLoaderPreMortem...
method cleanUp (line 17) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/BeanELResolverCleanUp.java
class BeanELResolverCleanUp (line 14) | public class BeanELResolverCleanUp implements ClassLoaderPreMortemCleanUp {
method cleanUp (line 15) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/BeanIntrospectorCleanUp.java
class BeanIntrospectorCleanUp (line 13) | public class BeanIntrospectorCleanUp implements ClassLoaderPreMortemClea...
method cleanUp (line 14) | @Override
method clearClassInfoCache (line 28) | private void clearClassInfoCache(ClassLoaderLeakPreventor preventor) {
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/BeanValidationCleanUp.java
class BeanValidationCleanUp (line 13) | public class BeanValidationCleanUp implements ClassLoaderPreMortemCleanUp {
method cleanUp (line 14) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/DefaultAuthenticatorCleanUp.java
class DefaultAuthenticatorCleanUp (line 17) | public class DefaultAuthenticatorCleanUp implements ClassLoaderPreMortem...
method cleanUp (line 18) | @Override
method getDefaultAuthenticator (line 70) | @SuppressWarnings("WeakerAccess")
method removeWrappedAuthenticators (line 90) | @SuppressWarnings("WeakerAccess")
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/DriverManagerCleanUp.java
class DriverManagerCleanUp (line 17) | public class DriverManagerCleanUp implements ClassLoaderPreMortemCleanUp {
method cleanUp (line 18) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/GeoToolsCleanUp.java
class GeoToolsCleanUp (line 12) | public class GeoToolsCleanUp implements ClassLoaderPreMortemCleanUp {
method cleanUp (line 13) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/IIOServiceProviderCleanUp.java
class IIOServiceProviderCleanUp (line 18) | public class IIOServiceProviderCleanUp implements ClassLoaderPreMortemCl...
method cleanUp (line 19) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/IntrospectionUtilsCleanUp.java
class IntrospectionUtilsCleanUp (line 10) | public class IntrospectionUtilsCleanUp implements ClassLoaderPreMortemCl...
method cleanUp (line 11) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/JDK8151486CleanUp.java
class JDK8151486CleanUp (line 14) | public class JDK8151486CleanUp implements ClassLoaderPreMortemCleanUp {
method cleanUp (line 15) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/JacksonCleanUp.java
class JacksonCleanUp (line 13) | public class JacksonCleanUp implements ClassLoaderPreMortemCleanUp {
method cleanUp (line 14) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/JavaServerFaces2746CleanUp.java
class JavaServerFaces2746CleanUp (line 15) | public class JavaServerFaces2746CleanUp implements ClassLoaderPreMortemC...
method cleanUp (line 16) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/JavaUtilLoggingLevelCleanUp.java
class JavaUtilLoggingLevelCleanUp (line 14) | public class JavaUtilLoggingLevelCleanUp implements ClassLoaderPreMortem...
method cleanUp (line 15) | @Override
method process (line 44) | private Set/*<KnownLevel>*/ process(ClassLoaderLeakPreventor preventor...
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/JavaxSecurityAuthLoginConfigurationCleanUp.java
class JavaxSecurityAuthLoginConfigurationCleanUp (line 12) | public class JavaxSecurityAuthLoginConfigurationCleanUp implements Class...
method cleanUp (line 14) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/JceSecurityCleanUp.java
class JceSecurityCleanUp (line 14) | public class JceSecurityCleanUp implements ClassLoaderPreMortemCleanUp {
method cleanUp (line 16) | @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter")
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/KeepAliveTimerCacheCleanUp.java
class KeepAliveTimerCacheCleanUp (line 12) | public class KeepAliveTimerCacheCleanUp implements ClassLoaderPreMortemC...
method mustBeBeforeMe (line 15) | @Override
method cleanUp (line 20) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/MBeanCleanUp.java
class MBeanCleanUp (line 20) | public class MBeanCleanUp implements ClassLoaderPreMortemCleanUp {
method cleanUp (line 21) | @Override
method isJettyWithJMX (line 71) | @SuppressWarnings("WeakerAccess")
class JettyJMXRemover (line 98) | private class JettyJMXRemover {
method JettyJMXRemover (line 114) | @SuppressWarnings("WeakerAccess")
method unregisterJettyJMXBean (line 187) | boolean unregisterJettyJMXBean(ObjectName objectName) {
method findJettyClass (line 213) | Class findJettyClass(String className) throws ClassNotFoundException {
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/MXBeanNotificationListenersCleanUp.java
class MXBeanNotificationListenersCleanUp (line 21) | public class MXBeanNotificationListenersCleanUp implements ClassLoaderPr...
method cleanUp (line 22) | @Override
method unregisterNotificationListeners (line 83) | protected void unregisterNotificationListeners(ClassLoaderLeakPrevento...
method unwrap (line 120) | private NotificationListener unwrap(ClassLoaderLeakPreventor preventor...
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/MoxyCleanUp.java
class MoxyCleanUp (line 16) | public class MoxyCleanUp implements ClassLoaderPreMortemCleanUp {
method cleanUp (line 17) | @Override
method findClass (line 36) | public Class<?> findClass(ClassLoaderLeakPreventor preventor, String c...
method unsetField (line 50) | private void unsetField(ClassLoaderLeakPreventor preventor,
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/MultiThreadedHttpConnectionManagerCleanUp.java
class MultiThreadedHttpConnectionManagerCleanUp (line 11) | public class MultiThreadedHttpConnectionManagerCleanUp implements ClassL...
method cleanUp (line 13) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/ObjectStreamClassCleanup.java
class ObjectStreamClassCleanup (line 11) | public class ObjectStreamClassCleanup implements ClassLoaderPreMortemCle...
method cleanUp (line 13) | @Override
method clearIfConcurrentHashMap (line 30) | protected void clearIfConcurrentHashMap(Object object, ClassLoaderLeak...
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/PropertyEditorCleanUp.java
class PropertyEditorCleanUp (line 17) | public class PropertyEditorCleanUp implements ClassLoaderPreMortemCleanUp {
method cleanUp (line 18) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/ProxySelectorCleanUp.java
class ProxySelectorCleanUp (line 14) | public class ProxySelectorCleanUp implements ClassLoaderPreMortemCleanUp {
method cleanUp (line 15) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/ReactorNettyHttpResourcesCleanUp.java
class ReactorNettyHttpResourcesCleanUp (line 13) | public class ReactorNettyHttpResourcesCleanUp implements ClassLoaderPreM...
method cleanUp (line 14) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/ResourceBundleCleanUp.java
class ResourceBundleCleanUp (line 17) | public class ResourceBundleCleanUp implements ClassLoaderPreMortemCleanUp {
method cleanUp (line 18) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/RmiTargetsCleanUp.java
class RmiTargetsCleanUp (line 14) | public class RmiTargetsCleanUp implements ClassLoaderPreMortemCleanUp {
method cleanUp (line 15) | @Override
method clearRmiTargetsMap (line 30) | @SuppressWarnings("WeakerAccess")
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/SAAJEnvelopeFactoryParserPoolCleanUp.java
class SAAJEnvelopeFactoryParserPoolCleanUp (line 15) | public class SAAJEnvelopeFactoryParserPoolCleanUp implements ClassLoader...
method cleanUp (line 16) | @Override
method cleanupWithFactoryClass (line 24) | private void cleanupWithFactoryClass(final ClassLoaderLeakPreventor pr...
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/SecurityProviderCleanUp.java
class SecurityProviderCleanUp (line 13) | public class SecurityProviderCleanUp implements ClassLoaderPreMortemClea...
method cleanUp (line 14) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/ShutdownHookCleanUp.java
class ShutdownHookCleanUp (line 13) | public class ShutdownHookCleanUp implements ClassLoaderPreMortemCleanUp {
method ShutdownHookCleanUp (line 30) | @SuppressWarnings("unused")
method ShutdownHookCleanUp (line 35) | public ShutdownHookCleanUp(boolean executeShutdownHooks, int shutdownH...
method setExecuteShutdownHooks (line 40) | public void setExecuteShutdownHooks(boolean executeShutdownHooks) {
method setShutdownHookWaitMs (line 44) | public void setShutdownHookWaitMs(int shutdownHookWaitMs) {
method cleanUp (line 48) | @Override
method removeShutdownHook (line 65) | @SuppressWarnings({"deprecation", "WeakerAccess"})
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/StopThreadsCleanUp.java
class StopThreadsCleanUp (line 19) | @SuppressWarnings("WeakerAccess")
method StopThreadsCleanUp (line 41) | @SuppressWarnings("unused")
method StopThreadsCleanUp (line 46) | public StopThreadsCleanUp(boolean stopThreads, boolean stopTimerThread...
method setStopThreads (line 51) | public void setStopThreads(boolean stopThreads) {
method setStopTimerThreads (line 55) | public void setStopTimerThreads(boolean stopTimerThreads) {
method setThreadWaitMs (line 59) | public void setThreadWaitMs(int threadWaitMs) {
method cleanUp (line 63) | @Override
method forceStartOpenOfficeJurtCleanup (line 78) | protected void forceStartOpenOfficeJurtCleanup(ClassLoaderLeakPrevento...
method stopThreads (line 108) | protected void stopThreads(ClassLoaderLeakPreventor preventor) {
method setThreadSafeAccessControlContext (line 260) | private void setThreadSafeAccessControlContext(ClassLoaderLeakPrevento...
method getRunnable (line 276) | private Runnable getRunnable(ClassLoaderLeakPreventor preventor, Threa...
method stopTimerThread (line 286) | protected void stopTimerThread(ClassLoaderLeakPreventor preventor, Thr...
class JURTKiller (line 325) | protected class JURTKiller extends Thread {
method JURTKiller (line 333) | public JURTKiller(ClassLoaderLeakPreventor preventor, Thread jurtThr...
method run (line 343) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/ThreadGroupCleanUp.java
class ThreadGroupCleanUp (line 13) | public class ThreadGroupCleanUp implements ClassLoaderPreMortemCleanUp, ...
method mustBeBeforeMe (line 15) | @Override
method cleanUp (line 20) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/ThreadGroupContextCleanUp.java
class ThreadGroupContextCleanUp (line 15) | public class ThreadGroupContextCleanUp implements ClassLoaderPreMortemCl...
method cleanUp (line 16) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/ThreadLocalCleanUp.java
class ThreadLocalCleanUp (line 17) | @SuppressWarnings("WeakerAccess")
method mustBeBeforeMe (line 33) | @Override
method cleanUp (line 38) | @Override
method initFields (line 58) | private void initFields(ClassLoaderLeakPreventor preventor) {
method forEachThreadLocalInThread (line 66) | protected void forEachThreadLocalInThread(ClassLoaderLeakPreventor pre...
method processThreadLocalMap (line 81) | protected void processThreadLocalMap(ClassLoaderLeakPreventor preventor,
method dereferenceIfApplicable (line 174) | protected Object dereferenceIfApplicable(Object value) {
method processLeak (line 182) | protected void processLeak(ClassLoaderLeakPreventor preventor, Thread ...
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/WarningThreadLocalCleanUp.java
class WarningThreadLocalCleanUp (line 13) | @SuppressWarnings("unused")
method processLeak (line 19) | protected void processLeak(ClassLoaderLeakPreventor preventor, Thread ...
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/cleanup/X509TrustManagerImplUnparseableExtensionCleanUp.java
class X509TrustManagerImplUnparseableExtensionCleanUp (line 19) | public class X509TrustManagerImplUnparseableExtensionCleanUp implements ...
method cleanUp (line 23) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/preinit/AwtToolkitInitiator.java
class AwtToolkitInitiator (line 13) | public class AwtToolkitInitiator implements PreClassLoaderInitiator {
method doOutsideClassLoader (line 14) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/preinit/DatatypeConverterImplInitiator.java
class DatatypeConverterImplInitiator (line 20) | public class DatatypeConverterImplInitiator implements PreClassLoaderIni...
method doOutsideClassLoader (line 21) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/preinit/DocumentBuilderFactoryInitiator.java
class DocumentBuilderFactoryInitiator (line 13) | public class DocumentBuilderFactoryInitiator implements PreClassLoaderIn...
method doOutsideClassLoader (line 14) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/preinit/JarUrlConnectionInitiator.java
class JarUrlConnectionInitiator (line 17) | public class JarUrlConnectionInitiator implements PreClassLoaderInitiator {
method doOutsideClassLoader (line 18) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/preinit/Java2dDisposerInitiator.java
class Java2dDisposerInitiator (line 14) | public class Java2dDisposerInitiator implements PreClassLoaderInitiator {
method doOutsideClassLoader (line 15) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/preinit/Java2dRenderQueueInitiator.java
class Java2dRenderQueueInitiator (line 11) | public class Java2dRenderQueueInitiator implements PreClassLoaderInitiat...
method doOutsideClassLoader (line 12) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/preinit/JavaxSecurityLoginConfigurationInitiator.java
class JavaxSecurityLoginConfigurationInitiator (line 14) | public class JavaxSecurityLoginConfigurationInitiator implements PreClas...
method doOutsideClassLoader (line 15) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/preinit/JdbcDriversInitiator.java
class JdbcDriversInitiator (line 20) | public class JdbcDriversInitiator implements PreClassLoaderInitiator {
method doOutsideClassLoader (line 21) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/preinit/LdapPoolManagerInitiator.java
class LdapPoolManagerInitiator (line 15) | public class LdapPoolManagerInitiator implements PreClassLoaderInitiator {
method doOutsideClassLoader (line 16) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/preinit/OracleJdbcThreadInitiator.java
class OracleJdbcThreadInitiator (line 20) | public class OracleJdbcThreadInitiator implements PreClassLoaderInitiator {
method doOutsideClassLoader (line 21) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/preinit/SecurityPolicyInitiator.java
class SecurityPolicyInitiator (line 16) | public class SecurityPolicyInitiator implements PreClassLoaderInitiator {
method doOutsideClassLoader (line 17) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/preinit/SecurityProvidersInitiator.java
class SecurityProvidersInitiator (line 14) | public class SecurityProvidersInitiator implements PreClassLoaderInitiat...
method doOutsideClassLoader (line 15) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/preinit/SunAwtAppContextInitiator.java
class SunAwtAppContextInitiator (line 16) | public class SunAwtAppContextInitiator implements PreClassLoaderInitiator {
method doOutsideClassLoader (line 17) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/main/java/se/jiderhamn/classloader/leak/prevention/preinit/SunGCInitiator.java
class SunGCInitiator (line 21) | public class SunGCInitiator implements PreClassLoaderInitiator {
method doOutsideClassLoader (line 22) | @Override
method getGCClass (line 45) | private Class<?> getGCClass() throws ClassNotFoundException {
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/ClassLoaderLeakPreventorFactoryTest.java
class ClassLoaderLeakPreventorFactoryTest (line 15) | public class ClassLoaderLeakPreventorFactoryTest {
method cleanUpMustBeAfter (line 18) | @Test(expected = IllegalStateException.class) // TODO #51
method circularMustBeAfter (line 80) | @Test(expected = IllegalStateException.class)
class RecordingCleanUp (line 90) | private abstract static class RecordingCleanUp implements ClassLoaderP...
method RecordingCleanUp (line 94) | RecordingCleanUp(List<ClassLoaderPreMortemCleanUp> cleanUps) {
method cleanUp (line 98) | @Override
method toString (line 103) | @Override
class Foo (line 109) | private static class Foo extends RecordingCleanUp {
method Foo (line 110) | Foo(List<ClassLoaderPreMortemCleanUp> cleanUps) {
class Bar (line 115) | private static class Bar extends RecordingCleanUp {
method Bar (line 116) | Bar(List<ClassLoaderPreMortemCleanUp> cleanUps) {
class AfterFoo (line 121) | private static class AfterFoo extends RecordingCleanUp implements Must...
method AfterFoo (line 122) | AfterFoo(List<ClassLoaderPreMortemCleanUp> cleanUps) {
method mustBeBeforeMe (line 126) | @Override
class AfterBar (line 132) | private static class AfterBar extends RecordingCleanUp implements Must...
method AfterBar (line 133) | AfterBar(List<ClassLoaderPreMortemCleanUp> cleanUps) {
method mustBeBeforeMe (line 137) | @Override
class AfterFooAndBar (line 143) | private static class AfterFooAndBar extends RecordingCleanUp implement...
method AfterFooAndBar (line 144) | AfterFooAndBar(List<ClassLoaderPreMortemCleanUp> cleanUps) {
method mustBeBeforeMe (line 148) | @Override
class Circle1 (line 156) | private static class Circle1 extends RecordingCleanUp implements MustB...
method Circle1 (line 157) | Circle1() {
method mustBeBeforeMe (line 161) | @Override
class Circle2 (line 167) | private static class Circle2 extends RecordingCleanUp implements MustB...
method Circle2 (line 168) | Circle2() {
method mustBeBeforeMe (line 172) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/PreventionsTestBase.java
class PreventionsTestBase (line 10) | public abstract class PreventionsTestBase<C> {
method getTestedImplementation (line 15) | protected C getTestedImplementation() throws IllegalAccessException, I...
method getClassLoaderLeakPreventor (line 31) | protected ClassLoaderLeakPreventor getClassLoaderLeakPreventor() {
method getLeakSafeClassLoader (line 43) | protected ClassLoader getLeakSafeClassLoader() {
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/StopThreadsCleanUp_TimerTest.java
class StopThreadsCleanUp_TimerTest (line 15) | @RunWith(JUnitClassloaderRunner.class)
method createTimer (line 22) | @Test
class Preventor (line 28) | public static class Preventor implements Runnable {
method run (line 29) | public void run() {
class TimerThreadsCleanUp (line 51) | private static class TimerThreadsCleanUp extends StopThreadsCleanUp {
method TimerThreadsCleanUp (line 52) | public TimerThreadsCleanUp() {
method stopTimerThread (line 57) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/cleanup/BeanELResolverCleanUpTest.java
class BeanELResolverCleanUpTest (line 11) | public class BeanELResolverCleanUpTest extends ClassLoaderPreMortemClean...
method setUp (line 13) | @Before
method triggerLeak (line 19) | @Override
class Bean (line 29) | @SuppressWarnings("unused")
method getFoo (line 33) | public String getFoo() {
method setFoo (line 37) | public void setFoo(String foo) {
class MyELContext (line 43) | private static class MyELContext extends ELContext {
method getELResolver (line 44) | @Override
method getFunctionMapper (line 49) | @Override
method getVariableMapper (line 54) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/cleanup/BeanIntrospectorCleanUpTest.java
class BeanIntrospectorCleanUpTest (line 8) | public class BeanIntrospectorCleanUpTest extends ClassLoaderPreMortemCle...
method triggerLeak (line 10) | @Override
class Bean (line 15) | protected class Bean {
method getDummyField (line 18) | public int getDummyField() {
method setDummyField (line 22) | public void setDummyField(int dummyField) {
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/cleanup/BeanValidationCleanUpTest.java
class BeanValidationCleanUpTest (line 10) | public class BeanValidationCleanUpTest extends ClassLoaderPreMortemClean...
method triggerLeak (line 12) | @Override
class ThreadLocalCleanUp_ApacheAxis14Test (line 21) | @Ignore // Fixed in newer versions of Java???
method triggerLeak (line 24) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/cleanup/ClassLoaderPreMortemCleanUpTestBase.java
class ClassLoaderPreMortemCleanUpTestBase (line 15) | @RunWith(JUnitClassloaderRunner.class)
method triggerLeakWithoutCleanup (line 22) | @SuppressWarnings("DefaultAnnotationParam")
method cleanUpAfterTriggeringLeak (line 29) | @Test
method triggerLeak (line 37) | protected abstract void triggerLeak() throws Exception;
method getClassLoaderPreMortemCleanUp (line 45) | @SuppressWarnings("WeakerAccess")
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/cleanup/DefaultAuthenticatorCleanUpTest.java
class DefaultAuthenticatorCleanUpTest (line 10) | public class DefaultAuthenticatorCleanUpTest extends ClassLoaderPreMorte...
method triggerLeak (line 11) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/cleanup/DriverManagerCleanUpTest.java
class DriverManagerCleanUpTest (line 9) | @PackagesLoadedOutsideClassLoader(packages = "org.postgresql", addToDefa...
method triggerLeak (line 12) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/cleanup/GeoToolsCleanUpTest.java
class GeoToolsCleanUpTest (line 9) | public class GeoToolsCleanUpTest extends ClassLoaderPreMortemCleanUpTest...
method triggerLeak (line 10) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/cleanup/IIOServiceProviderCleanUpTest.java
class IIOServiceProviderCleanUpTest (line 13) | public class IIOServiceProviderCleanUpTest extends ClassLoaderPreMortemC...
method systemClassLoader (line 16) | @Before
method triggerLeak (line 21) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/cleanup/ImageIOMockImageInputStreamSPI.java
class ImageIOMockImageInputStreamSPI (line 10) | public class ImageIOMockImageInputStreamSPI extends ImageInputStreamSpi {
method createInputStreamInstance (line 12) | @Override
method getDescription (line 17) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/cleanup/JDK8151486CleanUpTest.java
class JDK8151486CleanUpTest (line 10) | @Ignore
method triggerLeak (line 12) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/cleanup/JacksonCleanUpTest.java
class JacksonCleanUpTest (line 10) | @PackagesLoadedOutsideClassLoader(packages = {"com.fasterxml.jackson.dat...
method triggerLeak (line 12) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/cleanup/JavaServerFaces2746CleanUpTest.java
class JavaServerFaces2746CleanUpTest (line 13) | public class JavaServerFaces2746CleanUpTest extends ClassLoaderPreMortem...
method doTriggerLeak (line 22) | private static void doTriggerLeak() {
class MyComponent (line 30) | @SuppressWarnings("unused")
method getFamily (line 32) | @Override
method getAttribute (line 38) | public MyAttribute getAttribute() {
method setAttribute (line 43) | public void setAttribute(MyAttribute myAttribute) {
class MyAttribute (line 49) | private static class MyAttribute {
method triggerLeak (line 59) | @SuppressWarnings("UnusedAssignment")
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/cleanup/JavaUtilLoggingLevelCleanUpTest.java
class JavaUtilLoggingLevelCleanUpTest (line 7) | public class JavaUtilLoggingLevelCleanUpTest extends ClassLoaderPreMorte...
method triggerLeak (line 8) | @Override
class CustomLevel (line 17) | public static class CustomLevel extends java.util.logging.Level {
method CustomLevel (line 18) | public CustomLevel() {
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/cleanup/JavaxSecurityAuthLoginConfigurationCleanUpTest.java
class JavaxSecurityAuthLoginConfigurationCleanUpTest (line 10) | public class JavaxSecurityAuthLoginConfigurationCleanUpTest
method triggerLeak (line 13) | @Override
class MockConfiguration (line 18) | private class MockConfiguration extends Configuration {
method getAppConfigurationEntry (line 19) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/cleanup/JceSecurityCleanUpTest.java
class JceSecurityCleanUpTest (line 11) | public class JceSecurityCleanUpTest extends ClassLoaderPreMortemCleanUpT...
method triggerLeak (line 12) | @Override
class MyProvider (line 28) | public static class MyProvider extends Provider {
method MyProvider (line 29) | public MyProvider(String name, double version, String info) {
method getService (line 33) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/cleanup/MBeanCleanUpTest.java
class MBeanCleanUpTest (line 11) | public class MBeanCleanUpTest extends ClassLoaderPreMortemCleanUpTestBas...
method triggerLeak (line 13) | @Override
type CustomMBean (line 19) | public interface CustomMBean {
class Custom (line 22) | public static class Custom implements CustomMBean {
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/cleanup/MXBeanNotificationListenersCleanUpTest.java
class MXBeanNotificationListenersCleanUpTest (line 12) | public class MXBeanNotificationListenersCleanUpTest extends ClassLoaderP...
method triggerLeak (line 13) | @Override
class CustomNotificationListener (line 19) | static class CustomNotificationListener implements NotificationListener {
method handleNotification (line 20) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/cleanup/MXBeanNotificationListenersCleanUp_ListenerWrapperTest.java
class MXBeanNotificationListenersCleanUp_ListenerWrapperTest (line 15) | public class MXBeanNotificationListenersCleanUp_ListenerWrapperTest exte...
method triggerLeak (line 16) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/cleanup/MoxyCleanUpTest.java
class MoxyCleanUpTest (line 11) | @PackagesLoadedOutsideClassLoader(packages = {"org.eclipse.persistence.j...
method triggerLeak (line 13) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/cleanup/MultiThreadedHttpConnectionManagerCleanUpTest.java
class MultiThreadedHttpConnectionManagerCleanUpTest (line 15) | public class MultiThreadedHttpConnectionManagerCleanUpTest extends Class...
method triggerLeak (line 17) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/cleanup/ObjectStreamClassCleanupTest.java
class ObjectStreamClassCleanupTest (line 9) | public class ObjectStreamClassCleanupTest extends ClassLoaderPreMortemCl...
method triggerLeak (line 11) | @Override
class SerializableEntity (line 16) | protected final class SerializableEntity implements Serializable {
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/cleanup/PropertyEditorCleanUpTest.java
class PropertyEditorCleanUpTest (line 11) | @Ignore // No longer leaks in Java 7+
method triggerLeak (line 13) | @Override
class Foo (line 19) | public static class Foo {
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/cleanup/ProxySelectorCleanUpTest.java
class ProxySelectorCleanUpTest (line 14) | public class ProxySelectorCleanUpTest extends ClassLoaderPreMortemCleanU...
method triggerLeak (line 15) | @Override
class MyProxySelector (line 21) | private static class MyProxySelector extends ProxySelector {
method select (line 22) | @Override
method connectFailed (line 27) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/cleanup/ReplaceDOMNormalizerSerializerAbortExceptionCleanUpTest.java
class ReplaceDOMNormalizerSerializerAbortExceptionCleanUpTest (line 11) | public class ReplaceDOMNormalizerSerializerAbortExceptionCleanUpTest ext...
method triggerLeak (line 12) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/cleanup/SAAJEnvelopeFactoryParserPoolCleanUpTest.java
class SAAJEnvelopeFactoryParserPoolCleanUpTest (line 14) | public class SAAJEnvelopeFactoryParserPoolCleanUpTest extends ClassLoade...
method triggerLeak (line 16) | @Override
method getCustomErrorHandlerInstance (line 52) | public Object getCustomErrorHandlerInstance(final ClassLoaderLeakPreve...
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/cleanup/SecurityProviderCleanUpTest.java
class SecurityProviderCleanUpTest (line 9) | public class SecurityProviderCleanUpTest extends ClassLoaderPreMortemCle...
method triggerLeak (line 14) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/cleanup/ShutdownHookCleanUpTest.java
class ShutdownHookCleanUpTest (line 7) | public class ShutdownHookCleanUpTest extends ClassLoaderPreMortemCleanUp...
method triggerLeak (line 8) | @Override
class ShutdownHookThread (line 14) | private class ShutdownHookThread extends Thread {
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/cleanup/StopThreadsCleanUp_MultiThreadedHttpConnectionManagerTest.java
class StopThreadsCleanUp_MultiThreadedHttpConnectionManagerTest (line 17) | public class StopThreadsCleanUp_MultiThreadedHttpConnectionManagerTest e...
method triggerLeak (line 19) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/cleanup/StopThreadsCleanUp_PostgresqlJdbcTest.java
class StopThreadsCleanUp_PostgresqlJdbcTest (line 10) | @PackagesLoadedOutsideClassLoader(packages = {"org.postgresql", "com.mys...
method triggerLeak (line 13) | @Override
method tearDown (line 18) | @After
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/cleanup/StopThreadsCleanUp_Runnable.java
class StopThreadsCleanUp_Runnable (line 8) | public class StopThreadsCleanUp_Runnable extends ClassLoaderPreMortemCle...
method triggerLeak (line 10) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/cleanup/StopThreadsClenup_ExecutorTest.java
class StopThreadsClenup_ExecutorTest (line 13) | public class StopThreadsClenup_ExecutorTest extends ClassLoaderPreMortem...
method createSharedExecutor (line 17) | private static ExecutorService createSharedExecutor() {
method triggerLeak (line 33) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/cleanup/ThreadGroupCleanUpTest.java
class ThreadGroupCleanUpTest (line 7) | public class ThreadGroupCleanUpTest extends ClassLoaderPreMortemCleanUpT...
method triggerLeak (line 12) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/cleanup/ThreadLocalCleanUpTest.java
class ThreadLocalCleanUpTest (line 16) | public class ThreadLocalCleanUpTest extends ClassLoaderPreMortemCleanUpT...
method triggerLeak (line 25) | @Override
class Value (line 31) | private static class Value {
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/cleanup/ThreadLocalWithNestedRefValueCleanUpTest.java
class ThreadLocalWithNestedRefValueCleanUpTest (line 10) | public class ThreadLocalWithNestedRefValueCleanUpTest extends ClassLoade...
method triggerLeak (line 14) | @Override
class Value (line 20) | private static class Value {
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/cleanup/ThreadLocalWithRefValueCleanUpTest.java
class ThreadLocalWithRefValueCleanUpTest (line 14) | public class ThreadLocalWithRefValueCleanUpTest extends ClassLoaderPreMo...
method triggerLeak (line 18) | @Override
class Value (line 24) | private static class Value {
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/cleanup/X509TrustManagerImplUnparseableExtensionCleanUpTest.java
class X509TrustManagerImplUnparseableExtensionCleanUpTest (line 10) | public class X509TrustManagerImplUnparseableExtensionCleanUpTest extends...
method triggerLeak (line 11) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/preinit/AwtToolkitInitiatorTest.java
class AwtToolkitInitiatorTest (line 9) | @Ignore // Fixed in newer versions of Java?
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/preinit/DatatypeConverterImplInitiatorTest.java
class DatatypeConverterImplInitiatorTest (line 17) | @Ignore // Doesn't leak in Java 1.7.0 (_55, _56), but does in 1.8.0 (_74)
method setSystemProperty (line 19) | @Before
class MyDatatypeFactory (line 25) | public static class MyDatatypeFactory extends DatatypeFactory {
method newDuration (line 26) | @Override
method newDuration (line 31) | @Override
method newDuration (line 36) | @Override
method newXMLGregorianCalendar (line 41) | @Override
method newXMLGregorianCalendar (line 46) | @Override
method newXMLGregorianCalendar (line 51) | @Override
method newXMLGregorianCalendar (line 56) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/preinit/DocumentBuilderFactoryInitiatorTest.java
class DocumentBuilderFactoryInitiatorTest (line 9) | @Ignore // Fixed in newer versions of Java?
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/preinit/Java2dDisposerInitiatorTest.java
class Java2dDisposerInitiatorTest (line 9) | @Ignore // Fixed in newer versions of Java
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/preinit/Java2dRenderQueueInitiatorTest.java
class Java2dRenderQueueInitiatorTest (line 6) | public class Java2dRenderQueueInitiatorTest extends PreClassLoaderInitia...
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/preinit/JavaxSecurityLoginConfigurationInitiatorTest.java
class JavaxSecurityLoginConfigurationInitiatorTest (line 9) | @Ignore // Fixed in newer versions of Java
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/preinit/JdbcDriversInitiatorTest.java
class JdbcDriversInitiatorTest (line 9) | @Ignore // Cannot be tested this way
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/preinit/LdapPoolManagerInitiatorTest.java
class LdapPoolManagerInitiatorTest (line 9) | public class LdapPoolManagerInitiatorTest extends PreClassLoaderInitiato...
method setSystemProperty (line 10) | @Before
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/preinit/OracleJdbcThreadInitiatorTest.java
class OracleJdbcThreadInitiatorTest (line 10) | @Ignore // Oracle JDBC driver needs to be available for this test
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/preinit/PreClassLoaderInitiatorTestBase.java
class PreClassLoaderInitiatorTestBase (line 16) | @RunWith(JUnitClassloaderRunner.class)
method firstShouldLeak (line 21) | @SuppressWarnings("DefaultAnnotationParam")
method secondShouldNotLeak (line 29) | @Leaks(false)
method invokeInitiator (line 35) | private void invokeInitiator() throws IllegalAccessException, Instanti...
method getLeakSafeClassLoader (line 40) | @Override
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/preinit/ReplaceDOMNormalizerSerializerAbortExceptionInitiatorTest.java
class ReplaceDOMNormalizerSerializerAbortExceptionInitiatorTest (line 20) | @RunWith(JUnitClassloaderRunner.class)
method noLeakAfterInitiatorRun (line 23) | @Leaks(false)
method triggerLeak (line 31) | public static void triggerLeak() throws ParserConfigurationException {
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/preinit/SecurityPolicyInitiatorTest.java
class SecurityPolicyInitiatorTest (line 9) | @Ignore // Fixed in newer versions of Java?
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/preinit/SecurityProvidersInitiatorTest.java
class SecurityProvidersInitiatorTest (line 9) | @Ignore // Cannot be tested this way?
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/preinit/SunAwtAppContextInitiatorTest.java
class SunAwtAppContextInitiatorTest (line 7) | public class SunAwtAppContextInitiatorTest extends PreClassLoaderInitiat...
FILE: classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/preinit/SunGCInitiatorTest.java
class SunGCInitiatorTest (line 7) | public class SunGCInitiatorTest extends PreClassLoaderInitiatorTestBase<...
FILE: classloader-leak-prevention/classloader-leak-prevention-servlet/src/main/java/se/jiderhamn/classloader/leak/prevention/ClassLoaderLeakPreventorListener.java
class ClassLoaderLeakPreventorListener (line 121) | @SuppressWarnings("WeakerAccess")
method getDefaultOtherListeners (line 133) | static List<ServletContextListener> getDefaultOtherListeners() {
method contextInitialized (line 152) | @Override
method contextInitialized (line 168) | void contextInitialized(final ServletContext servletContext) {
method contextDestroyed (line 227) | @Override
method createClassLoaderLeakPreventorFactory (line 250) | protected ClassLoaderLeakPreventorFactory createClassLoaderLeakPrevent...
method getIntInitParameter (line 255) | protected static int getIntInitParameter(ServletContext servletContext...
method getLogPrefix (line 277) | protected String getLogPrefix() {
method info (line 284) | protected void info(String s) {
FILE: classloader-leak-prevention/classloader-leak-prevention-servlet3/src/main/java/se/jiderhamn/classloader/leak/prevention/ClassLoaderLeakPreventionContainerInitializer.java
class ClassLoaderLeakPreventionContainerInitializer (line 13) | public class ClassLoaderLeakPreventionContainerInitializer implements ja...
method onStartup (line 16) | @Override
FILE: classloader-leak-test-framework/src/main/java/se/jiderhamn/HeapDumper.java
class HeapDumper (line 14) | public class HeapDumper {
method dumpHeap (line 30) | public static void dumpHeap(File file, boolean live) throws ClassNotFo...
method getHotSpotDiagnosticMBean (line 46) | private static HotSpotDiagnosticMXBean getHotSpotDiagnosticMBean() {
FILE: classloader-leak-test-framework/src/main/java/se/jiderhamn/classloader/RedefiningClassLoader.java
class RedefiningClassLoader (line 7) | public class RedefiningClassLoader extends org.apache.bcel.util.ClassLoa...
method RedefiningClassLoader (line 25) | public RedefiningClassLoader(ClassLoader parent) {
method RedefiningClassLoader (line 29) | public RedefiningClassLoader() {
method RedefiningClassLoader (line 33) | public RedefiningClassLoader(ClassLoader parent, String name) {
method RedefiningClassLoader (line 37) | RedefiningClassLoader(String name) {
method RedefiningClassLoader (line 41) | public RedefiningClassLoader(ClassLoader parent, String name, String[]...
method RedefiningClassLoader (line 47) | RedefiningClassLoader(String name, String[] ignoredPackages) {
method isDebugLoggingEnabled (line 53) | public static boolean isDebugLoggingEnabled() {
method modifyClass (line 57) | @Override
method markAsZombie (line 66) | public void markAsZombie() {
method toString (line 70) | @Override
method loadClass (line 76) | @Override
FILE: classloader-leak-test-framework/src/main/java/se/jiderhamn/classloader/ZombieMarker.java
class ZombieMarker (line 8) | public class ZombieMarker {
FILE: classloader-leak-test-framework/src/main/java/se/jiderhamn/classloader/leak/JUnitClassloaderRunner.java
class JUnitClassloaderRunner (line 27) | public class JUnitClassloaderRunner extends BlockJUnit4ClassRunner {
method JUnitClassloaderRunner (line 32) | public JUnitClassloaderRunner(Class<?> klass) throws InitializationErr...
method methodInvoker (line 37) | @Override
class SeparateClassLoaderInvokeMethod (line 46) | private class SeparateClassLoaderInvokeMethod extends InvokeMethod {
method SeparateClassLoaderInvokeMethod (line 69) | private SeparateClassLoaderInvokeMethod(FrameworkMethod testMethod, ...
method SeparateClassLoaderInvokeMethod (line 73) | private SeparateClassLoaderInvokeMethod(FrameworkMethod testMethod, ...
method evaluate (line 91) | @SuppressWarnings("UnusedAssignment")
method performErrorActions (line 203) | private void performErrorActions(String testName) throws Interrupted...
method forceGc (line 215) | public static void forceGc(int n) {
method forceGc (line 222) | public static void forceGc() {
method waitForHeapDump (line 229) | private static void waitForHeapDump() throws InterruptedException {
method dumpHeap (line 236) | private void dumpHeap(String testName) {
method getSurefireReportsDirectory (line 252) | private File getSurefireReportsDirectory() {
method getSurefireReportsDirectory (line 259) | private static File getSurefireReportsDirectory(final Class<?> clazz) {
method appendArrays (line 291) | private <T> T[] appendArrays(T[] arr1, T[] arr2) {
FILE: classloader-leak-test-framework/src/test/java/com/classloader/test/CustomClass.java
class CustomClass (line 3) | public class CustomClass {
FILE: classloader-leak-test-framework/src/test/java/se/jiderhamn/classloader/RedefiningClassLoaderTest.java
class RedefiningClassLoaderTest (line 11) | @RunWith(JUnitClassloaderRunner.class)
method getPackage (line 14) | @Test
FILE: classloader-leak-test-framework/src/test/java/se/jiderhamn/classloader/leak/JUnitClassloaderRunnerTest.java
class JUnitClassloaderRunnerTest (line 13) | @RunWith(JUnitClassloaderRunner.class)
method throwException (line 16) | @Test(expected = RuntimeException.class) // FileNotFoundException repl...
method throwAssertionError (line 22) | @Test(expected = RuntimeException.class) // CustomError replaced by Ru...
class CustomError (line 28) | public static class CustomError extends Error {
FILE: classloader-leak-test-framework/src/test/java/se/jiderhamn/classloader/leak/NonLeakingTest.java
class NonLeakingTest (line 10) | @RunWith(JUnitClassloaderRunner.class)
method nonLeakingMethod (line 13) | @Test
FILE: classloader-leak-test-framework/src/test/java/se/jiderhamn/classloader/leak/accused/CustomThreadLocalTest.java
class CustomThreadLocalTest (line 11) | @RunWith(JUnitClassloaderRunner.class)
method initialValue (line 16) | @Override
method setValueOfCustomThreadLocal (line 25) | @Test
FILE: classloader-leak-test-framework/src/test/java/se/jiderhamn/classloader/leak/known/CustomThreadLocalCustomValueTest.java
class CustomThreadLocalCustomValueTest (line 10) | @RunWith(JUnitClassloaderRunner.class)
method initialValue (line 15) | @Override
method setCustomThreadLocalValue (line 21) | @Test
class Value (line 27) | private static class Value {
FILE: classloader-leak-test-framework/src/test/java/se/jiderhamn/classloader/leak/known/JEditorPaneTest.java
class JEditorPaneTest (line 13) | @RunWith(JUnitClassloaderRunner.class)
method triggerJEditorPaneLeak (line 16) | @Test
Condensed preview — 153 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (373K chars).
[
{
"path": ".gitignore",
"chars": 68,
"preview": "target\n.classpath\n.project\n.settings\n.mvn/wrapper/maven-wrapper.jar\n"
},
{
"path": ".mvn/wrapper/MavenWrapperDownloader.java",
"chars": 6127,
"preview": "/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOT"
},
{
"path": ".mvn/wrapper/maven-wrapper.properties",
"chars": 1019,
"preview": "# Licensed to the Apache Software Foundation (ASF) under one\n# or more contributor license agreements. See the NOTICE f"
},
{
"path": ".travis.yml",
"chars": 409,
"preview": "dist: trusty\n\nlanguage: java\n\njdk:\n - oraclejdk8\n - openjdk11\n\n# Uncomment to upload heapdumps to S3 bucket; https://d"
},
{
"path": "LICENSE.txt",
"chars": 11358,
"preview": "\n Apache License\n Version 2.0, January 2004\n "
},
{
"path": "README.md",
"chars": 5238,
"preview": "# Classloader Leak Prevention library\n[ that"
},
{
"path": "classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/package-info.java",
"chars": 263,
"preview": "package se.jiderhamn.classloader.leak.prevention;\n\n/**\n * Test cases in this package are used to confirm a) that a leak "
},
{
"path": "classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/preinit/AwtToolkitInitiatorTest.java",
"chars": 312,
"preview": "package se.jiderhamn.classloader.leak.prevention.preinit;\n\nimport org.junit.Ignore;\n\n/**\n * Test case for {@link AwtTool"
},
{
"path": "classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/preinit/DatatypeConverterImplInitiatorTest.java",
"chars": 2105,
"preview": "package se.jiderhamn.classloader.leak.prevention.preinit;\n\nimport java.math.BigDecimal;\nimport java.math.BigInteger;\nimp"
},
{
"path": "classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/preinit/DocumentBuilderFactoryInitiatorTest.java",
"chars": 349,
"preview": "package se.jiderhamn.classloader.leak.prevention.preinit;\n\nimport org.junit.Ignore;\n\n/**\n * Test cases for {@link Docume"
},
{
"path": "classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/preinit/Java2dDisposerInitiatorTest.java",
"chars": 325,
"preview": "package se.jiderhamn.classloader.leak.prevention.preinit;\n\nimport org.junit.Ignore;\n\n/**\n * Test cases for {@link Java2d"
},
{
"path": "classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/preinit/Java2dRenderQueueInitiatorTest.java",
"chars": 235,
"preview": "package se.jiderhamn.classloader.leak.prevention.preinit;\n\n/**\n * Test cases for {@link Java2dRenderQueueInitiator}\n */\n"
},
{
"path": "classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/preinit/JavaxSecurityLoginConfigurationInitiatorTest.java",
"chars": 376,
"preview": "package se.jiderhamn.classloader.leak.prevention.preinit;\n\nimport org.junit.Ignore;\n\n/**\n * Test cases for {@link JavaxS"
},
{
"path": "classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/preinit/JdbcDriversInitiatorTest.java",
"chars": 309,
"preview": "package se.jiderhamn.classloader.leak.prevention.preinit;\n\nimport org.junit.Ignore;\n\n/**\n * Test cases for {@link JdbcDr"
},
{
"path": "classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/preinit/LdapPoolManagerInitiatorTest.java",
"chars": 433,
"preview": "package se.jiderhamn.classloader.leak.prevention.preinit;\n\nimport org.junit.Before;\n\n/**\n * Test cases for {@link LdapPo"
},
{
"path": "classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/preinit/OracleJdbcThreadInitiatorTest.java",
"chars": 500,
"preview": "package se.jiderhamn.classloader.leak.prevention.preinit;\n\nimport org.junit.Ignore;\nimport se.jiderhamn.classloader.Pack"
},
{
"path": "classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/preinit/PreClassLoaderInitiatorTestBase.java",
"chars": 1638,
"preview": "package se.jiderhamn.classloader.leak.prevention.preinit;\n\nimport org.junit.FixMethodOrder;\nimport org.junit.Test;\nimpor"
},
{
"path": "classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/preinit/ReplaceDOMNormalizerSerializerAbortExceptionInitiatorTest.java",
"chars": 1681,
"preview": "package se.jiderhamn.classloader.leak.prevention.preinit;\n\nimport javax.xml.parsers.DocumentBuilderFactory;\nimport javax"
},
{
"path": "classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/preinit/SecurityPolicyInitiatorTest.java",
"chars": 325,
"preview": "package se.jiderhamn.classloader.leak.prevention.preinit;\n\nimport org.junit.Ignore;\n\n/**\n * Test cases for {@link Securi"
},
{
"path": "classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/preinit/SecurityProvidersInitiatorTest.java",
"chars": 328,
"preview": "package se.jiderhamn.classloader.leak.prevention.preinit;\n\nimport org.junit.Ignore;\n\n/**\n * Test cases for {@link Securi"
},
{
"path": "classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/preinit/SunAwtAppContextInitiatorTest.java",
"chars": 261,
"preview": "package se.jiderhamn.classloader.leak.prevention.preinit;\n\n/**\n * Test cases for {@link SunAwtAppContextInitiator}\n * @a"
},
{
"path": "classloader-leak-prevention/classloader-leak-prevention-core/src/test/java/se/jiderhamn/classloader/leak/prevention/preinit/SunGCInitiatorTest.java",
"chars": 228,
"preview": "package se.jiderhamn.classloader.leak.prevention.preinit;\n\n/**\n * Test cases for {@link SunGCInitiator}\n * @author Matti"
},
{
"path": "classloader-leak-prevention/classloader-leak-prevention-core/src/test/resources/META-INF/services/javax.imageio.spi.ImageInputStreamSpi",
"chars": 79,
"preview": "se.jiderhamn.classloader.leak.prevention.cleanup.ImageIOMockImageInputStreamSPI"
},
{
"path": "classloader-leak-prevention/classloader-leak-prevention-core/src/test/resources/spi-cacert-2008.crt",
"chars": 2854,
"preview": "-----BEGIN CERTIFICATE-----\nMIIIDjCCBfagAwIBAgIJAOiOtsn4KhQoMA0GCSqGSIb3DQEBBQUAMIG8MQswCQYD\nVQQGEwJVUzEQMA4GA1UECBMHSW5"
},
{
"path": "classloader-leak-prevention/classloader-leak-prevention-servlet/pom.xml",
"chars": 1241,
"preview": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocat"
},
{
"path": "classloader-leak-prevention/classloader-leak-prevention-servlet/src/main/java/se/jiderhamn/classloader/leak/prevention/ClassLoaderLeakPreventorListener.java",
"chars": 12775,
"preview": "/*\n Copyright 2012 Mattias Jiderhamn\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may no"
},
{
"path": "classloader-leak-prevention/classloader-leak-prevention-servlet3/pom.xml",
"chars": 1462,
"preview": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocat"
},
{
"path": "classloader-leak-prevention/classloader-leak-prevention-servlet3/src/main/java/se/jiderhamn/classloader/leak/prevention/ClassLoaderLeakPreventionContainerInitializer.java",
"chars": 1776,
"preview": "package se.jiderhamn.classloader.leak.prevention;\n\nimport java.util.Set;\nimport javax.servlet.*;\n\n/**\n * Servlet 3.0 {@l"
},
{
"path": "classloader-leak-prevention/classloader-leak-prevention-servlet3/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer",
"chars": 86,
"preview": "se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventionContainerInitializer"
},
{
"path": "classloader-leak-prevention/classloader-leak-prevention-servlet3/src/main/resources/META-INF/web-fragment.xml",
"chars": 794,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<web-fragment xmlns=\"http://java.sun.com/xml/ns/javaee\"\n xmlns:xsi=\""
},
{
"path": "classloader-leak-prevention/pom.xml",
"chars": 6099,
"preview": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocat"
},
{
"path": "classloader-leak-test-framework/README.md",
"chars": 3973,
"preview": "# Classloader Leak test framework\n\nStand-alone test framework for detecting and/or verifying the existence or non-existe"
},
{
"path": "classloader-leak-test-framework/pom.xml",
"chars": 5143,
"preview": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocat"
},
{
"path": "classloader-leak-test-framework/src/main/java/se/jiderhamn/HeapDumper.java",
"chars": 2051,
"preview": "package se.jiderhamn;\n\nimport java.io.File;\nimport java.lang.management.ManagementFactory;\nimport javax.management.MBean"
},
{
"path": "classloader-leak-test-framework/src/main/java/se/jiderhamn/classloader/PackagesLoadedOutsideClassLoader.java",
"chars": 1028,
"preview": "package se.jiderhamn.classloader;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimpor"
},
{
"path": "classloader-leak-test-framework/src/main/java/se/jiderhamn/classloader/RedefiningClassLoader.java",
"chars": 2928,
"preview": "package se.jiderhamn.classloader;\n\nimport org.apache.bcel.classfile.ClassFormatException;\nimport org.apache.bcel.classfi"
},
{
"path": "classloader-leak-test-framework/src/main/java/se/jiderhamn/classloader/ZombieMarker.java",
"chars": 199,
"preview": "package se.jiderhamn.classloader;\n\n/**\n * Class used to help identify leaked class loaders in a heap dump.\n * Inspired b"
},
{
"path": "classloader-leak-test-framework/src/main/java/se/jiderhamn/classloader/leak/JUnitClassloaderRunner.java",
"chars": 12718,
"preview": "package se.jiderhamn.classloader.leak;\n\nimport java.io.File;\nimport java.lang.ref.WeakReference;\nimport java.lang.reflec"
},
{
"path": "classloader-leak-test-framework/src/main/java/se/jiderhamn/classloader/leak/LeakPreventor.java",
"chars": 301,
"preview": "package se.jiderhamn.classloader.leak;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPoli"
},
{
"path": "classloader-leak-test-framework/src/main/java/se/jiderhamn/classloader/leak/Leaks.java",
"chars": 802,
"preview": "package se.jiderhamn.classloader.leak;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPoli"
},
{
"path": "classloader-leak-test-framework/src/test/java/com/classloader/test/CustomClass.java",
"chars": 60,
"preview": "package com.classloader.test;\n\npublic class CustomClass {\n}\n"
},
{
"path": "classloader-leak-test-framework/src/test/java/se/jiderhamn/classloader/RedefiningClassLoaderTest.java",
"chars": 654,
"preview": "package se.jiderhamn.classloader;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport se.jiderhamn.classload"
},
{
"path": "classloader-leak-test-framework/src/test/java/se/jiderhamn/classloader/leak/JUnitClassloaderRunnerTest.java",
"chars": 847,
"preview": "package se.jiderhamn.classloader.leak;\n\nimport java.io.FileNotFoundException;\n\nimport org.junit.Test;\nimport org.junit.r"
},
{
"path": "classloader-leak-test-framework/src/test/java/se/jiderhamn/classloader/leak/NonLeakingTest.java",
"chars": 400,
"preview": "package se.jiderhamn.classloader.leak;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\n/**\n * Test that isn't "
},
{
"path": "classloader-leak-test-framework/src/test/java/se/jiderhamn/classloader/leak/accused/CustomThreadLocalTest.java",
"chars": 838,
"preview": "package se.jiderhamn.classloader.leak.accused;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport se.jiderh"
},
{
"path": "classloader-leak-test-framework/src/test/java/se/jiderhamn/classloader/leak/accused/package-info.java",
"chars": 182,
"preview": "package se.jiderhamn.classloader.leak.accused;\n\n/* Test cases in this package are used to confirm that code accused of c"
},
{
"path": "classloader-leak-test-framework/src/test/java/se/jiderhamn/classloader/leak/known/CustomThreadLocalCustomValueTest.java",
"chars": 784,
"preview": "package se.jiderhamn.classloader.leak.known;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport se.jiderham"
},
{
"path": "classloader-leak-test-framework/src/test/java/se/jiderhamn/classloader/leak/known/JEditorPaneTest.java",
"chars": 455,
"preview": "package se.jiderhamn.classloader.leak.known;\n\nimport javax.swing.*;\n\nimport org.junit.Test;\nimport org.junit.runner.RunW"
},
{
"path": "classloader-leak-test-framework/src/test/java/se/jiderhamn/classloader/leak/known/package-info.java",
"chars": 128,
"preview": "package se.jiderhamn.classloader.leak.known;\n\n/** Test cases in this package are used to confirm the existence of known "
},
{
"path": "mvnw",
"chars": 9781,
"preview": "#!/bin/sh\n# ----------------------------------------------------------------------------\n# Licensed to the Apache Softwa"
},
{
"path": "mvnw.cmd",
"chars": 6702,
"preview": "@REM ----------------------------------------------------------------------------\n@REM Licensed to the Apache Software F"
},
{
"path": "pom.xml",
"chars": 773,
"preview": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocat"
}
]
// ... and 1 more files (download for full content)
About this extraction
This page contains the full source code of the mjiderhamn/classloader-leak-prevention GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 153 files (329.3 KB), approximately 87.3k tokens, and a symbol index with 488 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.