This is the main class for the Fenix Framework. It is the central point for obtaining most of the APIs that the user of the
framework (a programmer) should need.
Before being able to use the framework, a program must initialize it, by providing a configuration. There are two (disjoint)
alternative methods to initialize the FenixFramework:
- By convention: When the FenixFramework class is loaded, its static initialization code will check for the
presence of a properties file that provides the configuration information or the existence of system properties (given through
the
-D
switch).
- Explicitly: The programmer either creates a
Config or
MultiConfig instance and then
explicitly calls the corresponding initialization method:
FenixFramework#initialize(Config) or
FenixFramework#initialize(MultiConfig).
Using the configuration by convention, the framework will first load the properties from the file
fenix-framework.properties
if it exists. Then it will load the properties from the file
fenix-framework-<NNN>.properties
, if it exists (where <NNN>
is the name of the BackEnd
that generated the domain-specific code). Files are looked up in the application's classpath using
Thread.currentThread().getContextClassLoader().getResource()
. Finally, it will load any system properties that
start with the prefix fenixframework.
(and discard that prefix). Whenever the same property is defined more than
once, the last setting prevails. After the previous steps have been taken, if there is any property set, then the framework
will attempt to create a
Config instance and automatically invoke
FenixFramework#initialize(Config).
The syntax for the configuration file is to have each line in the form:
property=value
where each
property
must be the name of an existing configuration field (when providind the properties via a
system property use
-Dfenixframework.property=value
). Additionally, there is one optional special property named
config.class
. By default, the config parser creates an instance of the
BackEndId#getDefaultConfigClass()provided by the current BackEndId, but this property can be used to choose a different (albeit compatible) configuration class.
The config instance is then populated with each property (except with the
config.class
property), using the
following algorithm:
- Confirm that the config has a field with the same name as the
property
. If not, ignore the property with a
warning.
- If the config class provides a method (can be private) in the format
<property>FromString(String)
, then
such method will be invoked to set the property. The
String argument will be the value
.
- Else, attempt to directly set the property on the field assigning it the
value
String.
- If the previous attempts to set the value fail, throw a
ConfigError.
The rationale supporting the previous mechanism is to allow the config class to process the String provided in the
value>
using the *FromString
method.
After population of the config finishes with success, the
FenixFramework#initialize(Config) method is invoked with the
created Config instance. From this point on, the initialization process continues just as if the programmer had explicitly
invoked that initialization method.
The explicit configuration maintains the original configuration style (since Fenix Framework 1.0) with compile-time checking of
the config's attributes. To use it, read the documentation in the
Config class. It also adds the possibility for the
programmer to provide multiple configurations and then let the initialization process decide which to use based on the current
pt.ist.fenixframework.backend.BackEnd (see
MultiConfig for more details).
After initialization completes with success, the framework is ready to manage operations on the domain objects. Also, it is
possible to get an instance of the
DomainModel class representing the structure of the application's domain.