templates.docs.configuration.html Maven / Gradle / Ivy
Show all versions of spincast-website Show documentation
{#==========================================
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 :
-
If you don't override a Spincast configuration defined in SpincastConfig,
the default value hardcoded in SpincastConfigDefault
will be used.
-
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.
-
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.
-
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.
-
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.
-
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 :
-
app1.admin.email.address = [email protected]
-
app2.admin.email.address = [email protected]
-
common.admin.email.format = html
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.
-
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
".
-
List<String> getEnvironmentVariablesPrefixes()
The allowed prefixes an environment variable can have
to be used as a configuration.
Defaults to "app."
only.
-
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.
-
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!