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.