Dropwizard Internals

You already read through the whole Dropwizard documentation? Congrats! Then you are ready to have a look into some nitty-gritty details of Dropwizard.

Startup Sequence

Application<T extends Configuration> is the “Main” class of a Dropwizard Application.

application.run(args) is the first method to be called on startup - Here is a simplified code snippet of its implementation:

public void run(String... arguments) throws Exception {

  final Bootstrap<T> bootstrap = new Bootstrap<>(this);
  bootstrap.addCommand(new ServerCommand<>(this));
  bootstrap.addCommand(new CheckCommand<>(this));

  initialize(bootstrap); // -- implemented by you; it should call:
        // 1. add bundles (typically being used)
        // 2. add commands (if any)

  // Should be called after `initialize` to give an opportunity to set a custom metric registry
  bootstrap.registerMetrics(); // start tracking some default jvm params…

  // for each cmd, configure parser w/ cmd
  final Cli cli = new Cli(new JarLocation(getClass()), bootstrap, our, err)
  cli.run(arguments);
}

Bootstrap is the pre-start (temp) application environment, containing everything required to bootstrap a Dropwizard command. Here is a simplified code snippet to illustrate its structure:

Bootstrap(application: Application<T>) {
  this.application = application;
  this.objectMapper = Jackson.newObjectMapper();
  this.bundles = new ArrayList<>();
  this.configuredBundles = new ArrayList<>();
  this.commands = new ArrayList<>();
  this.validatorFactory = Validators.newValidatorFactory();
  this.metricRegistry = new MetricRegistry();
  this.classLoader = Thread.currentThread().getContextClassLoader();
  this.configurationFactory = new DefaultConfigurationFactoryFactory<>();
  this.healthCheckRegistry = new HealthCheckRegistry();
}

Environment is a longer-lived object, holding Dropwizard’s Environment (not env. Such as dev or prod). It holds a similar, but somewhat different set of properties than the Bootstrap object - here is a simplified code snippet to illustrate that:

Environment (...) {
  // from bootstrap
  this.objectMapper = ...
  this.classLoader = ...
  this.metricRegistry = ...
  this.healthCheckRegistry = ...
  this.validator = bootstrap.getValidatorFactory().getValidator()

  // extra:
  this.bundles = new ArrayList<>();
  this.configuredBundles = new ArrayList<>();

  // sub-environments:
  this.servletEnvironment = ... // -- exposed via the servlets() method
  this.jerseyEnvironment = ... // -- exposed via the jersey() method
  this.adminEnvironment = ... // -- exposed via the admin() method

}

A Dropwizard Bundle is a reusable group of functionality (sometimes provided by the Dropwizard project itself), used to define blocks of an application’s behavior. For example, AssetBundle from the dropwizard-assets module provides a simple way to serve static assets from your application’s src/main/resources/assets directory as files available from /assets/* (or any other path) in your application.

A ConfiguredBundle is a bundle that requires a configuration provided by the Configuration object (implementing a relevant interface)

Properties such as database connection details should not be stored on the Environment; that is what your Configuration .yml file is for. Each logical environment (dev/test/staging/prod) - would have its own Configuration .yml - reflecting the differences between different “server environments”.

Commands

Command objects are basic actions, which Dropwizard runs based on the arguments provided on the command line. The built-in server command, for example, spins up an HTTP server and runs your application. Each Command subclass has a name and a set of command line options which Dropwizard will use to parse the given command line arguments. The check command parses and validates the application’s configuration.

If you will check again the first code snippet in this document - you will see creating these two commands, is the first step in the bootstrapping process.

Another important command is db - allowing executing various db actions, see Dropwizard Migrations

Similar to ConfiguredBundle, some commands require access to configuration parameters and should extend the ConfiguredCommand class, using your application’s Configuration class as its type parameter.

The CLI class

Let us begin with a simplified version of the constructor:

public Cli(location : JarLocation, bootstrap : Bootstrap<?>,
                   stdOut: OutputStream, stdErr: OutputStream) {
  this.stdout = stdOut; this.stdErr = stdErr;
  this.commands = new TreeMap<>();
  this.parser = buildParser(location);
  this.bootstrap = bootstrap;
  for (command in bootstrap.commands) {
        addCommand(command)
  }
}

Cli is the command-line runner for Dropwizard application. Initializing, and then running it - is the last step of the Bootstrapping process.

Run would just handle commandline args (–help, –version) or runs the configured commands. E.g. - When running the server command:

java -jar target/hello-world-0.0.1-SNAPSHOT.jar server hello-world.yml

Just note the two basic commands are built of a parent, and a sub-class:

class CheckCommand<T extends Configuration> extends ConfiguredCommand<T>
class ServerCommand<T extends Configuration> extends EnvironmentCommand<T>

The order of operations is therefore:

  1. Parse cmdline args, determine sub-command.

  2. Run ConfiguredCommand, which get a parameter with the location of a YAML configuration file - parses and validates it.

  3. CheckCommand.run() runs next, and does almost nothing: it logs "Configuration is OK"

  4. Run EnvironmentCommand:

  1. Create Environment

  2. Calls bootstrap.run(cfg, env) - run bundles with config. & env.

  3. Bundles run in FIFO order.

  4. Calls application.run(cfg, env) – implemented by you

  1. Now, ServerCommand.run() runs

  1. Calls serverFactory.build(environment) - to configure Jetty and Jersey, with all relevant Dropwizard modules.

  2. Starts Jetty.

Jetty Lifecycle

If you have a component of your app that needs to know when Jetty is going to start, you can implement Managed as described in the Dropwizard docs.

If you have a component that needs to be signaled that Jetty has started (this happens after all Managed objects’ start() methods are called), you can register with the env’s lifecycle like:

env.lifecycle().addServerLifecycleListener(new ServerLifecycleListener() {
    @Override
    public void serverStarted(Server server) {
              /// ... do things here ....
    }
});