Skip to main content

Launcher

Overview

Launcher is essentially an abstracted and generalized implementation of the main() method. It integrates modules and services together and manages the lifecycle of the application.

Features

  • Perfect compatibility with ActiveJ Inject
  • Can be combined with the Service Graph to support start/stop of independent services
  • Provides the ability to gracefully shutdown services
  • Takes care of full application lifecycle and logging

Launcher in a nutshell

Launcher is a very explicit tool. You can understand how it just by looking at the implementation of its main launch() method in a nutshell:

public void launch(String[] args) throws Exception {    logger.info("=== INJECTING DEPENDENCIES");    Injector injector = createInjector(args);    logger.info("Eager instances: " + injector.createEagerInstances());    Set<LauncherService> services = injector.getInstance(new Key<Set<LauncherService>>() {});    Set<LauncherService> startedServices = new HashSet<>();    logger.info("Post-inject instances: " + injector.postInjectInstances());
    logger.info("=== STARTING APPLICATION");    logger.info("Starting LauncherServices: " + services);    startServices(services, startedServices);    onStart();    onStartFuture.complete(null);
    logger.info("=== RUNNING APPLICATION");    run();    onRunFuture.complete(null);
    logger.info("=== STOPPING APPLICATION");    onStop();    stopServices(startedServices);    onCompleteFuture.complete(null);

Step-by-step examination:

  • Create an Injector using the Modules provided in Launcher
  • Inject top-level dependencies into the Launcher instance itself
  • Instantiate all keys marked as @Eager, exported by Launcher’s Modules
  • Start all services from the Set<LauncherService> multibinder set, exported by Launcher’s Modules, and execute the onStart() method
  • Execute run() method
  • When run() is finished (either by finishing all app-specific computations or in response to a shutdown request such as SIGKILL), execute the onStop() method and stop all services

Here is an example of the Launcher lifecycle represented as logs (particularly, HttpServerLauncher subclass that provides an AsyncHttpServer, an Eventloop and Config). Launch this example:

public final class HttpHelloWorldExample extends HttpServerLauncher {  @Provides  AsyncServlet servlet() {    return request -> HttpResponse.ok200().withPlainText("Hello World");  }
  public static void main(String[] args) throws Exception {    Launcher launcher = new HttpHelloWorldExample();    launcher.launch(args);  }}

to see alike logs:

 === INJECTING DEPENDENCIESCreated eager singletonsPost-injected instances: [HttpHelloWorldExample]
=== STARTING APPLICATIONStarting LauncherServices: [io.active.net.jmx.JmxModule, io.active.net.service.ServiceGraphModule]Starting LauncherService: io.active.net.jmx.JmxModuleStarting LauncherService: io.active.net.service.ServiceGraphModuleCreating ServiceGraph...Starting servicesListening on [/0.0.0.0:8080]: AsyncHttpServer{listenAddresses=[/0.0.0.0:8080]}Started io.active.net.http.AsyncHttpServer
=== RUNNING APPLICATIONHTTP Server is listening on http://localhost:8080/
=== STOPPING APPLICATIONStopping LauncherService: io.active.net.jmx.JmxModuleStopping LauncherService: io.active.net.service.ServiceGraphModuleStopping ServiceGraph...Stopping services
  • Launcher optionally requires the following dependencies from its Modules:
    • @Eager keygroup
    • Set<LauncherService> multibinder set

Launcher exports the following dependencies to its Modules:

class Launcher{    @NotNull    public final Injector createInjector() {       return Injector.of(getInternalModule()                      .combineWith(getModule())                      .overrideWith(getOverrideModule()));    }
  private Module getInternalModule() {     Class<Launcher> launcherClass = (Class<Launcher>) getClass();     Key<CompletionStage<Void>> completionStageKey = new Key<CompletionStage<Void>>() {};
     return Module.create()         .bind(String[].class).annotatedWith(Args.class).toInstance(args)         .bind(Launcher.class).to(launcherClass)         .bind(launcherClass).toInstance(this)
         .postInjectInto(launcherClass)         .bind(completionStageKey.named(OnStart.class)).toInstance(onStartFuture)         .bind(completionStageKey.named(OnRun.class)).toInstance(onRunFuture)         .bind(completionStageKey.named(OnComplete.class)).toInstance(onCompleteFuture)
         .scan(Launcher.this);    }
  // this method can be overridden by subclasses which extend Launcher,  // provides business logic modules (for example, ConfigModule)  protected Module getModule() {      return Module.empty();  }
  // this method can be overridden by subclasses which extend Launcher,  // overrides definitions in internal module  protected Module getOverrideModule() {      return Module.empty();  }}
  • @Args String[] arguments of its Launcher.launch(String[] args) method, corresponding to main(String[] args) method
  • The Launcher instance itself
  • @OnStart CompletionStage<Void> future which is completed when application is wired and started
  • @OnRun CompletionStage<Void> future which is completed when Launcher.run() is complete.
  • @OnComplete CompletionStage<Void> future which is completed when application is stopped

Launcher also has convenient diagnostic and JMX features:

  • Instant of launch, start, run, stop, and complete
  • Duration of start, run, stop, and total duration
  • Throwable applicationError property

More examples

note

To run the examples, you need to clone ActiveJ from GitHub

git clone https://github.com/activej/activej

And import it as a Maven project. Check out tag v5.3. Before running the examples, build the project. These examples are located at activej/examples/core/datastream

Hello World Example

Here is a simple "Hello World" Launcher application using ActiveJ Inject:

public final class HelloWorldExample extends Launcher {  @Inject  String message;
  @Provides  String message() {    return "Hello, world!";  }
  @Override  protected void run() {    System.out.println(message);  }
  public static void main(String[] args) throws Exception {    Launcher launcher = new HelloWorldExample();    launcher.launch(args);  }}

See full example on GitHub

HTTP Server from scratch using Launcher

When creating HTTP servers or any other applications, you can either use some predefined solutions (see examples) or a simple Launcher instead. In this case, you will have to take care of all the required dependencies and override some basic methods:

public final class CustomHttpServerExample extends Launcher {  private static final int PORT = 8080;
  @Provides  Eventloop eventloop() {    return Eventloop.create();  }
  @Provides  AsyncServlet servlet() {    return request -> HttpResponse.ok200()        .withPlainText("Hello from HTTP server");  }
  @Provides  @Eager  AsyncHttpServer server(Eventloop eventloop, AsyncServlet servlet) {    return AsyncHttpServer.create(eventloop, servlet).withListenPort(PORT);  }
  @Override  protected Module getModule() {    return ServiceGraphModule.create();  }
  @Override  protected void run() throws Exception {    logger.info("HTTP Server is now available at http://localhost:" + PORT);    awaitShutdown();  }
  public static void main(String[] args) throws Exception {    Launcher launcher = new CustomHttpServerExample();    launcher.launch(args);  }}

See full example on GitHub

Working with program arguments

To access program arguments from the Launcher, you have to pass those arguments to Launcher#launch method. Then there are two ways to access the arguments:

  • Using Launcher's String[] args field
  • Requesting arguments using Dependency Injection. A key is String[].class annotated with @Args annotation. The following example illustrates both cases:
public final class LauncherArgsExample extends Launcher {
  @Inject  Injector injector;
  @Override  protected void run() {    System.out.println("Received args: " + Arrays.toString(args));
    String[] injectedArgs = injector.getInstance(Key.of(String[].class, Args.class));    System.out.println("Args retrieved from DI: " + Arrays.toString(injectedArgs));  }
  public static void main(String[] args) throws Exception {    new LauncherArgsExample().launch(args);  }}

See full example on GitHub