This is an automated email from the ASF dual-hosted git repository.

wujimin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/servicecomb-java-chassis.git


The following commit(s) were added to refs/heads/master by this push:
     new b110d7e  [SCB-1179]Optimize the mainclass auto-discovery logic to 
cover more scenes.
b110d7e is described below

commit b110d7efbb38cbc958bc8b6a43a72ca90f8d6487
Author: yangshaoqin <yangshaoq...@huawei.com>
AuthorDate: Fri Mar 1 18:46:28 2019 +0800

    [SCB-1179]Optimize the mainclass auto-discovery logic to cover more scenes.
---
 .../foundation/common/utils/BeanUtils.java         | 32 ++++++++---
 .../foundation/common/utils/JvmUtils.java          | 52 ++++++++++++++---
 .../foundation/common/utils/TestBeanUtils.java     | 14 ++++-
 .../foundation/common/utils/TestJvmUtils.java      | 66 ++++++++++++++++++++--
 4 files changed, 143 insertions(+), 21 deletions(-)

diff --git 
a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/BeanUtils.java
 
b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/BeanUtils.java
index 103ac79..f7f4647 100644
--- 
a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/BeanUtils.java
+++ 
b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/BeanUtils.java
@@ -21,12 +21,17 @@ import java.util.LinkedHashSet;
 import java.util.Set;
 
 import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.aop.TargetClassAware;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.support.ClassPathXmlApplicationContext;
 
 
 public final class BeanUtils {
+
+  private static final Logger LOGGER = 
LoggerFactory.getLogger(BeanUtils.class);
+
   public static final String DEFAULT_BEAN_RESOURCE = 
"classpath*:META-INF/spring/*.bean.xml";
 
   public static final String SCB_SCAN_PACKAGE = "scb-scan-package";
@@ -49,6 +54,16 @@ public final class BeanUtils {
     context = new ClassPathXmlApplicationContext(configLocations);
   }
 
+  private static void addItem(Set<String> set, String item){
+
+    for(String it: set){
+      if(item.startsWith(it)){
+        return;
+      }
+     }
+    set.add(item);
+  }
+
   public static void prepareServiceCombScanPackage() {
     Set<String> scanPackags = new LinkedHashSet<>();
     // add exists settings
@@ -56,25 +71,26 @@ public final class BeanUtils {
     if (exists != null) {
       for (String exist : exists.trim().split(",")) {
         if (!exist.isEmpty()) {
-          scanPackags.add(exist.trim());
+          addItem(scanPackags, exist.trim());
         }
       }
     }
 
     // ensure servicecomb package exist
-    scanPackags.add(SCB_PACKAGE);
+    addItem(scanPackags, SCB_PACKAGE);
 
     // add main class package
-    Class<?> mainClass = JvmUtils.findMainClass();
-    if (mainClass != null) {
-      String pkg = mainClass.getPackage().getName();
-      if (!pkg.startsWith(SCB_PACKAGE)) {
-        scanPackags.add(pkg);
+    for (Class<?> mainClass : new Class<?>[] {JvmUtils.findMainClass(), 
JvmUtils.findMainClassByStackTrace()}) {
+      if (mainClass != null) {
+        String pkg = mainClass.getPackage().getName();
+        addItem(scanPackags, pkg);
       }
     }
 
     // finish
-    System.setProperty(SCB_SCAN_PACKAGE, StringUtils.join(scanPackags, ","));
+    String scbScanPackages = StringUtils.join(scanPackags, ",");
+    System.setProperty(SCB_SCAN_PACKAGE, scbScanPackages);
+    LOGGER.info("Scb scan package list: " + scbScanPackages);
   }
 
   public static ApplicationContext getContext() {
diff --git 
a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/JvmUtils.java
 
b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/JvmUtils.java
index e6315e9..04196d3 100644
--- 
a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/JvmUtils.java
+++ 
b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/JvmUtils.java
@@ -22,6 +22,7 @@ import java.net.URL;
 import java.util.jar.JarFile;
 import java.util.jar.Manifest;
 
+import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -39,24 +40,55 @@ public final class JvmUtils {
 
   /**
    *
-   * @return main class or null, never throw exception
+   * @return main class or null, never throw exception.
+   * Note that this method does not ensure that the scbMainClass can be 
returned correctly in the some scene.
+   */
+  public static Class<?> findMainClassByStackTrace() {
+    String mainClass = null;
+    StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
+    if (stackTrace != null && stackTrace.length > 0) {
+      for (StackTraceElement stackTraceElement : stackTrace) {
+        if ("main".equals(stackTraceElement.getMethodName())) {
+          mainClass = stackTraceElement.getClassName();
+          break;
+        }
+      }
+    }
+    if(StringUtils.isEmpty(mainClass)){
+      LOGGER.info("Can't found main class by stackTrace.");
+      return null;
+    }
+    try {
+      Class<?> cls = Class.forName(mainClass);
+      LOGGER.info("Found main class \"{}\" by stackTrace.", mainClass);
+      return cls;
+    } catch (Throwable e) {
+      LOGGER.warn("\"{}\" is not a valid class.", mainClass, e);
+      return null;
+    }
+  }
+
+  /**
+   *
+   * @return main class or null, never throw exception.
+   * Note that this method does not ensure that the scbMainClass can be 
returned correctly in the some scene,like "mvn spring-boot:run".
    */
   public static Class<?> findMainClass() {
+    //Get the mainClass from the call stack
+    String mainClass = null;
     // 1.run with java -cp ......
     //   command is main class and args
     // 2.run with java -jar ......
     //   command is jar file name and args
     String command = System.getProperty(SUN_JAVA_COMMAND);
-    if (command == null || command.isEmpty()) {
-      return null;
+    if (StringUtils.isNotEmpty(command)) {
+      String mainClassOrJar = command.trim().split(" ")[0];
+      mainClass = readFromJar(mainClassOrJar);
     }
-
-    String mainClassOrJar = command.trim().split(" ")[0];
-    String mainClass = readFromJar(mainClassOrJar);
-    if (mainClass == null || mainClass.isEmpty()) {
+    if(StringUtils.isEmpty(mainClass)){
+      LOGGER.info("Can't found main class by manifest.");
       return null;
     }
-
     try {
       Class<?> cls = Class.forName(mainClass);
       LOGGER.info("Found main class \"{}\".", mainClass);
@@ -78,6 +110,10 @@ public final class JvmUtils {
       URL url = new URL(manifestUri);
       try (InputStream inputStream = url.openStream()) {
         Manifest manifest = new Manifest(inputStream);
+        String startClass = 
manifest.getMainAttributes().getValue("Start-Class");
+        if (StringUtils.isNotEmpty(startClass)) {
+          return startClass;
+        }
         return manifest.getMainAttributes().getValue("Main-Class");
       }
     } catch (Throwable e) {
diff --git 
a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestBeanUtils.java
 
b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestBeanUtils.java
index c9a28c1..70c888e 100644
--- 
a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestBeanUtils.java
+++ 
b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestBeanUtils.java
@@ -16,6 +16,8 @@
  */
 package org.apache.servicecomb.foundation.common.utils;
 
+import java.math.BigDecimal;
+
 import org.aspectj.lang.annotation.Aspect;
 import org.junit.AfterClass;
 import org.junit.Assert;
@@ -69,6 +71,8 @@ public class TestBeanUtils {
       {
         JvmUtils.findMainClass();
         result = null;
+        JvmUtils.findMainClassByStackTrace();
+        result = null;
       }
     };
 
@@ -84,6 +88,8 @@ public class TestBeanUtils {
       {
         JvmUtils.findMainClass();
         result = TestBeanUtils.class;
+        JvmUtils.findMainClassByStackTrace();
+        result = TestBeanUtils.class;
       }
     };
 
@@ -99,12 +105,14 @@ public class TestBeanUtils {
       {
         JvmUtils.findMainClass();
         result = String.class;
+        JvmUtils.findMainClassByStackTrace();
+        result = BigDecimal.class;
       }
     };
 
     BeanUtils.prepareServiceCombScanPackage();
 
-    Assert.assertEquals("org.apache.servicecomb,java.lang", 
System.getProperty(BeanUtils.SCB_SCAN_PACKAGE));
+    Assert.assertEquals("org.apache.servicecomb,java.lang,java.math", 
System.getProperty(BeanUtils.SCB_SCAN_PACKAGE));
   }
 
   @Test
@@ -114,6 +122,8 @@ public class TestBeanUtils {
       {
         JvmUtils.findMainClass();
         result = null;
+        JvmUtils.findMainClassByStackTrace();
+        result = null;
       }
     };
 
@@ -129,6 +139,8 @@ public class TestBeanUtils {
       {
         JvmUtils.findMainClass();
         result = TestBeanUtils.class;
+        JvmUtils.findMainClassByStackTrace();
+        result = TestBeanUtils.class;
       }
     };
     BeanUtils.init();
diff --git 
a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestJvmUtils.java
 
b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestJvmUtils.java
index 1d6630f..890ea21 100644
--- 
a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestJvmUtils.java
+++ 
b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestJvmUtils.java
@@ -33,6 +33,7 @@ import org.powermock.api.mockito.PowerMockito;
 import org.powermock.core.classloader.annotations.PrepareForTest;
 import org.powermock.modules.junit4.PowerMockRunner;
 
+
 @RunWith(PowerMockRunner.class)
 @PrepareForTest({JvmUtils.class})
 public class TestJvmUtils {
@@ -84,16 +85,16 @@ public class TestJvmUtils {
 
   @Test
   public void findMainClass_jar_normal() throws Exception {
-    String content = String.format("Manifest-Version: 1.0\nMain-Class: %s\n", 
TestJvmUtils.class.getName());
-    InputStream inputStream = new ByteArrayInputStream(content.getBytes());
 
     URL url = PowerMockito.mock(URL.class);
 
     String command = "a.jar";
     String manifestUri = "jar:file:" + new File(command).getAbsolutePath() + 
"!/" + JarFile.MANIFEST_NAME;
-
     PowerMockito.whenNew(URL.class).withParameterTypes(String.class)
         .withArguments(manifestUri).thenReturn(url);
+
+    String content = String.format("Manifest-Version: 1.0\nMain-Class: %s\n", 
TestJvmUtils.class.getName());
+    InputStream inputStream = new ByteArrayInputStream(content.getBytes());
     PowerMockito.when(url.openStream()).thenReturn(inputStream);
 
     System.setProperty(JvmUtils.SUN_JAVA_COMMAND, command + " arg");
@@ -123,8 +124,8 @@ public class TestJvmUtils {
   @Test
   public void findMainClass_jar_readFailed() throws Exception {
     URL url = PowerMockito.mock(URL.class);
-
     String command = "a.jar";
+
     String manifestUri = "jar:file:" + new File(command).getAbsolutePath() + 
"!/" + JarFile.MANIFEST_NAME;
 
     PowerMockito.whenNew(URL.class).withParameterTypes(String.class)
@@ -135,4 +136,61 @@ public class TestJvmUtils {
 
     Assert.assertNull(JvmUtils.findMainClass());
   }
+
+
+  @Test
+  public void findMainClassByStackTrace_normal() throws Exception{
+    RuntimeException re = PowerMockito.mock(RuntimeException.class);
+    PowerMockito.when(re.getStackTrace()).thenReturn(new StackTraceElement[]{
+        new StackTraceElement("declaring.class.fileName", "methodName", 
"fileName", 100),
+        new StackTraceElement("java.lang.String", "main", "fileName", 120)
+    });
+    
PowerMockito.whenNew(RuntimeException.class).withNoArguments().thenReturn(re);
+
+    Assert.assertEquals(String.class, JvmUtils.findMainClassByStackTrace());
+  }
+
+  @Test
+  public void findMainClassByStackTrace_invalidClass() throws Exception{
+    RuntimeException re = PowerMockito.mock(RuntimeException.class);
+    PowerMockito.when(re.getStackTrace()).thenReturn(new StackTraceElement[]{
+        new StackTraceElement("declaring.class.fileName", "methodName", 
"fileName", 100),
+        new StackTraceElement("InvalidClass", "main", "fileName", 120)
+    });
+    
PowerMockito.whenNew(RuntimeException.class).withNoArguments().thenReturn(re);
+
+    Assert.assertNull(JvmUtils.findMainClassByStackTrace());
+  }
+
+
+  @Test
+  public void findMainClassByStackTrace_withoutMainMethod() throws Exception{
+    RuntimeException re = PowerMockito.mock(RuntimeException.class);
+    PowerMockito.when(re.getStackTrace()).thenReturn(new StackTraceElement[]{
+        new StackTraceElement("declaring.class.fileName", "methodName", 
"fileName", 100),
+        new StackTraceElement("InvalidClass", "methodName", "fileName", 120)
+    });
+    
PowerMockito.whenNew(RuntimeException.class).withNoArguments().thenReturn(re);
+
+    Assert.assertNull(JvmUtils.findMainClassByStackTrace());
+  }
+
+  @Test
+  public void findMainClassByStackTrace_emptyStackTrace() throws Exception{
+    RuntimeException re = PowerMockito.mock(RuntimeException.class);
+    PowerMockito.when(re.getStackTrace()).thenReturn(new 
StackTraceElement[]{});
+    
PowerMockito.whenNew(RuntimeException.class).withNoArguments().thenReturn(re);
+
+    Assert.assertNull(JvmUtils.findMainClassByStackTrace());
+  }
+
+  @Test
+  public void findMainClassByStackTrace_nullStackTrace() throws Exception{
+    RuntimeException re = PowerMockito.mock(RuntimeException.class);
+    PowerMockito.when(re.getStackTrace()).thenReturn(null);
+    
PowerMockito.whenNew(RuntimeException.class).withNoArguments().thenReturn(re);
+
+    Assert.assertNull(JvmUtils.findMainClassByStackTrace());
+  }
+
 }

Reply via email to