2015-10-26 128 views
4

我想在我的應用程序的主線程上處理未捕獲的異常,因此我可以將它們記錄到文件中(我的應用程序是一個命令行應用程序,出現錯誤我希望管理員能夠輕鬆查看例外情況)。我儘可能簡化了測試用例。setUncaughtExceptionHandler在Maven項目中不工作

mvn -B archetype:generate \ 
     -DarchetypeGroupId=org.apache.maven.archetypes \ 
     -DgroupId=com.mycompany.app \ 
     -DartifactId=my-app 

App.java:與(按照getting started docs)產生

Maven的應用

package com.mycompany.app; 

public class App { 

    public static void main(String[] args) throws Exception { 
     Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { 
      public void uncaughtException(Thread t, Throwable e) { 
       System.out.println("Handled exception - let's log it!"); 
       // Logging code here 
      } 
     }); 

     System.out.println("Exception testing"); 
     throw new Exception("Catch and log me!"); 
    } 

} 

mvn exec:java運行生產:

[INFO] Error stacktraces are turned on. 
[INFO] Scanning for projects... 
[WARNING] 
[WARNING] Some problems were encountered while building the effective model for com.mycompany.app:my-app:jar:1.0-SNAPSHOT 
[WARNING] 'build.plugins.plugin.version' for org.codehaus.mojo:exec-maven-plugin is missing. @ line 20, column 15 
[WARNING] 
[WARNING] It is highly recommended to fix these problems because they threaten the stability of your build. 
[WARNING] 
[WARNING] For this reason, future Maven versions might no longer support building such malformed projects. 
[WARNING] 
[INFO]                   
[INFO] ------------------------------------------------------------------------ 
[INFO] Building my-app 1.0-SNAPSHOT 
[INFO] ------------------------------------------------------------------------ 
[INFO] 
[INFO] --- exec-maven-plugin:1.4.0:java (default-cli) @ my-app --- 
Exception testing 
[WARNING] 
java.lang.reflect.InvocationTargetException 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    at java.lang.reflect.Method.invoke(Method.java:606) 
    at org.codehaus.mojo.exec.ExecJavaMojo$1.run(ExecJavaMojo.java:293) 
    at java.lang.Thread.run(Thread.java:745) 
Caused by: java.lang.Exception: Catch and log me! 
    at com.mycompany.app.App.main(App.java:14) 
    ... 6 more 
[INFO] ------------------------------------------------------------------------ 
[INFO] BUILD FAILURE 
[INFO] ------------------------------------------------------------------------ 
[INFO] Total time: 0.493 s 
[INFO] Finished at: 2015-10-26T10:57:00+00:00 
[INFO] Final Memory: 8M/240M 
[INFO] ------------------------------------------------------------------------ 
[ERROR] Failed to execute goal org.codehaus.mojo:exec-maven-plugin:1.4.0:java (default-cli) on project my-app: An exception occured while executing the Java class. null: InvocationTargetException: Catch and log me! -> [Help 1] 
org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal org.codehaus.mojo:exec-maven-plugin:1.4.0:java (default-cli) on project my-app: An exception occured while executing the Java class. null 
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:216) 
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153) 
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145) 
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:116) 
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:80) 
    at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:51) 
    at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:128) 
    at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:307) 
    at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:193) 
    at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:106) 
    at org.apache.maven.cli.MavenCli.execute(MavenCli.java:862) 
    at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:286) 
    at org.apache.maven.cli.MavenCli.main(MavenCli.java:197) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    at java.lang.reflect.Method.invoke(Method.java:606) 
    at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:289) 
    at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:229) 
    at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:415) 
    at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:356) 
Caused by: org.apache.maven.plugin.MojoExecutionException: An exception occured while executing the Java class. null 
    at org.codehaus.mojo.exec.ExecJavaMojo.execute(ExecJavaMojo.java:345) 
    at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:134) 
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:208) 
    ... 20 more 
Caused by: java.lang.reflect.InvocationTargetException 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    at java.lang.reflect.Method.invoke(Method.java:606) 
    at org.codehaus.mojo.exec.ExecJavaMojo$1.run(ExecJavaMojo.java:293) 
    at java.lang.Thread.run(Thread.java:745) 
Caused by: java.lang.Exception: Catch and log me! 
    at com.mycompany.app.App.main(App.java:14) 
    ... 6 more 

相同的代碼運行作爲一個簡單的Java應用程序工作正常App.java:

public class App { 

    public static void main(String[] args) throws Exception { 
     Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { 
      public void uncaughtException(Thread t, Throwable e) { 
       System.out.println("Handled exception - let's log it!"); 
       // Logging code here 
      } 
     }); 

     System.out.println("Exception testing"); 
     throw new Exception("Catch and log me!"); 
    } 

} 

運行javac App.java && java App生產:

Exception testing 
Handled exception - let's log it! 

我懷疑要給出的建議是:「你不應該有任何未捕獲的異常」,但我不知道我保證無論如何,對Maven和非Maven結果之間的差異感到好奇。

+0

它與'exec:exec'而不是'exec:java'一起使用嗎?我懷疑這與'exec:java'在一個Maven運行的同一虛擬機內運行Java應用程序的事實有關。 – Tunaki

+0

嗯,我沒有嘗試過。剛剛看過'exec:exec'的文檔,看起來我必須自己設置類路徑,這感覺就像使用Maven一樣。我剛剛嘗試使用'exec:exec',但無法弄清楚如何傳遞正確的配置。我收到 「錯誤:無法找到或加載主類com.mycompany.app.App」(插件配置設置爲'的Java com.mycompany.app.App ' –

回答

2

JavaExecMojo不會分叉新的進程。相反,它在與mojo相同的線程中調用main方法。所以他們必須捕捉主要方法拋出的任何異常,並模擬jvm的行爲。

我認爲這是maven exec插件中的一個錯誤。看看ExecJavaMojo的源代碼。

JavaExecMojo只使用

Thread.currentThread().getThreadGroup().uncaughtException(Thread.currentThread(), e); 

效仿未捕獲的異常行爲。但是這是不正確的,因爲如果沒有爲當前線程註冊未捕獲的異常處理程序,那麼只有JVM調用uncaughtException方法ThreadGroup。請參閱ThreadGroup.uncaughtException(Thread t, Throwable e)的javadoc。

Called by the Java Virtual Machine when a thread in this thread group stops because of an uncaught exception, and the thread does not have a specific Thread.UncaughtExceptionHandler installed.

我猜開發商想要做的是

try { 
    .... 
    // invoke main method 
} catch (Exception e) { 
    Thread currentThread = Thread.currentThread(); 
    Thread.UncaughtExceptionHandler ueh = currentThread.getUncaughtExceptionHandler(); 
    if (ueh == null) { 
    currentThread.getThreadGroup().uncaughtException(currentThread, e); 
    } else { 
    ueh.uncaughtException(currentThread, e); 
    } 
} 

這將模仿JVM的行爲。

您可以使用defaultUncaughtExceptionHandler作爲快速修復。

Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { 
    public void uncaughtException(Thread t, Throwable e) { 
    System.out.println("Handled exception - let's log it!"); 
    // Logging code here 
    } 
}); 

編輯

However, using defaultUncaughtExceptionHandler produces the same java.lang.reflect.InvocationTargetException.

你是對的。他們又做了一個詭計他們使用自己的ThreadGroup實現。見IsolatedThreadGroup

class IsolatedThreadGroup extends ThreadGroup { 
    private Throwable uncaughtException; // synchronize access to this 

    public IsolatedThreadGroup(String name){ 
     super(name); 
    } 

    public void uncaughtException(Thread thread, Throwable throwable) { 
     if (throwable instanceof ThreadDeath) { 
      return; // harmless 
     } 
     synchronized (this) { 
      if (uncaughtException == null) { 
       uncaughtException = throwable; // will be reported eventually 
      } 
     } 
     getLog().warn(throwable); 
    } 
} 

uncaughtException的Javadoc強制執行

The uncaughtException method of ThreadGroup does the following:

  • If this thread group has a parent thread group, the uncaughtException method of that parent is called with the same two arguments.
  • Otherwise, this method checks to see if there is a default uncaught exception handler installed, and if so, its uncaughtException method is called with the same two arguments.
  • Otherwise, this method determines if the Throwable argument is an instance of ThreadDeath. If so, nothing special is done. Otherwise, a message containing the thread's name, as returned from the thread's getName method, and a stack backtrace, using the Throwable's printStackTrace method, is printed to the standard error stream.

所以其執行不兼容的API。這就是爲什麼defaultUncaughtExceptionHandler不起作用。

結論

不要使用JavaExecMojo。改爲使用ExecMojo。例如。

mvn exec:exec -Dexec.executable="java" -Dexec.workingdir="someDir" -Dexec.args="-cp target/classes" 

在pom中指定插件屬性,您可以使用佔位符。例如。

<workingdir>${basedir}</workingdir> 
<args>-cp ${project.build.outputDirectory}</args> 
+0

哇,謝謝但是,使用'defaultUncaughtExceptionHandler'會產生相同的'java.lang.reflect.InvocationTargetException'。 –

+0

@TomJenkins你是對的,我深入瞭解了源代碼並更新了我的答案 –

+0

太棒了,謝謝!幸運的是,我的應用程序將在Maven之外(作爲陰影的jar)在生產環境中運行;我可以在dev中使用它。 –

相關問題