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;
                         }
                     }