All Downloads are FREE. Search and download functionalities are using the official Maven repository.

templates.docs.configuration.html Maven / Gradle / Ivy

There is a newer version: 2.2.0
Show newest version
{#==========================================
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 :

  • The configurations are typed, preventing many errors :

    // Doesn't compile! The "getServerHost()" getter returns a String.
    int port = configs.getServerHost();

    Compare this nice compile time error to a simple .properties based configuration that will fail at runtime :

    // Compiles... But boom at runtime!
    int port = (int)properties.get("server.host");

  • The creation of a configuration value can involve complex logic (caching the generated value is easy to implement too).

    // Some configuration getter...
    public int getHttpServerPort() {
    
    	// We use another configuration to create the value
    	// of this one! You can use any logic you need...
    	if("local".equals(getEnvironmentName())) {
    		return 12345;
    	} else {
    		return 80;
    	}
    }

  • A configuration can be of any type, not only String, Booleans and Numbers.

    // A configuration getter that returns a File object
    public File getSpincastWritableDir() {
    	// ...
    }

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 :

  • String getClasspathFilePath()
    The path to a configuration file to load from the classpath.

    Defaults to "app-config.yaml". This means you can simply create that file in your project's /src/main/resources/ folder and it will be used.

    @return the classpath path or null to disable this configuration source.
  • String getExternalFilePath()
    The path to a configuration file to load from the file system.

    The path can be relative or absolute. Spincast will check this using :

    File configFile = new File(thePath);
    if(configFile.isAbsolute()) {
        // ...
    }

    If the path is relative, it is from the executable .jar or, if not run from a .jar, from the root of the project.

    Defaults to "app-config.yaml".

    @return the path or null to disable this configuration source.
  • List<String> getEnvironmentVariablesPrefixes()
    The allowed prefixes an environment variable can have to be used as a configuration.

    Defaults to "app." only.

    @return the allowed prefixes or null to disable environment variables as a source for configurations.
  • boolean isEnvironmentVariablesStripPrefix()
    Should the prefix of an environment variable be stripped? For example, if environmentVariablesPrefixes() indicates that "app." is an environment variable prefix, then "app.admin.email" will result in a "admin.email" key.

    Note that each environment variable key must be unique once the prefixes are stripped, otherwise an exception will be thrown when the application starts!

    Defaults to false.

  • List<String> getSystemPropertiesPrefixes()
    The allowed prefixes a system property can have to be used as a configuration.

    Defaults to "app." only.

    @return the allowed prefixes or null to disable system properties as a source for configurations.
  • boolean isSystemPropertiesStripPrefix()
    Should the prefix of an system property be stripped? For example, if systemPropertiesPrefixes() indicates that "app." is an system property prefix, then "app.admin.email" will result in a "admin.email" key.

    Note that each system properties key must be unique once the prefixes are stripped, otherwise an exception will be thrown when the application starts!

    Defaults to false.

  • boolean isExternalFileConfigsOverrideEnvironmentVariables()
    If an external configuration file is used and environment variables too, should configurations from the file override those from environment variables?

    The default is false : environment variables have priority.

  • boolean isThrowExceptionIfSpecifiedClasspathConfigFileIsNotFound()
    Should an exception be thrown if a classpath config file is specified (is not null) but is not found.

    If set to false, a message will be logged but no exception will be thrown.

    Defaults to false.

  • boolean isThrowExceptionIfSpecifiedExternalConfigFileIsNotFound()
    Should an exception be thrown if an external config file is specified (is not null) but is not found.

    If set to false, a message will be logged but no exception will be thrown.

    Defaults to false.

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 :

  • getPublicUrlBase() : This configuration is very important and you should override it in your application and adjust it from environment to environment! It tells Spincast what is the base public URL used to reach your application. For example, your application may be accessed using a URL such as http://www.example.com but can in fact be behind a reverse-router and actually started on the "localhost" host and on port "12345". The problem is that the public base URL ("http://www.example.com") can't be automatically found, but Spincast still requires it to :
    • Generate an absolute URL for a link to provide to the user.
    • Set a cookie using the appropriated domain.

    By default, the getPublicUrlBase() configuration will be "http://localhost:44419". This default can be used for development purposes, but very should be changed when releasing to another environment.

    It is so important to override this configuration that Spincast has a validation in place : when an application starts, an exception will be thrown if those conditions are all meet :

    • The environment name is not "local".
    • isDevelopmentMode() configuration returns false.
    • The public host is still "localhost".
    In other words, Spincast tries to catch the case where an application is running anywhere else than locally, without the default public base URL ajusted.

    Note that you can disable this startup validation using the isValidateLocalhostHost() configuration.

  • getServerHost() : The host/IP the HTTP Server will listen on. The default is 0.0.0.0, which means the Server will listen on any IP.
  • getHttpServerPort() : The port the Server will listen to for HTTP (unsecure) requests. If <= 0, the Server won't listen on HTTP requests.
  • getHttpsServerPort() : The port the Server will listen to for HTTPS (secure) requests. If <= 0, the Server won't listen on HTTPS requests. If you use HTTPS, you also have to provide some extra configurations related to the SSL certificate to use.
  • isDevelopmentMode() : If true, a development environment is taken for granted, and internal error messages may be displayed publicly, no cache will be used for the templates, etc. The default is true, so make sure you change this to false before deploying to production!





© 2015 - 2024 Weber Informatics LLC | Privacy Policy