Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

spring-boot-loader: return code is 0 despite UnsupportedClassVersionError #5006

Closed
alampada opened this issue Jan 22, 2016 · 2 comments
Closed
Assignees
Milestone

Comments

@alampada
Copy link

We are using spring-boot-loader 1.3.2 in order to build our dropwizard application fat jar. Due to a bad configuration, we ended up building the fat jar with java 8 and try to run it in order to perform some tests with java 7. As expected, launching the application fails with UnsupportedClassVersionError. The issue is that the return code is zero despite the failure.

java -jar ~/jarTest/ext/java8.jar 
Exception in thread "main" java.lang.UnsupportedClassVersionError: com/our/package/DropwizardService : Unsupported major.minor version 52.0
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClass(ClassLoader.java:800)
        at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
        at java.net.URLClassLoader.defineClass(URLClassLoader.java:449)
        at java.net.URLClassLoader.access$100(URLClassLoader.java:71)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
        at org.springframework.boot.loader.LaunchedURLClassLoader.doLoadClass(LaunchedURLClassLoader.java:170)
        at org.springframework.boot.loader.LaunchedURLClassLoader.loadClass(LaunchedURLClassLoader.java:142)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
        at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:48)
        at java.lang.Thread.run(Thread.java:745)
echo $?
0

I think that this is due to MainMethodRunner.run not handling this Error and, as a result, the thread just dies. Do you think it would make sense to try to handle UnsupportedClassVersionError (despite being an Error) in order to be able to return an appropriate exit code?

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Jan 22, 2016
@wilkinsona
Copy link
Member

Do you think it would make sense to try to handle UnsupportedClassVersionError (despite being an Error) in order to be able to return an appropriate exit code?

TL;DR Returning an appropriate exit code is what we want to do, but I don't think that handling UnsupportedClassVersionError is the right way to do it.

Our goal is for MainMethodRunner to behave in the same way as if the main method that it runs had been called directly. We recently tried to move in that direction in by changing MainMethodRunner to rethrow any exception that it catches (2dc3660). There's another piece in the puzzle, though. Launcher, the base class for JarLauncher which is used when you run an app with java -jar uses a separate thread to call MainMethodRunner. When that thread dies, even due to an Error, the JVM exits, but it exits with 0. That's what we need to fix.

@wilkinsona
Copy link
Member

Here's a possible fix:

diff --git a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/Launcher.java b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/Launcher.java
index 3bcd43a..a57787d 100644
--- a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/Launcher.java
+++ b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/Launcher.java
@@ -17,6 +17,7 @@
 package org.springframework.boot.loader;

 import java.io.File;
+import java.lang.Thread.UncaughtExceptionHandler;
 import java.lang.reflect.Constructor;
 import java.net.URI;
 import java.net.URL;
@@ -103,6 +104,15 @@ public abstract class Launcher {
                Thread runnerThread = new Thread(runner);
                runnerThread.setContextClassLoader(classLoader);
                runnerThread.setName(Thread.currentThread().getName());
+               runnerThread.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
+
+                       @Override
+                       public void uncaughtException(Thread thread, Throwable ex) {
+                               ex.printStackTrace();
+                               System.exit(1);
+                       }
+
+               });
                runnerThread.start();
        }

It has the desired effect of producing a non-zero exit code for an UnsupportedClassVersionError and aligns with the behaviour in Launcher.launch. I'm a bit concerned about how it'll interact with the new exception mapping stuff that was added in 1.3.2, though.

@wilkinsona wilkinsona added for: team-attention An issue we'd like other members of the team to review and removed status: waiting-for-triage An issue we've not yet triaged labels Jan 22, 2016
@wilkinsona wilkinsona added this to the 1.4.0.M1 milestone Feb 3, 2016
@wilkinsona wilkinsona self-assigned this Feb 3, 2016
@wilkinsona wilkinsona removed the for: team-attention An issue we'd like other members of the team to review label Feb 3, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants