Blake Smith

create. code. learn.

»

Running Dropwizard as a Guava Service

There are many things to like about the Dropwizard Framework, but if you’re like me, you might want to “own your main” function. A normal Dropwizard application gives you a run hook as part of its Application parent class, but in the end your code is still subservient to the Dropwizard framework code.

One pattern that is very good for organizing small logical services in your application is to use Guava’s Services to break your application into service level groupings (And avoid the drinking the microservice kool-aid prematurely). Guava services give you a common operational interface to coordinate all your in-process logical components. In this model, the Dropwizard web server is no more important than my periodic polling service, or my separated RPC service, and so on. I’d like my Dropwizard web stack to be a peer to the other services that I’m running inside my application. It only takes two steps to make this work.

Step 1: Create the Guava Service

Create a new class that extends AbstractIdleService. When we implement the startUp method in the service, we will need to handle the key bootstrap setup that normally is handled within the Dropwizard framework when you invoke the server command.

import com.codahale.metrics.MetricRegistry;

import com.google.common.util.concurrent.AbstractIdleService;
import com.google.inject.Guice;
import com.google.inject.Inject;

import com.google.inject.Injector;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;

import org.eclipse.jetty.server.Server;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DashboardService extends AbstractIdleService {
    private static final Logger logger = LoggerFactory.getLogger(DashboardService.class);
    private final DashboardApplication application;
    private final DashboardConfiguration config;
    private final MetricRegistry metrics;
    private Server server;

    @Inject
    public DashboardService(final DashboardApplication application,
                            final DashboardConfiguration config,
                            final MetricRegistry metrics) {
        this.application = application;
        this.config = config;
        this.metrics = metrics;
    }

    @Override
    protected void startUp() throws Exception {
        logger.info("Starting DashboardService");
        final Bootstrap<DashboardConfiguration> bootstrap = new Bootstrap<>(application);
        application.initialize(bootstrap);
        final Environment environment = new Environment(bootstrap.getApplication().getName(),
                                                        bootstrap.getObjectMapper(),
                                                        bootstrap.getValidatorFactory().getValidator(),
                                                        metrics,
                                                        bootstrap.getClassLoader());
        bootstrap.run(config, environment);
        application.run(config, environment);
        this.server = config.getServerFactory()
            .build(environment);
        server.start();
    }

    @Override
    protected void shutDown() throws Exception {
        logger.info("Stopping DashboardService");
        server.stop();
    }
}

Step 2: Reinitialize logging inside the Dropwizard Application

public class DashboardApplication extends Application<DashboardConfiguration> {
    @Override
    public void run(DashboardConfiguration config,
                    Environment environment) {
        reinitializeLogging(environment);
    }

    /**
     * Because Dropwizard clears our logback settings, reload them.
     */

    private void reinitializeLogging(Environment env) {
        LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
        try {
            JoranConfigurator configurator = new JoranConfigurator();
            configurator.setContext(context);
            context.reset();
            String logBackConfigPath = System.getProperty("logback.configurationFile");
            if (logBackConfigPath != null) {
                configurator.doConfigure(logBackConfigPath);
            }
        } catch (JoranException e) {
            throw new RuntimeException("Unable to initialize logging.", e);
        }
    }
}

Start your Guava Service

Now you have a nicely contained Guava service that you can manage alongside your other Guava service that aren’t necessarily Dropwizard related. You can startAsync and stopAsync the service to start and stop the web server, even while other services are still running.


about the author

Blake Smith is a Principal Software Engineer at Sprout Social.