Running Background Tasks In DropWizard with Guava

Recently, I wrote a RESTful web service in Java 8 using Dropwizard.
Dropwizard is a breath of fresh air, a simple, lightweight, getting-things-done approach to RESTful web service design. In this post I will demonstrate how to combine Dropwizard’s Managed objects with Guava’s concurrency primitives to write a simple background task for each of our instances

The Specifics

Guava provides com.google.common.util.concurrent.AbstractScheduledService,
an abstraction for a periodic task that runs at it’s own background thread at certain intervals and can be started and stopped from the main thread. Here is one that logs to the console every 5 seconds

public class ConsoleLoggingScheduledTask extends AbstractScheduledService {

    private final Logger LOGGER = LoggerFactory.getLogger(ScheduledSynchronizationService.class);

    @Override
    protected void runOneIteration() throws Exception {
        LOGGER.info("runOneIteration");
    }

    @Override
    protected AbstractScheduledService.Scheduler scheduler() {
        return AbstractScheduledService.Scheduler.newFixedRateSchedule(0, 5, TimeUnit.SECONDS);
    }
}

Dropwizard provides the Managed interface for objects that need to be started
and stopped as the application instance is started or stopped.
It is easy to create a container/adapter for an AbstractScheduledService subclass like the one in the above example and enable it to be managed by Dropwizard.

public class ManagedPeriodicTask implements Managed {

    private final Logger LOGGER = LoggerFactory.getLogger(ManagedPeriodicTask.class);
    private final AbstractScheduledService periodicTask;

    public ManagedPeriodicTask(AbstractScheduledService ) {
        this.periodicTask = periodicTask;
    }

    @Override
    public void start() throws Exception {
        periodicTask.startAsync().awaitRunning();
    }

    @Override
    public void stop() throws Exception {
        periodicTask.stopAsync().awaitTerminated();
    }
}

Then on the run method of our Application subclass we can just do the following

final ConsoleLoggingScheduledTask periodicTask = new ConsoleLoggingScheduledTask();
final Managed managedImplementer = new ManagedPeriodicTask(periodicTask);
environment.lifecycle().manage(managedImplementer);

So, there you have it! A simple way to create background (semi) periodic tasks that are managed (started and stopped) by dropwizard, for usages such as gossip, consensus or asynchronous tasks like emailing and logging.

Flask configs per environment

When working on a Flask project, you may want to override flask configurations
on different environments. Here are some suggestions.

A folder structure like the following might work well

/demo/
     __init__.py
     configs/
             __init__.py
             application.id
             default.py
             development.py
             production.py
             staging.py
             test.py
     controllers.py
     templates/
               index.html

In this structure, configs are plain python files and default is always loaded.
It is then merged with the environment-specific configuration (e.g. development).

It helps if the flask application is instantiated by a factory method/function.
Here are some ways to select the environment, each with higher precendence
than the previous one.

  • read the contents of a file (in this example application.id)
  • pass an environment variable (e.g. APP_ENV)
  • Override with a variable (useful for testing)

    from os import path
    from flask import Flask, Blueprint
    from flask import current_app

    root = Blueprint(“root”, name)

    @root.route(“/”)
    def root_index():
    return “Hello from %s” % current_app.config.get(“MYCONFIG”)

    basepath = path.abspath(path.dirname(file))

    CONFIGS = “demo.configs”

    def config_object_for(override=None):

    if override:
        return ".".join([CONFIGS, override])
    
    from os import environ
    
    try:
        with open(path.join(basepath, 'configs/application.id')) as appid:
            env = appid.read().strip()
    except Exception, e:
        pass
    
    environment_variable = environ.get("APP_ENV")
    if environment_variable:
        env = environment_variable or "development"
    
    return ".".join([CONFIGS, env])
    

    def create_app(override=None):

    app = Flask(__name__)
    app.config.from_object(config_object_for('default'))
    app.config.from_object(config_object_for(override))
    app.register_blueprint(root)
    
    return app
    

    if name == ‘main‘:
    create_app().run(debug=True)

%d bloggers like this: