1

I have essentially the same question as here but am hoping to get a less vague, more informative answer.

I'm looking for a way to configure DropWizard programmatically, or at the very least, to be able to tweak configs at runtime. Specifically I have a use case where I'd like to configure metrics in the YAML file to be published with a frequency of, say, 2 minutes. This would be the "normal" default. However, under certain circumstances, I may want to speed that up to, say, every 10 seconds, and then throttle it back to the normal/default.

How can I do this, and not just for the metrics.frequency property, but for any config that might be present inside the YAML config file?

smeeb
  • 22,487
  • 41
  • 197
  • 389

3 Answers3

1

Dropwizard reads the YAML config file and configures all the components only once on startup. Neither the YAML file nor the Configuration object is used ever again. That means there is no direct way to configure on run-time.

It also doesn't provide special interfaces/delegates where you can manipulate the components. However, you can access the objects of the components (usually; if not you can always send a pull request) and configure them manually as you see fit. You may need to read the source code a bit but it's usually easy to navigate.

In the case of metrics.frequency you can see that MetricsFactory class creates ScheduledReporterManager objects per metric type using the frequency setting and doesn't look like you can change them on runtime. But you can probably work around it somehow or even better, modify the code and send a Pull Request to dropwizard community.

Natan
  • 2,620
  • 14
  • 29
0

Although this feature isn't supported out of the box by dropwizard, you're able to accomplish this fairly easy with the tools they give you. Note that the below solution definitely works on config values you've provided, but it may not work for built in configuration values.

Also note that this doesn't persist the updated config values to the config.yml. However, this would be easy enough to implement yourself simply by writing to the config file from the application. If anyone would like to write this implementation feel free to open a PR on the example project I've linked below.

Code

Start off with a minimal config:

config.yml

myConfigValue: "hello"

And it's corresponding configuration file:

ExampleConfiguration.java

public class ExampleConfiguration extends Configuration {
    private String myConfigValue;

    public String getMyConfigValue() {
        return myConfigValue;
    }

    public void setMyConfigValue(String value) {
        myConfigValue = value;
    }
}

Then create a task which updates the config:

UpdateConfigTask.java

public class UpdateConfigTask extends Task {
    ExampleConfiguration config;

    public UpdateConfigTask(ExampleConfiguration config) {
        super("updateconfig");
        this.config = config;
    }

    @Override
    public void execute(Map<String, List<String>> parameters, PrintWriter output) {
        config.setMyConfigValue("goodbye");
    }
}

Also for demonstration purposes, create a resource which allows you to get the config value:

ConfigResource.java

@Path("/config")
public class ConfigResource {
    private final ExampleConfiguration config;

    public ConfigResource(ExampleConfiguration config) {
        this.config = config;
    }

    @GET
    public Response handleGet() {
        return Response.ok().entity(config.getMyConfigValue()).build();
    }
}

Finally wire everything up in your application:

ExampleApplication.java (exerpt)

environment.jersey().register(new ConfigResource(configuration));
environment.admin().addTask(new UpdateConfigTask(configuration));

Usage

Start up the application then run:

$ curl 'http://localhost:8080/config'
hello
$ curl -X POST 'http://localhost:8081/tasks/updateconfig'
$ curl 'http://localhost:8080/config'
goodbye

How it works

This works simply by passing the same reference to the constructor of ConfigResource.java and UpdateConfigTask.java. If you aren't familiar with the concept see here: Is Java "pass-by-reference" or "pass-by-value"?

The linked classes above are to a project I've created which demonstrates this as a complete solution. Here's a link to the project:

scottg489/dropwizard-runtime-config-example

Footnote: I haven't verified this works with the built in configuration. However, the dropwizard Configuration class which you need to extend for your own configuration does have various "setters" for internal configuration, but it may not be safe to update those outside of run().



Disclaimer: The project I've linked here was created by me.

scottg489
  • 189
  • 1
  • 5
0

I solved this with bytecode manipulation via Javassist In my case, I wanted to change the "influx" reporter and modifyInfluxDbReporterFactory should be ran BEFORE dropwizard starts

private static void modifyInfluxDbReporterFactory() throws Exception {
    ClassPool cp = ClassPool.getDefault();
    CtClass cc = cp.get("com.izettle.metrics.dw.InfluxDbReporterFactory"); // do NOT use InfluxDbReporterFactory.class.getName() as this will force the class into the classloader
    CtMethod m = cc.getDeclaredMethod("setTags");
    m.insertAfter(
                "if (tags.get(\"cloud\") != null) tags.put(\"cloud_host\", tags.get(\"cloud\") + \"_\" + host);tags.put(\"app\", \"sam\");");
    cc.toClass();
}
Henry Chan
  • 11
  • 2