dropwizard/dropwizard

View on GitHub
docs/source/manual/internals.rst

Summary

Maintainability
Test Coverage
.. _man-internals:

####################
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:

.. code-block:: java

    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:

.. code-block:: java

    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:

.. code-block:: java

    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 :ref:`man-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:

.. code-block:: java

    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:

.. code-block:: bash

  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:

.. code-block:: java

  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``:

  a) Create ``Environment`` 
  b) Calls ``bootstrap.run(cfg, env)`` - run bundles with config. & env.
  c) Bundles run in FIFO order.
  d) Calls ``application.run(cfg, env)`` -- implemented by you
  
6. Now, ``ServerCommand.run()`` runs

  a) Calls ``serverFactory.build(environment)`` - to configure Jetty and Jersey, with all relevant Dropwizard modules.
  b) 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:

.. code-block:: java

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