1515import org .slf4j .LoggerFactory ;
1616
1717import java .io .IOException ;
18+ import java .lang .reflect .InvocationTargetException ;
1819import java .nio .file .ClosedWatchServiceException ;
1920import java .nio .file .Files ;
2021import java .nio .file .Path ;
@@ -65,25 +66,73 @@ private static class AppModule {
6566 this .contextClassLoader = contextClassLoader ;
6667 }
6768
68- public void start () {
69+ public Exception start () {
6970 try {
70- System .setProperty ("___jooby_run_hook__" , SERVER_REF );
71- // Track the number of restarts
72- System .setProperty ("joobyRun.counter" , Integer .toString (counter ++));
73-
7471 module = loader .loadModule (conf .getProjectName ());
7572 ModuleClassLoader classLoader = module .getClassLoader ();
7673 Thread .currentThread ().setContextClassLoader (classLoader );
7774
75+ if (counter == 0 ) {
76+ // main class must exists
77+ module .getClassLoader ().loadClass (conf .getMainClass ());
78+ }
79+
80+ System .setProperty ("___jooby_run_hook__" , SERVER_REF );
81+ // Track the number of restarts
82+ System .setProperty ("joobyRun.counter" , Integer .toString (counter ++));
83+
7884 module .run (conf .getMainClass (),
7985 new String []{"server.port=" + conf .getPort (), "server.join=false" });
80-
81- } catch (Exception x ) {
82- logger .error ("execution of {} resulted in exception" , conf .getMainClass (),
83- withoutReflection (x ));
86+ } catch (ClassNotFoundException x ) {
87+ String message = x .getMessage ();
88+ if (message .trim ().startsWith (conf .getMainClass ())) {
89+ logger .error (
90+ "Application class: '{}' not found. Possible solutions:\n 1) Make sure class exists\n 2) Class name is correct (no typo)" ,
91+ conf .getMainClass ());
92+ // We must exit the JVM, due it is impossible to guess the main application.
93+ return new ClassNotFoundException (conf .getMainClass ());
94+ } else {
95+ printErr (x );
96+ }
97+ } catch (Throwable x ) {
98+ printErr (x );
8499 } finally {
85100 Thread .currentThread ().setContextClassLoader (contextClassLoader );
86101 }
102+ // In theory: application started successfully, then something went wrong. Still, users
103+ // can fix the problem and recompile.
104+ return null ;
105+ }
106+
107+ private void printErr (Throwable source ) {
108+ Throwable cause = withoutReflection (source );
109+ System .out .println ("CAUSE " + cause );
110+ StackTraceElement [] stackTrace = cause .getStackTrace ();
111+ int truncateAt = stackTrace .length ;
112+ for (int i = 0 ; i < stackTrace .length ; i ++) {
113+ StackTraceElement it = stackTrace [i ];
114+ if (it .getClassName ().equals ("org.jboss.modules.Module" )) {
115+ truncateAt = i ;
116+ }
117+ }
118+ if (truncateAt != stackTrace .length ) {
119+ StackTraceElement [] cleanstack = new StackTraceElement [truncateAt ];
120+ System .arraycopy (stackTrace , 0 , cleanstack , 0 , truncateAt );
121+ cause .setStackTrace (cleanstack );
122+ }
123+ logger .error ("execution of {} resulted in exception" , conf .getMainClass (), cause );
124+
125+ // is fatal?
126+ if (isFatal (source ) || isFatal (cause )) {
127+ sneakyThrow0 (source );
128+ }
129+ }
130+
131+ private boolean isFatal (Throwable cause ) {
132+ return cause instanceof InterruptedException
133+ || cause instanceof LinkageError
134+ || cause instanceof ThreadDeath
135+ || cause instanceof VirtualMachineError ;
87136 }
88137
89138 public void restart () {
@@ -97,10 +146,13 @@ public void close() {
97146 }
98147
99148 private Throwable withoutReflection (Throwable cause ) {
100- while (cause instanceof ReflectiveOperationException ) {
101- cause = cause .getCause ();
149+ Throwable it = cause ;
150+ Throwable prev = cause ;
151+ while (it instanceof InvocationTargetException ) {
152+ prev = it ;
153+ it = it .getCause ();
102154 }
103- return cause ;
155+ return it == null ? prev : it ;
104156 }
105157
106158 private void unloadModule () {
@@ -192,9 +244,9 @@ public boolean addResource(Path path) {
192244 /**
193245 * Start the application.
194246 *
195- * @throws Exception If something goes wrong.
247+ * @throws Throwable If something goes wrong.
196248 */
197- public void start () throws Exception {
249+ public void start () throws Throwable {
198250 this .watcher = newWatcher ();
199251 try {
200252 logger .debug ("project: {}" , toString ());
@@ -205,8 +257,14 @@ public void start() throws Exception {
205257 ExtModuleLoader loader = new ExtModuleLoader (finders );
206258 module = new AppModule (logger , loader , Thread .currentThread ().getContextClassLoader (),
207259 options );
208- module .start ();
209- watcher .watch ();
260+ Exception error = module .start ();
261+ if (error == null ) {
262+ watcher .watch ();
263+ } else {
264+ // exit
265+ shutdown ();
266+ throw error ;
267+ }
210268 } catch (ClosedWatchServiceException expected ) {
211269 logger .trace ("Watcher.close resulted in exception" , expected );
212270 }
@@ -277,4 +335,9 @@ private void onFileChange(DirectoryChangeEvent.EventType kind, Path path) {
277335 }
278336 }
279337 }
338+
339+ @ SuppressWarnings ("unchecked" )
340+ private static <E extends Throwable > void sneakyThrow0 (final Throwable x ) throws E {
341+ throw (E ) x ;
342+ }
280343}
0 commit comments