Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix ClassNotFoundException when running JettyLauncher with Java 9+ #9822

Merged
merged 1 commit into from
Apr 12, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 36 additions & 56 deletions dev/core/src/com/google/gwt/dev/shell/jetty/JettyLauncher.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.preventers.AppContextLeakPreventer;
import org.eclipse.jetty.util.preventers.DOMLeakPreventer;
import org.eclipse.jetty.util.preventers.GCThreadLeakPreventer;
import org.eclipse.jetty.util.preventers.SecurityProviderLeakPreventer;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.webapp.ClasspathPattern;
import org.eclipse.jetty.webapp.Configuration;
Expand All @@ -60,10 +64,6 @@
import java.util.Iterator;
import java.util.List;

import javax.imageio.ImageIO;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

/**
* A {@link ServletContainerLauncher} for an embedded Jetty server.
*/
Expand Down Expand Up @@ -744,6 +744,7 @@ public ServletContainer start(TreeLogger logger, int port, File appRootDir)
ServerConnector connector = getConnector(server, logger);
setupConnector(connector, bindAddress, port);
server.addConnector(connector);
addPreventers(server);

Configuration.ClassList cl = Configuration.ClassList.setServerDefault(server);
try {
Expand Down Expand Up @@ -859,6 +860,37 @@ protected HttpConfiguration defaultConfig() {
return config;
}

private void addPreventers(Server server) {
// Trigger a call to sun.awt.AppContext.getAppContext(). This will
// pin the common class loader in memory but that shouldn't be an
// issue.
server.addBean(new AppContextLeakPreventer());

/*
* Several components end up calling: sun.misc.GC.requestLatency(long)
*
* Those libraries / components known to trigger memory leaks due to
* eventual calls to requestLatency(long) are:
* - javax.management.remote.rmi.RMIConnectorServer.start()
*/
server.addBean(new GCThreadLeakPreventer());

/*
* Creating a MessageDigest during web application startup initializes the
* Java Cryptography Architecture. Under certain conditions this starts a
* Token poller thread with TCCL equal to the web application class loader.
*
* Instead we initialize JCA right now.
*/
server.addBean(new SecurityProviderLeakPreventer());

/*
* Haven't got to the root of what is going on with this leak but if a web app is the first to
* make the calls below the web application class loader will be pinned in memory.
*/
server.addBean(new DOMLeakPreventer());
}

private void checkStartParams(TreeLogger logger, int port, File appRootDir) {
if (logger == null) {
throw new NullPointerException("logger cannot be null");
Expand Down Expand Up @@ -899,37 +931,6 @@ private TreeLogger.Type getBaseLogLevel() {
* (http://www.apache.org/).
*/
private void jreLeakPrevention(TreeLogger logger) {
// Trigger a call to sun.awt.AppContext.getAppContext(). This will
// pin the common class loader in memory but that shouldn't be an
// issue.
ImageIO.getCacheDirectory();

/*
* Several components end up calling: sun.misc.GC.requestLatency(long)
*
* Those libraries / components known to trigger memory leaks due to
* eventual calls to requestLatency(long) are: -
* javax.management.remote.rmi.RMIConnectorServer.start()
*/
try {
Class<?> clazz = Class.forName("sun.misc.GC");
Method method = clazz.getDeclaredMethod("requestLatency",
new Class[]{long.class});
method.invoke(null, Long.valueOf(3600000));
} catch (ClassNotFoundException e) {
logger.log(TreeLogger.ERROR, "jreLeakPrevention.gcDaemonFail", e);
} catch (SecurityException e) {
logger.log(TreeLogger.ERROR, "jreLeakPrevention.gcDaemonFail", e);
} catch (NoSuchMethodException e) {
logger.log(TreeLogger.ERROR, "jreLeakPrevention.gcDaemonFail", e);
} catch (IllegalArgumentException e) {
logger.log(TreeLogger.ERROR, "jreLeakPrevention.gcDaemonFail", e);
} catch (IllegalAccessException e) {
logger.log(TreeLogger.ERROR, "jreLeakPrevention.gcDaemonFail", e);
} catch (InvocationTargetException e) {
logger.log(TreeLogger.ERROR, "jreLeakPrevention.gcDaemonFail", e);
}

/*
* Calling getPolicy retains a static reference to the context class loader.
*/
Expand All @@ -953,15 +954,6 @@ private void jreLeakPrevention(TreeLogger logger) {
logger.log(TreeLogger.WARN, "jreLeakPrevention.authPolicyFail", e);
}

/*
* Creating a MessageDigest during web application startup initializes the
* Java Cryptography Architecture. Under certain conditions this starts a
* Token poller thread with TCCL equal to the web application class loader.
*
* Instead we initialize JCA right now.
*/
java.security.Security.getProviders();

/*
* Several components end up opening JarURLConnections without first
* disabling caching. This effectively locks the file. Whilst more
Expand All @@ -984,17 +976,5 @@ private void jreLeakPrevention(TreeLogger logger) {
} catch (IOException e) {
logger.log(TreeLogger.ERROR, "jreLeakPrevention.jarUrlConnCacheFail", e);
}

/*
* Haven't got to the root of what is going on with this leak but if a web
* app is the first to make the calls below the web application class loader
* will be pinned in memory.
*/
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
try {
factory.newDocumentBuilder();
} catch (ParserConfigurationException e) {
logger.log(TreeLogger.ERROR, "jreLeakPrevention.xmlParseFail", e);
}
}
}