{#========================================== Docs : "configuration" ==========================================#}

Configuration

Spincast doesn't force you to configure your application in a specific way, but does suggest a strategy. The only requirement is that in order to modify the configurations used by the internals of Spincast itself (for example the port the server is going to be started with), you need to bind a custom implementation for the SpincastConfig interface. Spincast retrieves the values to use for its configurations through this interface.

If you don't bind a custom implementation for that SpincastConfig interface, a default one will be used and will provide default values.

Configuration strategy - introduction

The strategy we suggest to configure your application allows you to both modify the default configurations and add specific configurations to your application in a single location. This strategy involves creating a standard Java class with getter methods for each configuration that is needed.

Compared to a simple .properties based configuration strategy, a class based one requires more work (since you do have to define a getter method for each configuration), but comes with three big advantages :

Configuration strategy - components

The first step is to create a custom AppConfig interface that extends SpincastConfig :


public interface AppConfig extends SpincastConfig {

    /**
     * An app specific configuration
     */
    public String getSomeAppConfiguration();

    /**
     * Another app specific configuration
     */
    public int getAnotherAppConfiguration();
}

And then create an implementation that implements your custom interface and extends the Spincast provided SpincastConfigDefault implementation :


public class AppConfigDefault extends SpincastConfigDefault implements AppConfig {

    /**
     * An app specific configuration
     */
    public String getSomeAppConfiguration() { ... }

    /**
     * Another app specific configuration
     */
    public int getAnotherAppConfiguration() { ... }
    
    /**
     * Overrides a default Spincast configuration too!
     */
    @Override
    public int getHttpServerPort() {
        return 12345;
    }
}

Finally, you add the associated bindings to your Guice module :

public class AppModule extends SpincastGuiceModuleBase {

    @Override
    protected void configure() {

        bind(AppConfig.class).to(AppConfigDefault.class).in(Scopes.SINGLETON);
        
        //...
    }
}

Note that Spincast will detect that a custom implementation of the SpincastConfig interface has been bound, and will automatically adjust the binding for this interface. You can bind SpincastConfig to AppConfigDefault by yourself if you want, but it is not required.

Have a look at the configuration of this very website for an example of how this strategy looks like!

Be careful with the dependencies you inject in your implementation class : the configurations are used by a lot of other components and it is therefore easy to create circular dependencies. One dependency that you can inject without any problem, and that is often useful in a configuration class, is the application arguments.

Configuration strategy - implementation

By using the strategy above, so by extending the SpincastConfigDefault base class, you also extend the ConfigFinder base class and get access to a lot of useful features to help you build your configuration. In particular, you gain access to an easy way to externalize the values of your configurations (ie : have different configurations depending on the environment the application runs on).

We'll see in the next section, Configuring the config plugin, that the way Spincast searches for external configurations is fully configurable.

Making configurations externalizable

To make configurations externalizable, the first thing to do in your implementation class is to remove any hardcoded values and, instead, use the provided getters. Those special getters are provided by the ConfigFinder class, from which SpincastConfigDefault extends. There are multiple getters, depending on the type of the configuration.

For example, in your implementation class, instead of this hardcoded value :

public class AppConfigDefault extends SpincastConfigDefault implements AppConfig {

    @Inject
    public AppConfigDefault(SpincastConfigPluginConfig spincastConfigPluginConfig) {
        super(spincastConfigPluginConfig);
    }

	/**
	 * The HTTP server port to use
	 */
    @Override
    public int getHttpServerPort() {
    	
    	// Hardcoded value!
        return 12345;
    }
    
    // ...
}

You would instead use the provided getInteger(...) method, so the "port" configuration is externalized :

public class AppConfigDefault extends SpincastConfigDefault implements AppConfig {

    @Inject
    public AppConfigDefault(SpincastConfigPluginConfig spincastConfigPluginConfig) {
        super(spincastConfigPluginConfig);
    }

	/**
	 * The HTTP server port to use
	 */
    @Override
    public int getHttpServerPort() {
    
    	// Makes this configuration externalizable
    	// and provides a default value in case no
    	// external value is found.
        return getInteger("server.port", 12345);
    }
    
    // ...
}

By using the special getters provided by the ConfigFinder base class, your configuration is now externalized. A getter is provided for all common types : String, Boolean, Integer, Long, BigDecimal and Date.

Note that date configuration values must be using a valid ISO-8601 format.

The sources of configuration values

Spincast will load externalized configurations from various sources, each source overriding the previous one, if a same configuration is found in both :

  1. If you don't override a Spincast configuration defined in SpincastConfig, the default value hardcoded in SpincastConfigDefault will be used.
  2. If you override a Spincast configuration, but you hardcode it in your implementation class, the configuration is not externalizable and the hardcoded value will be used.
  3. An app-config.yaml file is looked for on the classpath. This is where you generally will place the default values of your externalizable configurations.
  4. If your application is running from an executable .jar, Spincast will check if a app-config.yaml file exists next to it. If your application is not running from an executable .jar (for example it is launched in an IDE), Spincast will check if a app-config.yaml file exists at the root of the project.
  5. Environnement variables will be checked to see if some configurations are defined there. An environment variable must start with "app." to be considered as a configuration for a Spincast application. This prefix is configurable.
  6. System properties will be checked to see if some configurations have been passed to the application when launched. An system property must start with "app." to be considered as a configuration for a Spincast application. This prefix is configurable. System properties have the highest priority and overrides any existing configurations (except of course for hardcoded/non-externalized configurations).

Both environment variables and system properties can have multiple prefixes. In association with the feature that can strip those prefixes when getting the configurations (see next section), this allows you to define variables for more than one application on the same server. For example, you could have those environment variables :

By configuring the environment variable prefixes of a first application as being "app1" and "common", and the prefixes of a second application as being "app2" and "common", you can have both application specific variables and common variables.

Configuration file example

Here's an example of a app-config.yaml file that could be used as a source of externalized configurations :

app:
	name: My Super app
	  
	api:
	  base: https://www.myApp.com
	  
	databases:
	  bd1:
	    host: dbHost.com
	    port: 12345
	    username: myDbUser
	    password: # Empty! Must be provided at runtime...

Then in your AppConfigDefault class, you could access the port for the "db1" database using :

@Override
public int getDb1Port() {
    return getInteger("app.databases.bd1.port");
}

In this example, the password for the "db1" database will have to be defined as an environment variable, or using any other mechanism that doesn't require the password to be defined as plain text and be committed to your version control system (which would be a really bad idea)! Since the configuration values are retrieved using standard Java methods, you can implement any mechanism you want in order to retrieve such "secret" configurations.

Configuring the config plugin

The steps described in the sources of configuration values section are configurable. You configure the way the Spincast Config plugin works by binding a custom implementation of the SpincastConfigPluginConfig interface. If you don't bind a custom implementation for this interface, a default implementation, SpincastConfigPluginConfigDefault, will be used.

Those are the methods you can tweak :

Core Configurations

To know all the core configurations required by Spincast, have a look at the SpincastConfig javadoc. Here, we're simply going to introduce the most important ones, and their default value :