Logging swallowed OSGi exception in debug mode.
Added pretty printed version so it's easier to see what's going on.
Signed-off-by: arjantijms <arjan.tijms@gmail.com>
diff --git a/nucleus/admin/launcher/src/main/java/com/sun/enterprise/admin/launcher/GFLauncher.java b/nucleus/admin/launcher/src/main/java/com/sun/enterprise/admin/launcher/GFLauncher.java
index 186a974..d023f9c 100644
--- a/nucleus/admin/launcher/src/main/java/com/sun/enterprise/admin/launcher/GFLauncher.java
+++ b/nucleus/admin/launcher/src/main/java/com/sun/enterprise/admin/launcher/GFLauncher.java
@@ -30,6 +30,7 @@
import static com.sun.enterprise.universal.process.ProcessStreamDrainer.redirect;
import static com.sun.enterprise.universal.process.ProcessStreamDrainer.save;
import static com.sun.enterprise.util.OS.isDarwin;
+import static com.sun.enterprise.util.SystemPropertyConstants.DEBUG_MODE_PROPERTY;
import static com.sun.enterprise.util.SystemPropertyConstants.DROP_INTERRUPTED_COMMANDS;
import static com.sun.enterprise.util.SystemPropertyConstants.INSTALL_ROOT_PROPERTY;
import static com.sun.enterprise.util.SystemPropertyConstants.INSTANCE_ROOT_PROPERTY;
@@ -37,6 +38,8 @@
import static com.sun.enterprise.util.io.FileUtils.copyFile;
import static java.lang.Boolean.TRUE;
import static java.util.Collections.emptyList;
+import static java.util.logging.Level.INFO;
+import static java.util.logging.Level.WARNING;
import static java.util.stream.Collectors.toList;
import java.io.BufferedWriter;
@@ -52,7 +55,6 @@
import java.util.Locale;
import java.util.Map;
import java.util.Set;
-import java.util.logging.Level;
import com.sun.enterprise.universal.glassfish.ASenvPropertyReader;
import com.sun.enterprise.universal.glassfish.GFLauncherUtils;
@@ -766,10 +768,13 @@
addIgnoreNull(cmdLine, domainXMLjavaConfigDebugOptions);
String CLIStartTime = System.getProperty("WALL_CLOCK_START");
-
if (CLIStartTime != null && CLIStartTime.length() > 0) {
cmdLine.add("-DWALL_CLOCK_START=" + CLIStartTime);
}
+
+ if (debugPort >= 0) {
+ cmdLine.add("-D" + DEBUG_MODE_PROPERTY + "=" + TRUE);
+ }
if (domainXMLjvmOptions != null) {
addIgnoreNull(cmdLine, domainXMLjvmOptions.toList());
@@ -1029,7 +1034,8 @@
Map<String, String> props = new HashMap<>();
props.put(INSTALL_ROOT_PROPERTY, getInfo().getInstallDir().getAbsolutePath());
props.put(INSTANCE_ROOT_PROPERTY, getInfo().getInstanceRootDir().getAbsolutePath());
- return this.propsToJvmOptions(props);
+
+ return propsToJvmOptions(props);
}
String getClasspath() {
@@ -1061,9 +1067,9 @@
private void setupLogLevels() {
if (callerParameters.isVerbose()) {
- GFLauncherLogger.setConsoleLevel(Level.INFO);
+ GFLauncherLogger.setConsoleLevel(INFO);
} else {
- GFLauncherLogger.setConsoleLevel(Level.WARNING);
+ GFLauncherLogger.setConsoleLevel(WARNING);
}
}
diff --git a/nucleus/common/common-util/src/main/java/com/sun/enterprise/util/FelixPrettyPrinter.java b/nucleus/common/common-util/src/main/java/com/sun/enterprise/util/FelixPrettyPrinter.java
new file mode 100644
index 0000000..6cb5421
--- /dev/null
+++ b/nucleus/common/common-util/src/main/java/com/sun/enterprise/util/FelixPrettyPrinter.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2008, 2020 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+package com.sun.enterprise.util;
+
+public class FelixPrettyPrinter {
+
+ /**
+ * Prints exception messages from Felix bundle classloading in a more human readable way.
+ *
+ * @param message
+ * @return
+ */
+ public static String prettyPrintExceptionMessage(String message) {
+ StringBuilder messageBuilder = new StringBuilder();
+ int index = message.indexOf("Unable to resolve");
+ int indent = 0;
+ while (index >= 0) {
+ printLn(messageBuilder, indent, "Unable to resolve");
+ index += "Unable to resolve".length();
+
+ int index2 = message.indexOf("missing requirement", index);
+ if (index2 >= 0) {
+
+ indent++;
+
+ // Module name would be e.g.
+ // org.glassfish.server.internal.batch.glassfish-batch-connector [103](R 103.0):
+ String module = message.substring(index, index2);
+
+ // Remove the duplicate number
+ if (module.contains("(R")) {
+ module = module.substring(0, module.indexOf("(R"));
+ }
+
+ printLn(messageBuilder, indent, module);
+ printLn(messageBuilder, indent, "missing requirement");
+
+ index = index2 + "missing requirement".length();
+
+ // In GlassFish and in a classloader the search is always for package, so we can
+ // use that as a delimiter here
+ index = message.indexOf("osgi.wiring.package; ", index);
+ if (index >= 0) {
+
+ indent++;
+
+ // Remainder of input now looks like this:
+
+ // osgi.wiring.package; (&(osgi.wiring.package=org.glassfish.grizzly)(version>=2.4.0)(!(version>=3.0.0)))
+
+ // Skip over "osgi.wiring.package; ", we're always searching for this so
+ // no need to print it.
+ index += "osgi.wiring.package; ".length();
+
+ // Now extracting this:
+ // "(&(osgi.wiring.package=org.glassfish.grizzly)(version>=2.4.0)(!(version>=3.0.0)))"
+ index2 = message.indexOf(" ", index);
+
+ String packageAndVersion = message.substring(index, index2);
+
+ // Make it a little less "cramped"
+ // "(&(package=org.glassfish.grizzly) (version>=2.4.0) (!(version>=3.0.0)))"
+ packageAndVersion = packageAndVersion.replace("osgi.wiring.package", "package");
+ packageAndVersion = packageAndVersion.replace(")(", ") (");
+ packageAndVersion = packageAndVersion.replace("=", " = ");
+ packageAndVersion = packageAndVersion.replace("> =", " >=");
+ packageAndVersion = packageAndVersion.replace("< =", " <=");
+
+ // Remove outer braces
+ // "&(package=org.glassfish.grizzly) (version>=2.4.0) (!(version>=3.0.0))"
+ if (packageAndVersion.startsWith("(")) {
+ packageAndVersion = packageAndVersion.substring(1);
+ }
+ if (packageAndVersion.endsWith(")")) {
+ packageAndVersion = packageAndVersion.substring(0, packageAndVersion.length()-1);
+ }
+
+ printLn(messageBuilder, indent, packageAndVersion);
+
+ // If there's a "caused by:", print it and increase the indent
+ index = message.indexOf("caused by: ", index2);
+ if (index >= 0) {
+
+ printLn(messageBuilder, indent, "caused by:");
+
+ indent++;
+ index += "caused by: ".length();
+ }
+ }
+
+ }
+
+ index = index2;
+
+ index = message.indexOf("Unable to resolve", index);
+ }
+
+ return messageBuilder.toString();
+ }
+
+ private static void printLn(StringBuilder messageBuilder, int indent, String message) {
+ for (int i=0; i<(indent * 4); i++) {
+ messageBuilder.append(" ");
+ }
+
+ messageBuilder.append(message.trim())
+ .append("\n");
+ }
+
+ public static void main(String[] args) {
+ String test = "Caused by: org.osgi.framework.BundleException: Unable to resolve org.glassfish.server.internal.batch.glassfish-batch-connector [103](R 103.0): missing requirement [org.glassfish.server.internal.batch.glassfish-batch-connector [103](R 103.0)] osgi.wiring.package; (osgi.wiring.package=com.ibm.jbatch.spi) [caused by: Unable to resolve org.glassfish.server.internal.batch.payara-jbatch [325](R 325.0): missing requirement [org.glassfish.server.internal.batch.payara-jbatch [325](R 325.0)] osgi.wiring.package; (osgi.wiring.package=org.glassfish.weld) [caused by: Unable to resolve org.glassfish.server.internal.web.weld-integration [395](R 395.0): missing requirement [org.glassfish.server.internal.web.weld-integration [395](R 395.0)] osgi.wiring.package; (&(osgi.wiring.package=com.sun.faces.spi)(version>=2.3.0)(!(version>=3.0.0)))]] Unresolved requirements: [[org.glassfish.server.internal.batch.glassfish-batch-connector [103](R 103.0)] osgi.wiring.package; (osgi.wiring.package=com.ibm.jbatch.spi)]";
+
+ String test1 = prettyPrintExceptionMessage(test);
+
+ String test2 = prettyPrintExceptionMessage(" (java.lang.String) Unable to resolve org.glassfish.jersey.containers.jersey-container-grizzly2-http [142](R 142.0): missing requirement [org.glassfish.jersey.containers.jersey-container-grizzly2-http [142](R 142.0)] osgi.wiring.package; (&(osgi.wiring.package=org.glassfish.grizzly)(version>=2.4.0)(!(version>=3.0.0))) Unresolved requirements: [[org.glassfish.jersey.containers.jersey-container-grizzly2-http [142](R 142.0)] osgi.wiring.package; (&(osgi.wiring.package=org.glassfish.grizzly)(version>=2.4.0)(!(version>=3.0.0)))]");
+
+ System.out.println(test1);
+ System.out.println("\n\n");
+ System.out.println(test2);
+ }
+
+}
diff --git a/nucleus/common/common-util/src/main/java/com/sun/enterprise/util/SystemPropertyConstants.java b/nucleus/common/common-util/src/main/java/com/sun/enterprise/util/SystemPropertyConstants.java
index 19393dc..c153239 100644
--- a/nucleus/common/common-util/src/main/java/com/sun/enterprise/util/SystemPropertyConstants.java
+++ b/nucleus/common/common-util/src/main/java/com/sun/enterprise/util/SystemPropertyConstants.java
@@ -98,6 +98,11 @@
* Field
*/
public static final String INSTANCE_ROOT_PROPERTY = "com.sun.aas.instanceRoot";
+
+ /**
+ * Field
+ */
+ public static final String DEBUG_MODE_PROPERTY = "com.sun.aas.debugMode";
/**
* The certificate nick name specified in the System-Jmx-Conenctor of the DAS with which a Node Agent synchronizes
diff --git a/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/server/APIClassLoaderServiceImpl.java b/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/server/APIClassLoaderServiceImpl.java
index 2b481f2..fa76156 100644
--- a/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/server/APIClassLoaderServiceImpl.java
+++ b/nucleus/core/kernel/src/main/java/com/sun/enterprise/v3/server/APIClassLoaderServiceImpl.java
@@ -16,8 +16,11 @@
package com.sun.enterprise.v3.server;
+import static com.sun.enterprise.util.FelixPrettyPrinter.prettyPrintExceptionMessage;
+import static com.sun.enterprise.util.SystemPropertyConstants.DEBUG_MODE_PROPERTY;
import static java.util.Collections.enumeration;
import static java.util.logging.Level.FINE;
+import static java.util.logging.Level.SEVERE;
import java.io.IOException;
import java.net.URL;
@@ -85,11 +88,15 @@
private HK2Module APIModule;
/*
- * Implementation Note: 1. This class currently depends on a special which is configured such that it can load all
- * public APIs. The APIClassLoader is a wrapper around such a module's loader. This is how we are indepdendent of actual
+ * Implementation Note:
+ *
+ * 1. This class currently depends on a special which is configured such that it can load all
+ * public APIs. The APIClassLoader is a wrapper around such a module's loader. This is how we are independent of actual
* module system like OSGi. So far it has worked when we run in OSGi mode as well as when we run in a single classpath
- * mode. 2. APIClassLoader maintains a blacklist, i.e., classes and resources that could not be loaded to avoid
- * unnecessary delegation. It flushes that list everytime a new bundle is installed in the system. This takes care of
+ * mode.
+ *
+ * 2. APIClassLoader maintains a blacklist, i.e., classes and resources that could not be loaded to avoid
+ * unnecessary delegation. It flushes that list every time a new bundle is installed in the system. This takes care of
* performance problem in typical production use of GlassFish.
*
* TODO: 1. We need to add an upper threshold for blacklist to avoid excessive use of memory. 2. Externalize punch-in
@@ -208,6 +215,8 @@
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
+
+
@Override
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
@@ -232,7 +241,29 @@
if (classProvidingModule != null) {
if (select(classProvidingModule)) {
- return classProvidingModule.getClassLoader().loadClass(name); // abort search if we fail to load.
+ try {
+ return classProvidingModule.getClassLoader().loadClass(name); // abort search if we fail to load.
+ } catch (ClassNotFoundException cnfe2) {
+
+ // Only in debug mode, since the child classloader can legitimately try other souces to load a class
+ if ((Boolean.valueOf(System.getProperty(DEBUG_MODE_PROPERTY)))) {
+ logger.logp(SEVERE, "APIClassLoaderServiceImpl$APIClassLoader", "loadClass", name, cnfe2);
+
+ if (cnfe2.getCause() != null) {
+ String message = prettyPrintExceptionMessage(cnfe2.getCause().getMessage());
+
+ if (message != null && !message.isEmpty()) {
+ logger.logp(SEVERE, "APIClassLoaderServiceImpl$APIClassLoader", "loadClass",
+
+ "\nFailed loading class " + name + " by API Class Loader\n\n" +
+ message + "\n");
+ }
+ }
+ }
+
+ throw cnfe2;
+
+ }
} else {
logger.logp(FINE, "APIClassLoaderServiceImpl$APIClassLoader", "loadClass",
"Skipping loading {0} from module {1} as this module is not yet resolved.", new Object[] { name, classProvidingModule });
@@ -286,11 +317,11 @@
if (name.equals(MAILCAP)) {
// punch in for META-INF/mailcap files.
// see issue #8426
- for (HK2Module m : modulesRegistry.getModules()) {
- if (!select(m)) {
+ for (HK2Module module : modulesRegistry.getModules()) {
+ if (!select(module)) {
continue;
}
- if ((url = m.getClassLoader().getResource(name)) != null) {
+ if ((url = module.getClassLoader().getResource(name)) != null) {
return url;
}
}