6666import java .util .ArrayList ;
6767import java .util .Arrays ;
6868import java .util .Collection ;
69+ import java .util .Collections ;
6970import java .util .HashMap ;
7071import java .util .HashSet ;
7172import java .util .LinkedHashSet ;
131132import com .google .common .collect .ImmutableList ;
132133import com .google .common .collect .ImmutableMap ;
133134import com .google .common .collect .ImmutableSet ;
134- import com .google .common .collect .Lists ;
135135import com .google .inject .Binder ;
136136import com .google .inject .Guice ;
137137import com .google .inject .Injector ;
@@ -3497,54 +3497,31 @@ public static void run(final Class<? extends Jooby> app, final String... args) {
34973497 }
34983498
34993499 /**
3500- * <h1>Bootstrap</h1>
3501- * <p>
3502- * The bootstrap process is defined as follows:
3503- * </p>
3504- * <h2>1. Configuration files (first-listed are higher priority)</h2>
3505- * <ol>
3506- * <li>System properties</li>
3507- * <li>Application properties: {@code application.conf} or custom, see {@link #use(Config)}</li>
3508- * <li>{@link Jooby.Module Modules} properties</li>
3509- * </ol>
3500+ * Export routes from an application. Useful for route analysis, testing, debugging, etc...
35103501 *
3511- * <h2>2. Dependency Injection and {@link Jooby.Module modules}</h2>
3512- * <ol>
3513- * <li>An {@link Injector Guice Injector} is created.</li>
3514- * <li>It calls to {@link Jooby.Module#configure(Env, Config, Binder)} for each module.</li>
3515- * <li>At this point Guice is ready and all the services has been binded.</li>
3516- * <li>A web server is started</li>
3517- * </ol>
3502+ * @param app Application to extract/collect routes.
3503+ * @return Application routes.
35183504 */
3519- public void start () {
3520- start (new String [0 ]);
3521- }
3505+ public static List <Definition > exportRoutes (final Jooby app ) {
3506+ @ SuppressWarnings ("serial" )
3507+ class Success extends RuntimeException {
3508+ List <Definition > routes ;
35223509
3523- /**
3524- * <h1>Bootstrap</h1>
3525- * <p>
3526- * The bootstrap process is defined as follows:
3527- * </p>
3528- * <h2>1. Configuration files (first-listed are higher priority)</h2>
3529- * <ol>
3530- * <li>System properties</li>
3531- * <li>Application properties: {@code application.conf} or custom, see {@link #use(Config)}</li>
3532- * <li>{@link Jooby.Module Modules} properties</li>
3533- * </ol>
3534- *
3535- * <h2>2. Dependency Injection and {@link Jooby.Module modules}</h2>
3536- * <ol>
3537- * <li>An {@link Injector Guice Injector} is created.</li>
3538- * <li>It calls to {@link Jooby.Module#configure(Env, Config, Binder)} for each module.</li>
3539- * <li>At this point Guice is ready and all the services has been binded.</li>
3540- * <li>A web server is started</li>
3541- * </ol>
3542- *
3543- * @param routes Routes callback. Invoked once all app routes has been collected.
3544- * @throws Throwable If something fails to start.
3545- */
3546- public void start (final Consumer <List <Route .Definition >> routes ) throws Throwable {
3547- start (new String [0 ], routes );
3510+ Success (final List <Route .Definition > routes ) {
3511+ this .routes = routes ;
3512+ }
3513+ }
3514+ List <Definition > routes = Collections .emptyList ();
3515+ try {
3516+ app .start (new String [0 ], r -> {
3517+ throw new Success (r );
3518+ });
3519+ } catch (Success success ) {
3520+ routes = success .routes ;
3521+ } catch (Throwable x ) {
3522+ logger (app ).error ("Unable to get routes from {}" , app , x );
3523+ }
3524+ return routes ;
35483525 }
35493526
35503527 /**
@@ -3566,12 +3543,9 @@ public void start(final Consumer<List<Route.Definition>> routes) throws Throwabl
35663543 * <li>At this point Guice is ready and all the services has been binded.</li>
35673544 * <li>A web server is started</li>
35683545 * </ol>
3569- *
3570- * @param args Application arguments. Using the <code>name=value</code> format, except for
3571- * application.env where can be just: <code>myenv</code>.
35723546 */
3573- public void start (final String [] args ) {
3574- start (args , null );
3547+ public void start () {
3548+ start (new String [ 0 ] );
35753549 }
35763550
35773551 /**
@@ -3596,55 +3570,59 @@ public void start(final String[] args) {
35963570 *
35973571 * @param args Application arguments. Using the <code>name=value</code> format, except for
35983572 * application.env where can be just: <code>myenv</code>.
3599- * @param routes Routes callback. Invoked once all app routes has been collected.
36003573 */
3601- public void start (final String [] args , final Consumer < List < Route . Definition >> routes ) {
3574+ public void start (final String ... args ) {
36023575 try {
3603- long start = System .currentTimeMillis ();
3576+ start (args , null );
3577+ } catch (Throwable x ) {
3578+ stop (Optional .of (x ));
3579+ }
3580+ }
36043581
3605- started .set (true );
3582+ private void start (final String [] args , final Consumer <List <Route .Definition >> routes )
3583+ throws Throwable {
3584+ long start = System .currentTimeMillis ();
36063585
3607- // shutdown hook
3608- Runtime .getRuntime ().addShutdownHook (new Thread (() -> stop ()));
3586+ started .set (true );
36093587
3610- this .injector = bootstrap (args (args ), routes );
3588+ this .injector = bootstrap (args (args ), routes );
36113589
3612- Config conf = injector .getInstance (Config .class );
3590+ // shutdown hook
3591+ Runtime .getRuntime ().addShutdownHook (new Thread (() -> stop ()));
36133592
3614- Logger log = LoggerFactory .getLogger (getClass ());
3615- log .debug ("config tree:\n {}" , configTree (conf .origin ().description ()));
3593+ Config conf = injector .getInstance (Config .class );
36163594
3617- // start services
3618- for (CheckedConsumer <Registry > onStart : this .onStart ) {
3619- onStart .accept (this );
3620- }
3595+ Logger log = LoggerFactory .getLogger (getClass ());
3596+ log .debug ("config tree:\n {}" , configTree (conf .origin ().description ()));
36213597
3622- // route mapper
3623- Set <Route .Definition > routeDefs = injector .getInstance (Route .KEY );
3624- Set <WebSocket .Definition > sockets = injector .getInstance (WebSocket .KEY );
3625- if (mapper != null ) {
3626- routeDefs .forEach (it -> it .map (mapper ));
3627- }
3598+ // start services
3599+ for (CheckedConsumer <Registry > onStart : this .onStart ) {
3600+ onStart .accept (this );
3601+ }
36283602
3629- // Start server
3630- Server server = injector .getInstance (Server .class );
3631- String serverName = server .getClass ().getSimpleName ().replace ("Server" , "" ).toLowerCase ();
3603+ // route mapper
3604+ Set <Route .Definition > routeDefs = injector .getInstance (Route .KEY );
3605+ Set <WebSocket .Definition > sockets = injector .getInstance (WebSocket .KEY );
3606+ if (mapper != null ) {
3607+ routeDefs .forEach (it -> it .map (mapper ));
3608+ }
36323609
3633- server .start ();
3634- long end = System .currentTimeMillis ();
3610+ // Start server
3611+ Server server = injector .getInstance (Server .class );
3612+ String serverName = server .getClass ().getSimpleName ().replace ("Server" , "" ).toLowerCase ();
36353613
3636- log .info ("[{}@{}]: Server started in {}ms\n \n {}\n " ,
3637- conf .getString ("application.env" ),
3638- serverName ,
3639- end - start ,
3640- new AppPrinter (routeDefs , sockets , conf ));
3614+ server .start ();
3615+ long end = System .currentTimeMillis ();
36413616
3642- boolean join = conf .hasPath ("server.join" ) ? conf .getBoolean ("server.join" ) : true ;
3643- if (join ) {
3644- server .join ();
3645- }
3646- } catch (Throwable x ) {
3647- stop (Optional .of (x ));
3617+ log .info ("[{}@{}]: Server started in {}ms\n \n {}\n " ,
3618+ conf .getString ("application.env" ),
3619+ serverName ,
3620+ end - start ,
3621+ new AppPrinter (routeDefs , sockets , conf ));
3622+
3623+ boolean join = conf .hasPath ("server.join" ) ? conf .getBoolean ("server.join" ) : true ;
3624+ if (join ) {
3625+ server .join ();
36483626 }
36493627 }
36503628
@@ -4058,11 +4036,11 @@ private Injector bootstrap(final Config args,
40584036 List <Object > bag = normalize (realbag , env , rm , prefix );
40594037
40604038 // collect routes and fire route callback
4061- List <Route .Definition > routes = bag .stream ()
4062- .filter (it -> it instanceof Route .Definition )
4063- .map (it -> (Route .Definition ) it )
4064- .collect (Collectors .<Route .Definition > toList ());
40654039 if (rcallback != null ) {
4040+ List <Route .Definition > routes = bag .stream ()
4041+ .filter (it -> it instanceof Route .Definition )
4042+ .map (it -> (Route .Definition ) it )
4043+ .collect (Collectors .<Route .Definition > toList ());
40664044 rcallback .accept (routes );
40674045 }
40684046
@@ -4354,22 +4332,20 @@ public void stop() {
43544332
43554333 private void stop (final Optional <Throwable > x ) {
43564334 if (started .compareAndSet (true , false )) {
4357- Logger log = LoggerFactory .getLogger (getClass ());
4358- fireStop (injector , this , log , onStop );
4335+ Logger log = logger (this );
43594336
4360- List <Throwable > cause = Lists .newArrayList ();
4361- x .ifPresent (cause ::add );
4362- try {
4363- injector .getInstance (Server .class ).stop ();
4364- } catch (Throwable ex ) {
4365- cause .add (ex );
4366- }
4367- if (cause .size () > 0 ) {
4368- log .error ("Shutdown after error" , cause .get (0 ));
4369- } else {
4370- log .info ("Shutdown successfully" );
4337+ x .ifPresent (c -> log .error ("An error occurred while starting the application:" , c ));
4338+ fireStop (injector , this , log , onStop );
4339+ if (injector != null ) {
4340+ try {
4341+ injector .getInstance (Server .class ).stop ();
4342+ } catch (Throwable ex ) {
4343+ log .debug ("server.stop() resulted in exception" , ex );
4344+ }
43714345 }
43724346 injector = null ;
4347+
4348+ log .info ("Stopped" );
43734349 }
43744350 }
43754351
@@ -4676,4 +4652,8 @@ static String logback(final Config conf) {
46764652 return logback ;
46774653 }
46784654
4655+ private static Logger logger (final Jooby app ) {
4656+ return LoggerFactory .getLogger (app .getClass ());
4657+ }
4658+
46794659}
0 commit comments