17
17
18
18
import akka .actor .ActorRef ;
19
19
import akka .actor .ActorSystem ;
20
- import akka .http .javadsl .IncomingConnection ;
21
- import akka .http .javadsl .ServerBinding ;
22
- import akka .stream .javadsl .Source ;
23
20
import ch .qos .logback .classic .LoggerContext ;
24
21
import com .arpnetworking .clusteraggregator .configuration .ClusterAggregatorConfiguration ;
25
22
import com .arpnetworking .configuration .jackson .DynamicConfiguration ;
30
27
import com .arpnetworking .utility .Database ;
31
28
import com .arpnetworking .utility .Launchable ;
32
29
import com .fasterxml .jackson .databind .ObjectMapper ;
30
+ import com .google .common .base .Optional ;
31
+ import com .google .common .base .Throwables ;
33
32
import com .google .common .collect .Lists ;
34
33
import com .google .inject .Guice ;
35
34
import com .google .inject .Injector ;
38
37
import com .google .inject .name .Names ;
39
38
import org .slf4j .LoggerFactory ;
40
39
import scala .concurrent .Await ;
41
- import scala .concurrent .Future ;
42
40
import scala .concurrent .duration .Duration ;
43
41
44
42
import java .io .File ;
45
43
import java .util .List ;
44
+ import java .util .concurrent .Semaphore ;
46
45
import java .util .concurrent .TimeUnit ;
47
46
48
47
/**
51
50
* @author Brandon Arp (barp at groupon dot com)
52
51
*/
53
52
public final class Main implements Launchable {
54
-
55
53
/**
56
54
* Entry point.
57
55
*
58
56
* @param args command line arguments
59
57
*/
60
58
public static void main (final String [] args ) {
61
- LOGGER .info ()
62
- .setMessage ("Launching cluster-aggregator" )
63
- .log ();
64
-
65
59
Thread .setDefaultUncaughtExceptionHandler (
66
60
(thread , throwable ) -> {
67
- System .err .println ("Unhandled exception! exception: " + throwable .toString ());
68
- throwable .printStackTrace (System .err );
61
+ LOGGER .error ()
62
+ .setMessage ("Unhandled exception!" )
63
+ .setThrowable (throwable )
64
+ .log ();
69
65
});
70
66
71
67
Thread .currentThread ().setUncaughtExceptionHandler (
@@ -77,6 +73,12 @@ public static void main(final String[] args) {
77
73
}
78
74
);
79
75
76
+ LOGGER .info ()
77
+ .setMessage ("Launching cluster-aggregator" )
78
+ .log ();
79
+
80
+ Runtime .getRuntime ().addShutdownHook (SHUTDOWN_THREAD );
81
+
80
82
if (args .length != 1 ) {
81
83
throw new RuntimeException ("No configuration file specified" );
82
84
}
@@ -86,32 +88,37 @@ public static void main(final String[] args) {
86
88
.addData ("file" , args [0 ])
87
89
.log ();
88
90
89
- final File configurationFile = new File (args [0 ]);
90
- final Configurator <Main , ClusterAggregatorConfiguration > configurator =
91
- new Configurator <>(Main ::new , ClusterAggregatorConfiguration .class );
92
- final ObjectMapper objectMapper = ClusterAggregatorConfiguration .createObjectMapper ();
93
- final DynamicConfiguration configuration = new DynamicConfiguration .Builder ()
94
- .setObjectMapper (objectMapper )
95
- .addSourceBuilder (
96
- new JsonNodeFileSource .Builder ().setObjectMapper (objectMapper )
97
- .setFile (configurationFile ))
98
- .addTrigger (new FileTrigger .Builder ().setFile (configurationFile ).build ())
99
- .addListener (configurator )
100
- .build ();
101
-
102
- configuration .launch ();
103
-
104
- Runtime .getRuntime ().addShutdownHook (
105
- new Thread (
106
- () -> {
107
- configuration .shutdown ();
108
- configurator .shutdown ();
109
- LOGGER .info ()
110
- .setMessage ("Stopping cluster-aggregator" )
111
- .log ();
112
- final LoggerContext context = (LoggerContext ) LoggerFactory .getILoggerFactory ();
113
- context .stop ();
114
- }));
91
+ Optional <DynamicConfiguration > configuration = Optional .absent ();
92
+ Optional <Configurator <Main , ClusterAggregatorConfiguration >> configurator = Optional .absent ();
93
+ try {
94
+ final File configurationFile = new File (args [0 ]);
95
+ configurator = Optional .of (new Configurator <>(Main ::new , ClusterAggregatorConfiguration .class ));
96
+ final ObjectMapper objectMapper = ClusterAggregatorConfiguration .createObjectMapper ();
97
+ configuration = Optional .of (new DynamicConfiguration .Builder ()
98
+ .setObjectMapper (objectMapper )
99
+ .addSourceBuilder (
100
+ new JsonNodeFileSource .Builder ().setObjectMapper (objectMapper )
101
+ .setFile (configurationFile ))
102
+ .addTrigger (new FileTrigger .Builder ().setFile (configurationFile ).build ())
103
+ .addListener (configurator .get ())
104
+ .build ());
105
+
106
+ configuration .get ().launch ();
107
+
108
+ // Wait for application shutdown
109
+ SHUTDOWN_SEMAPHORE .acquire ();
110
+ } catch (final InterruptedException e ) {
111
+ throw Throwables .propagate (e );
112
+ } finally {
113
+ if (configurator .isPresent ()) {
114
+ configurator .get ().shutdown ();
115
+ }
116
+ if (configuration .isPresent ()) {
117
+ configuration .get ().shutdown ();
118
+ }
119
+ // Notify the shutdown that we're done
120
+ SHUTDOWN_SEMAPHORE .release ();
121
+ }
115
122
}
116
123
117
124
/**
@@ -245,6 +252,41 @@ private void shutdownAkka() {
245
252
private static final Logger LOGGER = com .arpnetworking .steno .LoggerFactory .getLogger (Main .class );
246
253
private static final Duration SHUTDOWN_TIMEOUT = Duration .create (3 , TimeUnit .MINUTES );
247
254
private static final SourceTypeLiteral SOURCE_TYPE_LITERAL = new SourceTypeLiteral ();
255
+ private static final Semaphore SHUTDOWN_SEMAPHORE = new Semaphore (0 );
256
+ private static final Thread SHUTDOWN_THREAD = new ShutdownThread ();
257
+
258
+ private static final class ShutdownThread extends Thread {
259
+ private ShutdownThread () {
260
+ super ("ClusterAggregatorShutdownHook" );
261
+ }
262
+
263
+ @ Override
264
+ public void run () {
265
+ LOGGER .info ()
266
+ .setMessage ("Stopping cluster-aggregator" )
267
+ .log ();
268
+
269
+ // release the main thread waiting for shutdown signal
270
+ SHUTDOWN_SEMAPHORE .release ();
271
+
272
+ try {
273
+ // wait for it to signal that it has completed shutdown
274
+ if (!SHUTDOWN_SEMAPHORE .tryAcquire (SHUTDOWN_TIMEOUT .toSeconds (), TimeUnit .SECONDS )) {
275
+ LOGGER .warn ()
276
+ .setMessage ("Shutdown did not complete in a timely manner" )
277
+ .log ();
278
+ }
279
+ } catch (final InterruptedException e ) {
280
+ throw Throwables .propagate (e );
281
+ } finally {
282
+ LOGGER .info ()
283
+ .setMessage ("Shutdown complete" )
284
+ .log ();
285
+ final LoggerContext context = (LoggerContext ) LoggerFactory .getILoggerFactory ();
286
+ context .stop ();
287
+ }
288
+ }
289
+ }
248
290
249
- private static class SourceTypeLiteral extends TypeLiteral <Source < IncomingConnection , Future < ServerBinding > >> {}
291
+ private static class SourceTypeLiteral extends TypeLiteral <java . util . concurrent . CompletionStage < akka . http . javadsl . ServerBinding >> {}
250
292
}
0 commit comments