Core components of the Dropwizard framework for building Java web applications
—
Dropwizard's extensible command-line interface system provides built-in commands for server management and configuration validation, along with support for custom commands.
Abstract base class for creating custom CLI commands that can be registered with the application.
/**
* A basic CLI command.
*/
public abstract class Command {
/**
* Create a new command with the given name and description.
* @param name the name of the command, used for command line invocation
* @param description a description of the command's purpose
*/
protected Command(String name, String description);
/**
* Returns the command's name.
* @return the command's name
*/
public final String getName();
/**
* Returns the command's description.
* @return the command's description
*/
public final String getDescription();
/**
* Configure the command's Subparser.
* @param subparser the Subparser specific to the command
*/
public abstract void configure(Subparser subparser);
/**
* Executes when the user runs this specific command.
* @param bootstrap the bootstrap
* @param namespace the parsed command line namespace
* @throws Exception if something goes wrong
*/
public abstract void run(Bootstrap<?> bootstrap, Namespace namespace) throws Exception;
/**
* Called if there is an issue parsing configuration, setting up the
* environment, or running the command itself.
* @param cli contains the streams for stdout and stderr
* @param namespace the parsed arguments from the commandline
* @param e The exception that was thrown when setting up or running the command
*/
public void onError(Cli cli, Namespace namespace, Throwable e);
}Usage Examples:
public class RenderCommand extends Command {
public RenderCommand() {
super("render", "Render the template");
}
@Override
public void configure(Subparser subparser) {
subparser.addArgument("-t", "--template")
.dest("template")
.help("template to render")
.required(true);
subparser.addArgument("-o", "--output")
.dest("output")
.help("output file")
.setDefault("output.html");
}
@Override
public void run(Bootstrap<?> bootstrap, Namespace namespace) throws Exception {
final String template = namespace.getString("template");
final String output = namespace.getString("output");
System.out.println("Rendering template: " + template);
System.out.println("Output file: " + output);
// Implementation here
}
}
// Register in Application.initialize()
@Override
public void initialize(Bootstrap<MyConfiguration> bootstrap) {
bootstrap.addCommand(new RenderCommand());
}Abstract command that loads and parses a configuration file before execution, providing the parsed configuration to the run method.
/**
* A command whose first parameter is the location of a YAML configuration file.
* @param <T> the Configuration subclass which is loaded from the configuration file
*/
public abstract class ConfiguredCommand<T extends Configuration> extends Command {
/**
* Creates a new configured command.
* @param name the command name
* @param description the command description
*/
protected ConfiguredCommand(String name, String description);
/**
* Returns the Class of the configuration type.
* @return the Class of the configuration type
*/
protected Class<T> getConfigurationClass();
/**
* Returns the parsed configuration or null if it hasn't been parsed yet.
* @return the parsed configuration or null
*/
public T getConfiguration();
/**
* Configure the command's Subparser. If you override this method, you must
* call super.configure(subparser) to preserve the configuration file parameter.
* @param subparser the Subparser specific to the command
*/
@Override
public void configure(Subparser subparser);
/**
* Adds the configuration file argument for the configured command.
* @param subparser The subparser to register the argument on
* @return the registered argument
*/
protected Argument addFileArgument(Subparser subparser);
/**
* Runs the command with the given Bootstrap and Configuration.
* @param bootstrap the bootstrap
* @param namespace the parsed command line namespace
* @param configuration the configuration object
* @throws Exception if something goes wrong
*/
protected abstract void run(Bootstrap<T> bootstrap,
Namespace namespace,
T configuration) throws Exception;
/**
* Mark the command for asynchronous cleanup.
*/
protected void cleanupAsynchronously();
/**
* Cleanup resources (called automatically unless cleanupAsynchronously() was called).
*/
protected void cleanup();
}Usage Examples:
public class DbMigrateCommand<T extends Configuration> extends ConfiguredCommand<T> {
private final String migrationsFileName;
private final Class<T> configurationClass;
public DbMigrateCommand(String name, Application<T> application, String migrationsFileName) {
super(name, "Run database migrations");
this.migrationsFileName = migrationsFileName;
this.configurationClass = application.getConfigurationClass();
}
@Override
protected Class<T> getConfigurationClass() {
return configurationClass;
}
@Override
public void configure(Subparser subparser) {
super.configure(subparser);
subparser.addArgument("--dry-run")
.action(storeTrue())
.dest("dryRun")
.help("Don't actually run migrations, just print what would be done");
}
@Override
protected void run(Bootstrap<T> bootstrap, Namespace namespace, T configuration) throws Exception {
final boolean dryRun = namespace.getBoolean("dryRun");
// Access configuration
MyConfiguration myConfig = (MyConfiguration) configuration;
DataSource dataSource = myConfig.getDataSourceFactory().build(bootstrap.getMetricRegistry(), "migrations");
if (dryRun) {
System.out.println("Would run migrations from: " + migrationsFileName);
} else {
System.out.println("Running migrations from: " + migrationsFileName);
// Run actual migrations
}
}
}Dropwizard provides several built-in commands that are automatically registered:
Starts the application server with the provided configuration.
/**
* Starts an HTTP server running a Dropwizard application.
* @param <T> the Configuration subclass
*/
public class ServerCommand<T extends Configuration> extends EnvironmentCommand<T> {
public ServerCommand(Application<T> application);
}Usage: java -jar myapp.jar server config.yml
Validates the configuration file without starting the server.
/**
* Validates a configuration file.
* @param <T> the Configuration subclass
*/
public class CheckCommand<T extends Configuration> extends ConfiguredCommand<T> {
public CheckCommand(Application<T> application);
}Usage: java -jar myapp.jar check config.yml
Abstract configured command that provides full environment setup including the Environment instance, useful for commands that need access to the full application context.
/**
* A command which provides access to the configured Environment.
* @param <T> the Configuration subclass
*/
public abstract class EnvironmentCommand<T extends Configuration> extends ConfiguredCommand<T> {
/**
* Creates a new environment command.
* @param application the application providing this command
* @param name the command name
* @param description the command description
*/
protected EnvironmentCommand(Application<T> application, String name, String description);
/**
* Runs the command with the given Environment.
* @param environment the configured environment
* @param namespace the parsed command line namespace
* @param configuration the configuration object
* @throws Exception if something goes wrong
*/
protected abstract void run(Environment environment, Namespace namespace, T configuration) throws Exception;
}Usage Examples:
public class DataExportCommand<T extends Configuration> extends EnvironmentCommand<T> {
public DataExportCommand(Application<T> application) {
super(application, "export-data", "Export application data");
}
@Override
public void configure(Subparser subparser) {
super.configure(subparser);
subparser.addArgument("--format")
.choices("json", "csv", "xml")
.setDefault("json")
.help("Export format");
}
@Override
protected void run(Environment environment, Namespace namespace, T configuration) throws Exception {
final String format = namespace.getString("format");
// Access environment services
HealthCheckRegistry healthChecks = environment.healthChecks();
MetricRegistry metrics = environment.metrics();
// Access configuration
MyConfiguration myConfig = (MyConfiguration) configuration;
System.out.println("Exporting data in " + format + " format");
// Implementation here
}
}The main CLI runner that processes command-line arguments and executes the appropriate command.
/**
* The main CLI runner for Dropwizard applications.
*/
public class Cli {
/**
* Creates a new CLI for the given application.
* @param location the application's JAR location
* @param bootstrap the application bootstrap
* @param stdOut the standard output stream
* @param stdErr the standard error stream
*/
public Cli(JarLocation location, Bootstrap<?> bootstrap, PrintStream stdOut, PrintStream stdErr);
/**
* Runs the command line interface with the given arguments.
* @param arguments the command line arguments
* @return Optional containing throwable if there was an error, empty otherwise
*/
public Optional<Throwable> run(String... arguments);
/**
* Returns the standard output stream.
* @return the stdout stream
*/
public PrintStream getStdOut();
/**
* Returns the standard error stream.
* @return the stderr stream
*/
public PrintStream getStdErr();
}Commands are registered during application initialization:
@Override
public void initialize(Bootstrap<MyConfiguration> bootstrap) {
// Register custom commands
bootstrap.addCommand(new RenderCommand());
bootstrap.addCommand(new DbMigrateCommand<>(this, "migrations.xml"));
bootstrap.addCommand(new DataExportCommand<>(this));
}# Run the server
java -jar myapp.jar server config.yml
# Check configuration
java -jar myapp.jar check config.yml
# Run custom commands
java -jar myapp.jar render -t template.html -o output.html
java -jar myapp.jar export-data --format csv config.yml
java -jar myapp.jar db migrate --dry-run config.yml
# Get help
java -jar myapp.jar --help
java -jar myapp.jar server --help// Argument parsing types from argparse4j
public interface Subparser {
Argument addArgument(String... nameOrFlags);
void setDefault(String dest, Object value);
void help(String help);
}
public interface Argument {
Argument dest(String dest);
Argument help(String help);
Argument required(boolean required);
Argument setDefault(Object defaultValue);
Argument choices(Object... choices);
Argument action(ArgumentAction action);
}
public interface Namespace {
String getString(String dest);
Boolean getBoolean(String dest);
Integer getInt(String dest);
Object get(String dest);
}Install with Tessl CLI
npx tessl i tessl/maven-io-dropwizard--dropwizard-core