diff --git a/docs/3.x/bootique-docs/index.html b/docs/3.x/bootique-docs/index.html index 23c367d4..c749c3a5 100644 --- a/docs/3.x/bootique-docs/index.html +++ b/docs/3.x/bootique-docs/index.html @@ -129,12 +129,11 @@

  • 2. Programming
  • 3. Testing
    -

    It is helpful for you to think of your own code as being "organized into modules" as well. An example of a simple custom module is shown below with an example of an object binding API that will be discussed later:

    +

    It is helpful for you to think of your own code as being "organized into modules" as well. A simple custom module is shown below with an example of an object binding API that will be discussed later:

    @@ -314,7 +332,7 @@

    2.1. Modules

    - By convention, all classes logically belonging to a module are packaged in a separate .jar, though nothing prevents you from keeping multiple BQModules in a single .jar, or spreading the module code across multiple jars. Other than autoloading descriptors explained below, there are no special packaging requirements for modules. When they are combined in runtime, it is all a single classpath. + By convention, all classes that logically belong to a module are packaged in a separate .jar. Though nothing prevents you from keeping multiple BQModules in a single .jar, or spreading your code across multiple jars. Other than autoloading descriptors explained below, there are no special packaging requirements for modules. They are all loaded into a single BQRuntime. @@ -403,9 +421,6 @@

    2.1.1. Modules (Auto) Loading

    If you have more than one module in a given project, you should put all the names in this file, one per line. The result of autoloading, is that you can manage inclusion/exclusion of modules purely via the build system instead of in the code. Auto-loading is the recommended mechanism for assembling your app in almost all cases. All the standard Bootique modules are autoloadable.

    -
    -

    If all your code is packaged in auto-loadable modules, you may not even need a custom main class. io.bootique.Bootique class itself declares a main() method and can be used as an app launcher. This creates some interesting possibilities. E.g. you can create Java projects that have no code of their own and are simply collections of modules declared as compile dependencies.

    -

    2.1.2. Modules Metadata

    @@ -434,9 +449,9 @@

    2.1.3. Deprecating Modules

    -

    2.2. Application Objects

    +

    2.2. Objects "Assembly"

    -

    As mentioned above, modules are object assembly blueprints. When implementing a module, you write Java code that defines how your objects should be created and initialized when the app is started. This is done via bindings and provider methods, and with the assistance of the dependency injection (DI) mechanism built into Bootique.

    +

    As mentioned above, BQRuntime is a holder of all application objects, and modules are blueprints to create those objects. You’d write your own module code to define how your objects should be created and initialized. This is done via bindings and "provider" methods, and with the assistance of the dependency injection (DI) mechanism built into Bootique.

    2.2.1. Dependency Injection

    @@ -475,7 +490,7 @@

    2.2.1. Dependency Injection

    public class HelloService2 implements Hello {
     
    -    private UserNameService nameService;
    +    private final UserNameService nameService;
     
         @Inject
         public HelloService2(UserNameService nameService) {
    @@ -511,41 +526,11 @@ 

    2.2.2. Bindings

    This creates a permanent association between Hello interface and a specific implementation. Hello can now be injected into other services. A single instance of HelloService2 is created automatically by the DI container, with a real object transparently provided for the "nameService".

    -
    -

    If the service construction requires additional logic, instead of binding service implementation, you can create and bind a Java class that implements Provider<Service>. E.g.:

    -
    -
    -
    -
    public class HelloService3Provider implements Provider<Hello> {
    -
    -    @Inject
    -    private UserNameService nameService;
    -
    -    @Override
    -    public Hello get() {
    -        return new HelloService3(nameService);
    -    }
    -}
    -
    -
    -
    -
    -
    @Override
    -public void configure(Binder binder) {
    -    binder.bind(Hello.class)
    -            .toProvider(HelloService3Provider.class)
    -            .inSingletonScope();
    -}
    -
    -
    -
    -

    Provider class has the same rules for injection of its own dependencies as regular objects (either annotated fields or annotated constructor). The benefit of the provider is that it can implement custom assembly logic, which is separate from the service itself. It also allows the service it creates to be completely free from injection annotations. Still creating a provider class may be an overkill, and there is a simpler way to define small bits of assembly code - "provider methods" discussed next.

    -
    -

    2.2.3. Provider Module Methods

    +

    2.2.3. Provider Methods in Modules

    -

    Any method of a module class annotated with @Provides is treated as an object declaration. E.g.:

    +

    If the service construction requires additional assembly logic, you can create simple "provider" methods in the module class. Any method of a module class annotated with @Provides is treated a "provider" method. E.g.:

    @@ -572,7 +557,7 @@

    2.2.3. Provider Module Methods

    2 - configure(..) method can be empty, as the service was already bound via provideHello. Of course, it can also contain bindings for unrelated services. + configure(..) method may be empty, or contain other bindings @@ -582,31 +567,68 @@

    2.2.3. Provider Module Methods

    -

    2.2.4. Injection in Collections

    +

    2.2.4. Provider Objects

    +
    +

    If more complex logic or lots of dependencies are required, instead of a provider method, you can create a provider class that implements Provider<MyService>. E.g.:

    +
    +
    +
    +
    public class HelloService3Provider implements Provider<Hello> {
    +
    +    @Inject
    +    private UserNameService nameService;
    +
    +    @Override
    +    public Hello get() {
    +        return new HelloService3(nameService);
    +    }
    +}
    +
    +
    +
    +

    It needs to be bound to an object declaration via toProvider(..) method;

    +
    +
    +
    +
    @Override
    +public void configure(Binder binder) {
    +    binder.bind(Hello.class)
    +            .toProvider(HelloService3Provider.class)
    +            .inSingletonScope();
    +}
    +
    +
    +
    +

    Provider object has the same rules for injection of its own dependencies as regular objects, i.e. either via annotated fields or an annotated constructor.

    +
    +
    +
    +

    2.2.5. Injection in Collections

    TODO

    -

    2.2.5. Injection and Generics

    +

    2.2.6. Injection and Generics

    TODO

    -

    2.3. Configuration and Configurable Factories

    +

    2.3. Configuration

    -

    Bootique Modules obtain their configuration in a form of "factory objects". We’ll show some examples shortly. For now let’s focus on the big picture, namely the fact that Bootique app configuration is multi-layered and roughly follows the sequence of "code - config files (contributed) - config files (CLI) - overrides". "Code" is the default values that are provided in constructors of factory objects. Config files overlay those defaults with their own values. Config files can be either contributed in the code, or specified on the command line. Files is where the bulk of configuration usually stored. Finally, config values may be further overridden via Java properties and/or environment variables.

    +

    Bootique app can accept external configuration in different forms, coming from different sources. Internally, all configuration sources are combined following the rules described below into a single JSON-like tree object, that is used to initialize properties of some application objects.

    -
    -

    2.3.1. Configuration via YAML Files

    -
    -

    Format of configuration file can be either JSON or YAML. For simplicity, we’ll focus on YAML format, but the two are interchangeable. Here is an example config file:

    -
    -
    -
    -
    log:
    +   
    +

    Common configuration formats are JSON or YAML. For simplicity, we’ll focus on YAML, but the two are interchangeable. Other sources are shell variables, Java properties and even cloud secrets managers. No matter which source (or combination of them) you use, all of them end up in a single tree object whose nodes match the structures of the application Java objects they will ultimately be converted to.

    +
    +
    +

    Here is an example YAML file:

    +
    +
    +
    +
    log:
       level: warn
       appenders:
         - type: file
    @@ -617,134 +639,206 @@ 

    2.3.1. Configuration via YAML Files

    context: /myapp connectors: - port: 12009
    -
    +
    +
    +

    By convention, the top-level keys correspond to the names of modules that use a given configuration. In the example above, log subtree configures bootique-logback module, while jetty configures bootique-jetty.

    +
    +
    + + + + + + + +
    For most standard modules, configuration formats are described in the module documentation. But each Bootique application contains a -H command that displays the full config structure for that app’s collection of modules. The output of this command is always the most accurate reference.
    +
    +
    +

    2.3.1. Configuration Basics

    -

    While not strictly required, as a rule the top-level keys in the file belong to configuration objects of individual modules. In the example above "log" subtree configures bootique-logback module, while "jetty" subtree configures bootique-jetty. For standard modules refer to module-specific documentation on the structure of the supported configuration (or run your app -H flag to print supported config to the console). Here we’ll discuss how to build your own configuration-aware module.

    +

    Consider the following YAML:

    +
    +
    +
    +
    my:
    +  intProperty: 55
    +  stringProperty: 'Hello, world!'
    +
    -

    Bootique allows each Module to read its specific configuration subtree as an object of the type defined in the Module. Very often such an object is written as a factory that contains a bunch of setters for configuration properties, and a factory method to produce some "service" a Module is interested in. Here is an example factory:

    +

    and the following object with matching property names:

    -
    public class MyFactory {
    +      
    @BQConfig (1)
    +public class MyObject {
    +
    +    final SomeOtherService soService;
     
         private int intProperty;
         private String stringProperty;
     
    +    @Inject
    +    public MyObject(SomeOtherService soService) {  (2)
    +        this.soService = soService;
    +    }
    +
    +    @BQConfigProperty (3)
         public void setIntProperty(int i) {
             this.intProperty = i;
         }
     
    +    @BQConfigProperty (4)
         public void setStringProperty(String s) {
             this.stringProperty = s;
         }
     
    -    // factory method
    -    public MyService createMyService(SomeOtherService soService) {
    -        return new MyServiceImpl(soService, intProperty, stringProperty);
    +    public void doSomething() {
    +        // ..
         }
     }
    +
    + + + + + + + + + + + + + + + + + + + +
    1The optional BQConfig annotation helps to include this object in the output of the -H command
    2Objects created from configuration can also inject any dependencies just like other Bootique objects
    3A setter used to load intProperty into the object. The optional BQConfigProperty annotation ensures the property is included in the output of the -H command
    4A setter used to load stringProperty into the object. The optional BQConfigProperty annotation ensures the property is included in the output of the -H command
    +
    -

    The factory contains configuration property declarations, as well as public setters for these properties. (You may create getters as well. It is not required, but may be useful for unit tests, etc.). Now let’s take a look at the Module class:

    +

    To create MyObject and load configuration values into it, we can use the following API:

    -
    public class MyModule extends BaseModule {
    +      
    @Singleton
    +@Provides
    +public MyObject createMyService(ConfigurationFactory configFactory) {
    +    return configFactory.config(MyObject.class, "my");
    +}
    +
    +
    +
    +

    In this example, MyObject obtains its properties separately from constructor injection and configuration. ConfigurationFactory is the object available in the core Bootique that holds the main config tree and serves as a factory for configuration-aware objects. The structure of MyObject is very simple, but it can be as complex as needed, containing nested objects, arrays, maps, etc. Internally Bootique uses Jackson framework to bind YAML/JSON to Java objects, so all the features of Jackson can be used to craft configuration.

    +
    +
    +
    +

    2.3.2. Object Factories

    +
    +

    Very often configuration-aware objects are not retained by the app, but are created only to serve as factories of other objects, and then discarded. For instance, we can turn MyObject above into MyFactory that creates an object called MyService:

    +
    +
    +
    +
    @BQConfig
    +public class MyFactory {
     
    -    @Singleton
    -    @Provides
    -    public MyService createMyService(
    -            ConfigurationFactory configFactory,
    -            SomeOtherService service) {
    +    private int intProperty;
    +    private String stringProperty;
     
    -        return config(MyFactory.class, configFactory).createMyService(service);
    +    @BQConfigProperty
    +    public void setIntProperty(int i) {
    +        this.intProperty = i;
    +    }
    +
    +    @BQConfigProperty
    +    public void setStringProperty(String s) {
    +        this.stringProperty = s;
    +    }
    +
    +    // factory method
    +    public MyService createMyService(SomeOtherService soService) {
    +        return new MyServiceImpl(soService, intProperty, stringProperty);
         }
     }
    -
    -

    A sample configuration that will work with our module may look like this:

    -
    -
    my:
    -  intProperty: 55
    -  stringProperty: 'Hello, world!'
    +
    @Singleton
    +@Provides
    +public MyService provideMyService(
    +        ConfigurationFactory configFactory,
    +        SomeOtherService service) {
    +
    +    return configFactory.config(MyFactory.class, "my").createMyService(service);
    +}
    -

    A few points to note here:

    +

    Also, here instead of injecting SomeOtherService into the factory, we injected it in the provider method. This is just another flavor. This is not specific to factories of course.

    -
    -
      -
    • -

      Subclassing from BaseModule provides a few utilities, such as a shorter "config" method and a default configuration key ("my" in this case. See the next bullet).

    • -
    • -

      Calling our module "MyModule" and extending from BaseModule gives it access to the protected "configPrefix" property that is initialized to the value of "my" based on the module class name. The naming convention here is to use the Module simple class name without the "Module" suffix and converted to lowercase.

    • -
    • -

      @Provides annotation is a Bootique DI way of marking a BQModule method as a "provider" for a certain type of injectable service. All its parameters are themselves injectable objects.

    • -
    • -

      ConfigurationFactory is the class used to bind a subtree of the app YAML configuration to a given Java object (in our case - MyFactory). The structure of MyFactory is very simple here, but it can be as complex as needed, containing nested objects, arrays, maps, etc. Internally Bootique uses Jackson framework to bind YAML to a Java class, so all the features of Jackson can be used to craft configuration.

    • -
    +
    + + + + + + + +
    All configuration-aware objects in Bootique standard modules are factories. We think this is the best pattern, and we recommend it to everyone, as it separates the actual application object from its configuration and factory code. This results in more "compact" and fully immutable objects. Though of course, the choice is ultimately left to the user.
    -

    2.3.2. Configuration File Loading

    +

    2.3.3. Configuration Loading

    -

    A config file can be passed to a Bootique app via DI (those are usually coming from classpath) or on the command line:

    +

    Configuration is multi-layered and can come from different sources:

    • -

      Contributing a config file via DI:

      -
      -
      -
      BQCoreModule.extend(binder).addConfig("classpath:com/foo/default.yml");
      -
      -
      -
      -

      A primary motivation for this style is to provide application default configuration, with YAML files often embedded in the app and read from the classpath (as suggested by the "classpath:.." URL in the example). More than one configuration can be contributed. E.g. individual modules might load their own defaults. Multiple configs are combined in a single config tree by the runtime. The order in which this combination happens is undefined, so make sure there are no conflicts between them. If there are, consider replacing multiple conflicting configs with a single config.

      -
    • +

      Config files (defined in the module code or passed on the command line)

    • -

      Conditionally contributing a config file via DI. It is possible to make DI configuration inclusion conditional on the presence of a certain command line option:

      -
      -
      -
      OptionMetadata o = OptionMetadata.builder("qa")
      -        .description("when present, uses QA config")
      -        .build();
      -
      -BQCoreModule.extend(binder)
      -        .addOption(o)
      -        .mapConfigResource(o.getName(), "classpath:a/b/qa.yml");
      -
      -
    • +

      Java properties

    • -

      Specifying a config file on the command line. Each Bootique app supports --config option that takes a configuration file as parameter. To specify more than one file, use --config option multiple times. Configurations will be loaded and merged together in the order of their appearance on the command line.

    • +

      Environment variables

    • -

      Specifying a single config value via a custom option:

      -
      -
      -
      OptionMetadata o1 = OptionMetadata.builder("db")
      -        .description("specifies database URL")
      -        .valueOptionalWithDefault("jdbc:mysql://127.0.0.1:3306/mydb")
      -        .build();
      -
      -BQCoreModule.extend(binder)
      -        .addOption(o1)
      -        .mapConfigPath(o1.getName(), "jdbc.mydb.url");
      -
      -
      -
      -

      This adds a new --db option to the app that can be used to set JDBC URL of a datasource called "mydb". If value is not specified, the default one will be used.

      -
    • +

      Cloud secrets managers

      +
    • +

      Custom configuration loaders

    +
    +

    The load sequence of the sources is roughly this: (1) config files from modules → (2) config files from command line → (3) properties → (4) env variables. The sources with the higher load order override values from the sources with the lower order.

    +
    -

    2.3.3. Configuration via Properties

    +

    2.3.4. Configuration Files from Modules

    +
    +

    One way to pass a config file to a Bootique app in the code.

    +
    +
    +
    +
    BQCoreModule.extend(binder).addConfig("classpath:com/foo/default.yml");
    +
    +
    +
    +

    Such configuration should be known at compile time and is usually embedded in the app, and referenced via the special classpath:…​ URL. A primary motivation for this style is to provide application default configuration. More than one configuration can be contributed. Individual modules might load their own defaults independently of each other. The order of multiple configs loaded via this mechanism is undefined and should not be relied upon.

    +
    +
    +
    +

    2.3.5. Configuration Files from CLI

    +
    +

    Config files can be specified on the command line with the --config option. It takes a file (or a URL) as a parameter. You can use --config multiple times to specify more than one file. The files will be loaded in the order of their appearance on the command line.

    +
    +
    +
    +

    2.3.6. Configuration Properties

    YAML file can be thought of as a set of nested properties. E.g. the following config

    @@ -756,13 +850,10 @@

    2.3.3. Configuration via Properties

    -

    can be represented as two properties ("my.prop1", "my.prop2") being assigned some values. Bootique takes advantage of this structural equivalence and allows defining configuration via properties as an alternative (or more frequently - an addition) to YAML. If the same "key" is defined in both YAML file and a property, ConfigurationFactory would use the value of the property (in other words properties override YAML values).

    -
    -
    -

    To turn a given property into a configuration property, you need to prefix it with “bq.”. This "namespace" makes configuration explicit and helps to avoid random naming conflicts with properties otherwise present in the system.

    +

    can be represented as two properties (my.prop1, my.prop2) that are assigned some values. Bootique takes advantage of this structural equivalence and allows to defined individual configuration values via Java properties. All configuration properties are prefixed with bq.. This "namespace" helps to avoid random naming conflicts with properties otherwise present in the system.

    -

    Properties can be provided to Bootique via BQCoreModule extender:

    +

    Properties can be provided via BQCoreModule extender:

    @@ -777,34 +868,43 @@

    2.3.3. Configuration via Properties

    -

    Alternatively they can be loaded from system properties. E.g.:

    +

    or come from system properties:

    java -Dbq.my.prop1=valX -Dbq.my.prop2=valY -jar myapp.jar
    -
    -

    Though generally this approach is sneered upon, as the authors of Bootique are striving to make Java apps look minimally "weird" in deployment, and "-D" is one of those unintuitive "Java-only" things. Often a better alternative is to define the bulk of configuration in YAML, and pass values for a few environment-specific properties via shell variables (see the next section) or bind them to CLI flags.

    +
    + + + + + + + +
    According to the Bootique authors' opinion, properties is the worst mechanism for app configuration, as there are so many better options. However, there is a common valid use case for them - building configurations dynamically in the code.
    -

    2.3.4. Configuration via Environment Variables

    +

    2.3.7. Configuration Environment Variables

    -

    Bootique allows to use environment variables to specify/override configuration values. While variables work similar to JVM properties, using them has advantages in certain situations:

    +

    Bootique allows to use environment variables to specify/override configuration values. Variables work similar to JVM properties, but have a number of advantages:

    • -

      They may be used to configure credentials, as unlike YAML they won’t end up in version control, and unlike Java properties, they won’t be visible in the process list.

    • +

      Provide customized application environment without changing the launch script

      +
    • +

      A natural approach for CI/CD, container and cloud environments

    • -

      They provide customized application environment without changing the launch script and are ideal for containerized and other virtual environments.

    • +

      Good for credentials. Unlike YAML, vars usually don’t end up in version control, and unlike Java properties, they are not visible in the process list

    • -

      They are more user-friendly and appear in the app help.

    • +

      Appear explicitly in the app help

    -

    To declare variables associated with configuration values, use the following API (notice that no "bq." prefix is necessary here to identify the configuration value):

    +

    To declare variables associated with configuration values, use the following API (notice that no bq. prefix is necessary here to identify the configuration value):

    @@ -830,9 +930,6 @@

    2.3.4. Configuration via Envir

    Moreover, explicitly declared vars will automatically appear in the application help, assisting the admins in configuring your app

    -
    -

    (TODO: document BQConfig and BQConfigProperty config factory annotations required for the help generation to work)

    -
    $ java -jar myapp-1.0.jar --help
    @@ -847,7 +944,42 @@ 

    2.3.4. Configuration via Envir

    -

    2.3.5. Polymorphic Configuration Objects

    +

    2.3.8. CLI Configuration Aliases

    +
    +

    It is possible to make fixed configuration inclusion conditional on the presence of a certain command line option:

    +
    +
    +
    +
    OptionMetadata o = OptionMetadata.builder("qa")
    +        .description("when present, uses QA config")
    +        .build();
    +
    +BQCoreModule.extend(binder)
    +        .addOption(o)
    +        .mapConfigResource(o.getName(), "classpath:a/b/qa.yml");
    +
    +
    +
    +

    Also, it is possible to link a single configuration value to a named CLI option:

    +
    +
    +
    +
    OptionMetadata o1 = OptionMetadata.builder("db")
    +        .description("specifies database URL")
    +        .valueOptionalWithDefault("jdbc:mysql://127.0.0.1:3306/mydb")
    +        .build();
    +
    +BQCoreModule.extend(binder)
    +        .addOption(o1)
    +        .mapConfigPath(o1.getName(), "jdbc.mydb.url");
    +
    +
    +
    +

    This adds a new --db option to the app that can be used to set JDBC URL of a datasource called "mydb". If value is not specified, the default one will be used.

    +
    +
    +
    +

    2.3.9. Polymorphic Configuration Objects

    A powerful feature of Jackson is the ability to dynamically create subclasses of the configuration objects. Bootique takes full advantage of this. E.g. imagine a logging module that needs "appenders" to output its log messages (file appender, console appender, syslog appender, etc.). The framework might not be aware of all possible appenders its users might come up with in the future. Yet it still wants to have the ability to instantiate any of them, based solely on the data coming from YAML. Moreover, each appender will have its own set of incompatible configuration properties. In fact this is exactly the situation with bootique-logback module.

    @@ -941,74 +1073,7 @@

    2.3.5. Polymorphic Configuration Obj

    -

    2.4. Using Modules

    -
    -

    Modules often depend on other "upstream" modules. They can interact with upstream modules in a few ways:

    -
    -
    -
      -
    • -

      Use upstream services - inject objects defined in an upstream module in this module objects

    • -
    • -

      Contribute extensions - inject module’s objects to collections and maps from the upstream module, thus extending upstream module behavior.

    • -
    -
    -
    -

    Let’s use BQCoreModule as an example of an upstream module, as it is available in all apps.

    -
    -
    -

    2.4.1. Injecting Other Module’s Services

    -
    -

    You can inject any services declared in other modules. E.g. BQCoreModule defines an array of CLI arguments passed to the main() method, that can be injected in our module:

    -
    -
    -
    -
    public class MyService {
    -
    -    @Args (1)
    -    @Inject
    -    private String[] args;
    -
    -    public String getArgsString() {
    -        return Stream.of(args).collect(joining(" "));
    -    }
    -}
    -
    -
    -
    - - - - - - - -
    1Since there can potentially be more than one String[] in a DI container, Bootique @Args annotation is used to disambiguate the array that we are referring to.
    -
    -
    -
    -

    2.4.2. Contributing to Other Modules

    -
    -

    Bootique DI supports Collection bindings, intended to contribute objects defined in a downstream module to collections/maps used by services in upstream modules. Bootique hides DI API complexities, usually providing "extenders" in each module. E.g. the following code adds MyCommand the app set of commands:

    -
    -
    -
    -
    public class MyModule implements BQModule {
    -
    -    @Override
    -    public void configure(Binder binder) {
    -        BQCoreModule.extend(binder).addCommand(MyCommand.class);
    -    }
    -}
    -
    -
    -
    -

    Here we obtained an extender instance via a static method on BQCoreModule. Most standard modules define their own extenders accessible via "extend(Binder)". This is a pattern you might want to follow in your own modules.

    -
    -
    -
    -
    -

    2.5. Commands

    +

    2.4. Commands

    Bootique runtime contains a set of commands coming from Bootique core and from all the modules currently in effect in the app. On startup Bootique attempts to map command-line arguments to a single command type. If no match is found, a default command is executed (which is normally a "help" command). To list all available commands, the app can be run with --help option (in most cases running without any options will have the same effect). E.g.:

    @@ -1035,7 +1100,7 @@

    2.5. Commands

    -

    2.5.1. Writing Commands

    +

    2.4.1. Writing Commands

    Most common commands are already available in various standard modules, still often you’d need to write your own. To do that, first create a command class. It should implement io.bootique.command.Command interface, though usually it more practical to extend io.bootique.command.CommandWithMetadata and provide some metadata used in help and elsewhere:

    @@ -1068,7 +1133,7 @@

    2.5.1. Writing Commands

    -
    public class MyModule extends BaseModule {
    +      
    public class MyModule implements BQModule {
     
         @Override
         public void configure(Binder binder) {
    @@ -1101,7 +1166,7 @@ 

    2.5.1. Writing Commands

    -

    2.5.2. Injection in Commands

    +

    2.4.2. Injection in Commands

    Commands can inject services, just like most other classes in Bootique. There are some specifics though. Since commands are sometimes instantiated, but not executed (e.g. when --help is run that lists all commands), it is often desirable to avoid immediate instantiation of all dependencies of a given command. So a common pattern with commands is to inject javax.inject.Provider instead of direct dependency:

    @@ -1118,7 +1183,7 @@

    2.5.2. Injection in Commands

    -

    2.5.3. Decorating Commands

    +

    2.4.3. Decorating Commands

    Each command typically does a single well-defined thing, such as starting a web server, executing a job, etc. But very often in addition to that main thing you need to do other things. E.g. when a web server is started, you might also want to run a few more commands:

    @@ -1151,9 +1216,9 @@

    2.5.3. Decorating Commands

    -

    2.6. Options

    +

    2.5. Options

    -

    2.6.1. Simple Options

    +

    2.5.1. Simple Options

    In addition to commands, the app can define "options". Options are not associated with any runnable java code, and simply pass command-line values to commands and services. E.g. the standard “--config” option is used to locate configuration file(s). Unrecognized options cause application startup errors. To be recognized, options need to be "contributed" to Bootique similar to commands:

    @@ -1183,7 +1248,7 @@

    2.6.1. Simple Options

    -

    2.6.2. Configuration Options

    +

    2.5.2. Configuration Options

    While you can process your own options as described above, options often are just aliases to enable certain pieces of configuration. Bootique supports three flavors of associating options with configuration. Let’s demonstrate them here.

    @@ -1227,15 +1292,15 @@

    2.6.2. Configuration Options

    -

    2.7. Logging

    +

    2.6. Logging

    -

    2.7.1. Loggers in the Code

    +

    2.6.1. Loggers in the Code

    Standard Bootique modules use SLF4J internally, as it is the most convenient least common denominator framework, and can be easily bridged to other logging implementations. Your apps or modules are not required to use SLF4J, though if they do, it will likely reduce the amount of bridging needed to route all logs to a single destination.

    -

    2.7.2. Configurable Logging with Logback

    +

    2.6.2. Configurable Logging with Logback

    For better control over logging a standard module called bootique-logback is available, that integrates Logback framework in the app. It seamlessly bridges SLF4J (so you keep using SLF4J in the code), and allows to configure logging via YAML config file, including appenders (file, console, etc.) and per class/package log levels. Just like any other module, bootique-logback can be enabled by simply adding it to the pom.xml dependencies, assuming autoLoadModules() is in effect:

    @@ -1269,7 +1334,7 @@

    2.7.2. Configurable Logging with Log

    -

    2.7.3. BootLogger

    +

    2.6.3. BootLogger

    To perform logging during startup, before DI environment is available and YAML configuration is processed, Bootique uses a special service called BootLogger, that is not dependent on SLF4J and is not automatically bridged to Logback. It provides an abstraction for writing to stdout / stderr, as well as conditional "trace" logs sent to stderr. To enable Bootique trace logs, start the app with -Dbq.trace as described in the deployment section.

    diff --git a/docs/3.x/bootique-docs/index.toc.html b/docs/3.x/bootique-docs/index.toc.html index 43095598..882ad4f7 100644 --- a/docs/3.x/bootique-docs/index.toc.html +++ b/docs/3.x/bootique-docs/index.toc.html @@ -12,12 +12,11 @@
  • 2. Programming
  • 3. Testing