|
|
|
Configuration
Properties and behavior of a Project Zero application may be specified in configuration files.
Zero configuration file: zero.config
The config/zero.config file is processed at the start of a Zero application.
The content of a config/zero.config file is organized into "stanzas" of related key/value pairs. Stanzas are associated with directives, such as "store to the Global Context" and "include another configuration file." For example:
Example zero.config file
# Set a key/value pair into the GlobalContext; value is loaded as an Integer
[/config/http]
port=8080
# Set multiple key/value pairs; bracketed values are loaded as a List
[/config/fileserver]
directoryBrowsing=false
searchOrder=[.groovy, .gt, .html]
# Create/append to a List of Map<String, Object> in the GlobalContext
[/config/handlers]
events=start
handler=zero.core.cfadapter.ChannelFrameworkAdapter.class
conditions=[true]
# Create the singleton List<Object> (List of Objects) in the GlobalContext
[/config/bindings/.gt[]]
zero.core.groovysupport.bindings.DefaultBindings.class
# Append to the /config/bindings/.gt singleton List<Object> created above
[/config/bindings/.gt[]]
acme.UsefulBindings.class
# Set a configuration property
[@properties]
authType=Form
# Include configuration templates
[@include ${zero.core}/config/security/rule.config]
uri=/employees[/{rest:any}]
methods=GET
groups=[ALL_AUTHENTICATED_USERS]
Directives
Zero configuration files are processed to set data into the Global Context. Specifics of configuration processing are based upon the following set of directives:
Key=value set
Example [/config/http]
The directive is treated as a prefix for the keys within the stanza. Overwrites are not allowed.
For example, after parsing the following configuration stanzas in order:
[/config/foo]
p1=v1
p2=v2
[/config/foo]
p2=x2
p3=x3
the results may be summarized as:
GlobalContext.get("/config/foo/p1").equals("v1")
GlobalContext.get("/config/foo/p2").equals("v2")
GlobalContext.get("/config/foo/p3").equals("x3")
List map append
Example [/app/foo[]]
If the lines within the stanza include an equals sign, then they are treated as key/value pairs for a Map. The Map is added to a List that is stored at the specified URI in the Global Context.
For example, after parsing the following configuration stanzas in order:
[/app/foo[]]
p1=v1
p2=v2
[/app/foo[]]
p2=x2
p3=x3
the results may be summarized as:
GlobalContext.get("/app/foo#0/p1").equals("v1")
GlobalContext.get("/app/foo#0/p2").equals("v2")
GlobalContext.get("/app/foo#1/p2").equals("x2")
GlobalContext.get("/app/foo#1/p3").equals("x3")
List singleton append
Example [/config/bindings/.gt[]]
If the line does not include an equal sign, the the value is treated as an Object. The Object is added to a List that is stored at the specified URI in the Global Context.
For example, after parsing the following configuration stanzas in order:
[/app/foo[]]
"some quoted value"
[/app/foo[]]
another value
the results may be summarized as:
GlobalContext.get("/app/foo#0").equals("some quoted value")
GlobalContext.get("/app/foo#1").equals("another value")
Configuration properties
Example [@properties]
Key/value pairs within an [@properties] stanza are defined as "configuration properties." These properties may be used for many purposes, including passing values into other configuration files.
Once set, the value of a configuration property may be used elsewhere with ${propertyName} notation. Note that configuration processing is a single-pass operation, so properties must be set before they are referenced. (If not, then the application will fail to start.)
Configuration properties are immutable within certain scoping rules:
- Resolved properties are "globals" in that they are defined within all configuration files. These properties are set prior to processing the first configuration file.
- Properties set within a "top-level" configuration file are global from that point on.
- Properties associated with an
@include directive are scoped to the included file and any files that it includes.
- Properties set within an included file are scoped to that file and any files that it includes.
Resolved properties
Root directories of resolved dependencies are available as configuration properties. The property names are the module names of the dependencies, as specified in the corresponding ivy.xml files (e.g. zero.core).
Advanced Resolved properties are automatically set into the Global Context, too. You can obtain the root directory for each dependency as follows:
for (String dependency : GlobalContext.get(Config.Dependencies.list)) {
String dependencyRoot = GlobalContext.condGet("/config/dependencies#" + dependency, null);
// Operate on the dependencyRoot (e.g. load files)
}
Include
Example [@include <includeFile>]
Zero processes includeFile as a configuration file. includeFile may be an absolute or relative file reference. Relative references are relative to the including file. You may use properties to specify the file reference, such as:
[@include ${zero.core}/config/security/rule.config]
p1=v1
p2=v2
Note that the body of an include stanza may include key/value pairs. These pairs are set as properties for the included file, subject to the scoping rules for configuration properties.
Configuration properties set within includeFile are scoped to that file and any files included by it. Properties are immutable within their scope, so "first one wins."
Comments
Lines that begin with a hash (#) are comments.
Type conversions
Zero loads all configuration values as Strings, then attempts to convert them into other types. Specifically, conversion is tested in the following order:
- A quoted value remains a String, without the quotes (e.g. "42" is treated as a String)
- Literal "true" and "false" are converted to Boolean
- Integer
- Double
If the value cannot be converted by one of these rules, then it remains a String.
Event-handler registration
Event handlers may be registered with configuration stanzas of the following form:
[/config/handlers]
events=<eventName>
handler=<handlerName>
conditions=<listOfConditions>
where
- eventName is the name of the event handled by this handler (e.g.
start); multiple events may be specified with a pipe-delimited string (e.g. GET|POST)
- handlerName is the name of the handler (e.g. zero.core.cfadapter.ChannelFrameworkAdapter.class). The suffix must match a registered handler type. The default list of registered handler types includes:
-
.class for Java
-
.groovy for Groovy scripts (searched in app/scripts; use slashes for path delimiters, e.g. foo/bar/handler.groovy)
-
.gt for Groovy templates (searched in app/views; use slashes for path delimiters, e.g. foo/bar/handler.gt)
- listOfConditions is based upon a set of condition operators. The value of the
conditions, computed at run time, is the logical AND of the list elements.
Condition operators
-
true - Always
true. Example: conditions=[true]
-
exists -
true only if the GlobalContext contains the specified key. Example: conditions=[exists /request/status]
-
urimatches - Selector pattern match. Named groups are specified as
{name}; matches may be qualified, such as ${name:any}. Values of named groups are set into the event zone for the handler. Example: [/event/_uri urimatches /config/handlers/{eventName}]; /event/eventName contains value.
Qualifiers:
-
-
any - Matches to the end.
Example: If /request/path == /a/b/c, then [/request/uri urimatches /a/{remainder:any}] evaluates to true and the handler will be invoked with /event/matchedURI == /a and /event/remainder == b/c.
The pipe character | will also match to the end of the URI and, additionally, set /event/pathInfo. Example: If /request/path == /a/b/c, then [/request/uri urimatches /a|] evaluates to true and the handler will be invoked with /event/matchedURI == /a and /event/pathInfo == /b/c.
Note: The | can only appear at the end of the string. Therefore, [/request/uri urimatches /a|/b] is invalid.
-
-
element - Matches to the next slash.
-
-
digit - Matches one or more digits: [0-9].
-
-
word - Matches one or more word characters: [a-zA-Z_0-9].
-
matches - Regex pattern match.
Example: [/event/_uri matches @include.*] Example: /event/resolvingEvent matches (GET|POST|DELETE|PUT)
-
not - Negates the condition value; placed before the operator it modifies.
Example: [not exists /request/status]
Instance properties
Additional key/value pairs within a handler registration are provided to the handler in the event zone.
For example, from the following configuration:
[/config/handlers]
events=<eventName>
handler=<handlerName>
conditions=<listOfConditions>
p1=v1
p2=v2
both p1 and p2 are instance properties. When this handler is invoked, these property values are available as /event/p1 and /event/p2.
Configuration templates
A combination of [@include <includeFile>] and configuration properties can be used to build configuration templates.
For example, security support in zero.core uses configuration templates.
Configuration template example: config/security/rule.config
# Set default properties; may be overridden by the including file
[@properties]
authType=None
users=[]
groups=[]
methods=GET|PUT|POST|DELETE
requireSSL=false
# Handler registration, based upon properties
[/config/handlers]
events=secure
handler=zero.core.security.${authType}SecurityHandler.class
conditions=[/request/path urimatches ${uri}, /request/method matches (${methods})]
groups=${groups}
users=${users}
requireSSL=${requireSSL}
[/config/handlers]
events=retrieveCredentials
handler=zero.core.security.${authType}UserInfoResolver.class
conditions=[/request/path urimatches ${uri}, /request/method matches (${methods})]
Using a configuration template
[@include ${zero.core}/config/security/rule.config]
uri=/employees[/{rest:any}]
authType=Form
methods=GET
groups=[ALL_AUTHENTICATED_USERS]
Order of processing
Zero enforces a "first one wins" strategy with configuration processing to ensure consistency throughout the application. As a result, you might need to understand the order in which configuration files are processed in cases involving overwrites.
By default, Zero first processes <apphome>/config/zero.config, then processes config/zero.config for each dependency. The order in which dependencies are processed is described in the Virtualized directories documentation. Although the config directory is not a virtualized directory, the order of processing configuration files is identical to the search order through dependencies within a virtualized directory.
Pre- and post-configuration files
Deployers may want to override application configuration settings without modifying any of the application files. This can be accomplished with pre- and post-configuration files. (Pre- and post- refer to the main configuration processing step: pre- happens before the main configuration processing; post- occurs after.)
In Eclipse, you specify an alternate configuration file by modifying the Run profile for that Zero application to set the Program Arguments to: [-pre <preConfigFile>] [-post <postConfigFile>] <applicationRootDirectory>
From the command line:
zero run [-args "-pre <preConfigFile>"]
<configFile> may be an absolute or relative file reference. Relative file references are relative to the current working directory.
|