setUncaughtExceptionHandler在Maven项目中不起作用

时间:2015-10-26 11:05:43

标签: java maven exception

我试图在我的应用程序中的主线程上处理未捕获的异常,因此我可以将它们记录到一个文件中(我的应用程序是一个命令行应用程序,它运行在一夜之间的工作,所以如果出现问题,我想要管理员能够轻松看到异常)。我已将此简化为一个简单的测试用例。

使用(根据getting started docs)生成的Maven应用程序:

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

App.java:

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结果之间。

1 个答案:

答案 0 :(得分:2)

JavaExecMojo不会分叉新进程。相反,它在与mojo相同的线程中调用main方法。因此,他们必须捕获main方法抛出的任何异常,并模拟jvm的行为。

我认为这是maven exec插件中的一个错误。看一下ExecJavaMojo的源代码。

JavaExecMojo仅使用

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

模拟未捕获的异常行为。但这是不正确的,因为如果没有为当前线程注册未捕获的异常处理程序,则uncaughtException的{​​{1}}方法仅由JVM调用。请参阅ThreadGroup.uncaughtException(Thread t, Throwable e)的javadoc。

  

当此线程组中的线程因未捕获的异常而停止,并且线程没有安装特定的Thread.UncaughtExceptionHandler 时由Java虚拟机调用。

我想开发人员想要做的是

ThreadGroup

这将模拟JVM的行为。

您可以使用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); } } 作为快速解决方法。

defaultUncaughtExceptionHandler

修改

  

但是,使用defaultUncaughtExceptionHandler会产生相同的java.lang.reflect.InvocationTargetException。

你是对的。他们做了另一招。他们使用自己的Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { public void uncaughtException(Thread t, Throwable e) { System.out.println("Handled exception - let's log it!"); // Logging code here } }); 实现。见IsolatedThreadGroup

ThreadGroup

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 ); } } 的javadoc强制执行

  

ThreadGroup的uncaughtException方法执行以下操作:

     
      
  • 如果此线程组具有父线程组,则使用相同的两个参数调用该父线程的uncaughtException方法。
  •   
  • 否则,此方法检查是否安装了默认的未捕获异常处理程序,如果是,则使用相同的两个参数调用其uncaughtException方法。
  •   
  • 否则,此方法确定Throwable参数是否是ThreadDeath的实例。如果是这样,没有什么特别的。否则,将使用Throwable的printStackTrace方法将包含从线程的getName方法返回的线程名称和堆栈回溯的消息打印到标准错误流中。
  •   

所以他们的实现不符合API。这就是uncaughtException不起作用的原因。

<强>结论

请勿使用defaultUncaughtExceptionHandler。请改用JavaExecMojo。 E.g。

ExecMojo

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

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