CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-dropwizard--dropwizard-core

Core components of the Dropwizard framework for building Java web applications

Pending
Overview
Eval results
Files

bundles.mddocs/

Bundle System

Dropwizard's bundle system provides reusable application components that can be registered during bootstrap to add functionality like SSL certificate reloading, asset serving, database migrations, and more.

Capabilities

ConfiguredBundle Interface

Base interface for creating reusable application components that can access configuration and modify the application environment.

/**
 * A reusable bundle of functionality, used to define blocks of application behavior
 * that are conditional on configuration parameters.
 * @param <T> the required configuration interface
 */
public interface ConfiguredBundle<T> {
    /**
     * Initializes the environment.
     * @param configuration the configuration object
     * @param environment the application's Environment
     * @throws Exception if something goes wrong
     */
    default void run(T configuration, Environment environment) throws Exception;
    
    /**
     * Initializes the application bootstrap.
     * @param bootstrap the application bootstrap
     */
    default void initialize(Bootstrap<?> bootstrap);
}

Usage Examples:

public class DatabaseBundle implements ConfiguredBundle<MyConfiguration> {
    @Override
    public void initialize(Bootstrap<?> bootstrap) {
        // Configure object mapper for database-specific serialization
        bootstrap.getObjectMapper().registerModule(new JavaTimeModule());
    }
    
    @Override
    public void run(MyConfiguration configuration, Environment environment) throws Exception {
        // Set up database connection pool
        final DataSource dataSource = configuration.getDataSourceFactory()
            .build(environment.metrics(), "database");
        
        // Register health check
        environment.healthChecks().register("database", 
            new DatabaseHealthCheck(dataSource));
        
        // Register managed object for connection pool lifecycle
        environment.lifecycle().manage(new DataSourceManager(dataSource));
    }
}

// Register in Application.initialize()
@Override
public void initialize(Bootstrap<MyConfiguration> bootstrap) {
    bootstrap.addBundle(new DatabaseBundle());
}

SslReloadBundle

Built-in bundle that provides SSL certificate reloading capability via an admin task, useful for certificate rotation without application restart.

/**
 * Bundle that gathers all the ssl connectors and registers an admin task that will
 * refresh ssl configuration on request.
 */
public class SslReloadBundle implements ConfiguredBundle<Configuration> {
    /**
     * Creates a new SSL reload bundle.
     */
    public SslReloadBundle();
    
    @Override
    public void run(Configuration configuration, Environment environment) throws Exception;
    
    @Override
    public void initialize(Bootstrap<?> bootstrap);
}

The SSL reload bundle automatically:

  • Discovers all SSL connectors in the server configuration
  • Registers an admin task at /tasks/ssl-reload for triggering certificate reloads
  • Provides logging for reload operations

Usage Examples:

@Override
public void initialize(Bootstrap<MyConfiguration> bootstrap) {
    // Add SSL reload capability
    bootstrap.addBundle(new SslReloadBundle());
}

After adding this bundle, you can reload SSL certificates by sending a POST request to the admin interface:

curl -X POST http://localhost:8081/tasks/ssl-reload

SslReloadTask

The admin task that performs the actual SSL certificate reloading operation.

/**
 * Admin task for reloading SSL certificates.
 */
public class SslReloadTask extends Task {
    /**
     * Creates a new SSL reload task.
     */
    public SslReloadTask();
    
    @Override
    public void execute(Map<String, List<String>> parameters, PrintWriter output) throws Exception;
}

This task is automatically registered by SslReloadBundle and can also be manually registered:

environment.admin().addTask(new SslReloadTask());

Creating Custom Bundles

Simple Bundle Example

public class MetricsBundle implements ConfiguredBundle<Configuration> {
    @Override
    public void initialize(Bootstrap<?> bootstrap) {
        // Configure metrics registry during bootstrap
        final MetricRegistry metrics = bootstrap.getMetricRegistry();
        metrics.register("jvm.uptime", new UptimeGauge());
    }
    
    @Override
    public void run(Configuration configuration, Environment environment) throws Exception {
        // Set up metrics reporters
        final ConsoleReporter reporter = ConsoleReporter.forRegistry(environment.metrics())
            .convertRatesTo(TimeUnit.SECONDS)
            .convertDurationsTo(TimeUnit.MILLISECONDS)
            .build();
        
        // Start reporting every 60 seconds
        reporter.start(60, TimeUnit.SECONDS);
        
        // Register as managed object to stop when application shuts down
        environment.lifecycle().manage(new Managed() {
            @Override
            public void start() throws Exception {
                // Already started above
            }
            
            @Override
            public void stop() throws Exception {
                reporter.stop();
            }
        });
    }
}

Parameterized Bundle Example

public class CorsBundle<T extends Configuration> implements ConfiguredBundle<T> {
    private final Function<T, CorsConfiguration> configExtractor;
    
    public CorsBundle(Function<T, CorsConfiguration> configExtractor) {
        this.configExtractor = configExtractor;
    }
    
    @Override
    public void run(T configuration, Environment environment) throws Exception {
        final CorsConfiguration corsConfig = configExtractor.apply(configuration);
        
        if (corsConfig.isEnabled()) {
            final FilterRegistration.Dynamic cors = environment.servlets()
                .addFilter("CORS", CrossOriginFilter.class);
            
            cors.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), true, "/*");
            cors.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, 
                String.join(",", corsConfig.getAllowedOrigins()));
            cors.setInitParameter(CrossOriginFilter.ALLOWED_HEADERS_PARAM,
                String.join(",", corsConfig.getAllowedHeaders()));
            cors.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM,
                String.join(",", corsConfig.getAllowedMethods()));
        }
    }
}

// Usage
@Override
public void initialize(Bootstrap<MyConfiguration> bootstrap) {
    bootstrap.addBundle(new CorsBundle<>(MyConfiguration::getCorsConfiguration));
}

Asset Bundle Example

public class AssetsBundle implements ConfiguredBundle<Configuration> {
    private final String resourcePath;
    private final String uriPath;
    private final String indexFile;
    
    public AssetsBundle(String resourcePath, String uriPath, String indexFile) {
        this.resourcePath = resourcePath;
        this.uriPath = uriPath;
        this.indexFile = indexFile;
    }
    
    @Override
    public void run(Configuration configuration, Environment environment) throws Exception {
        // Register servlet for serving static assets
        final ServletRegistration.Dynamic servlet = environment.servlets()
            .addServlet("assets", new AssetServlet(resourcePath, uriPath, indexFile));
        
        servlet.addMapping(uriPath + "*");
        servlet.setInitParameter("dirAllowed", "false");
        servlet.setInitParameter("etags", "true");
    }
}

// Usage
@Override  
public void initialize(Bootstrap<MyConfiguration> bootstrap) {
    // Serve static assets from /assets/ directory at /ui/ path
    bootstrap.addBundle(new AssetsBundle("/assets/", "/ui/", "index.html"));
}

Migration Bundle Example

public abstract class MigrationsBundle<T extends Configuration> implements ConfiguredBundle<T> {
    @Override
    public void initialize(Bootstrap<?> bootstrap) {
        // Add migration command to CLI
        bootstrap.addCommand(new DbMigrateCommand<>("db migrate", this));
        bootstrap.addCommand(new DbStatusCommand<>("db status", this));
    }
    
    @Override
    public void run(T configuration, Environment environment) throws Exception {
        // Register health check for database connectivity
        final DataSource dataSource = getDataSourceFactory(configuration)
            .build(environment.metrics(), "migrations");
            
        environment.healthChecks().register("database", 
            new DatabaseHealthCheck(dataSource));
    }
    
    /**
     * Override this method to provide the data source factory from your configuration.
     */
    public abstract DataSourceFactory getDataSourceFactory(T configuration);
}

// Usage
@Override
public void initialize(Bootstrap<MyConfiguration> bootstrap) {
    bootstrap.addBundle(new MigrationsBundle<MyConfiguration>() {
        @Override
        public DataSourceFactory getDataSourceFactory(MyConfiguration configuration) {
            return configuration.getDataSourceFactory();
        }
    });
}

Bundle Registration Order

Bundles are initialized and run in the order they are registered:

@Override
public void initialize(Bootstrap<MyConfiguration> bootstrap) {
    // These bundles will be initialized in this order
    bootstrap.addBundle(new DatabaseBundle());      // 1st
    bootstrap.addBundle(new SecurityBundle());      // 2nd  
    bootstrap.addBundle(new AssetsBundle());        // 3rd
    bootstrap.addBundle(new SslReloadBundle());     // 4th
}

During application startup:

  1. All bundles' initialize() methods are called in registration order
  2. Configuration is parsed and validated
  3. All bundles' run() methods are called in registration order
  4. Application's run() method is called

Bundle Best Practices

Configuration Integration

public class MyBundle implements ConfiguredBundle<MyConfiguration> {
    @Override
    public void run(MyConfiguration configuration, Environment environment) throws Exception {
        // Access bundle-specific configuration
        MyBundleConfig bundleConfig = configuration.getMyBundleConfig();
        
        if (bundleConfig.isEnabled()) {
            // Only configure if enabled
        }
    }
}

Resource Management

public class ResourceBundle implements ConfiguredBundle<Configuration> {
    @Override
    public void run(Configuration configuration, Environment environment) throws Exception {
        final ExpensiveResource resource = new ExpensiveResource();
        
        // Register as managed object for proper lifecycle
        environment.lifecycle().manage(new Managed() {
            @Override
            public void start() throws Exception {
                resource.start();
            }
            
            @Override
            public void stop() throws Exception {
                resource.close();
            }
        });
    }
}

Error Handling

public class SafeBundle implements ConfiguredBundle<Configuration> {
    @Override
    public void run(Configuration configuration, Environment environment) throws Exception {
        try {
            // Bundle initialization logic
        } catch (Exception e) {
            // Log error and optionally rethrow
            LOGGER.error("Failed to initialize SafeBundle", e);
            throw new RuntimeException("SafeBundle initialization failed", e);
        }
    }
}

Types

// Task base class for admin interface
public abstract class Task {
    protected Task(String name);
    public abstract void execute(Map<String, List<String>> parameters, PrintWriter output) throws Exception;
}

// Managed interface for lifecycle management
public interface Managed {
    void start() throws Exception;
    void stop() throws Exception;
}

// Common configuration interfaces
public interface DataSourceFactory {
    DataSource build(MetricRegistry metricRegistry, String name);
}

// SSL reload interfaces
public interface SslReload {
    void reload() throws Exception;
}

Install with Tessl CLI

npx tessl i tessl/maven-io-dropwizard--dropwizard-core

docs

bundles.md

cli.md

core-application.md

environment-setup.md

index.md

server-config.md

tile.json