AgE 2.6 : XML Configuration

On this page:

The syntax of XML configuration files in AgE is very similar to what you can find in Spring. 

A few notable differences are:

  • The basic unit of abstraction are components, not beans
  • AgE configurations are hierarchical, unlike flat Spring ones. Component definitions can be nested, resulting in a component tree, where each level is a separate context. Components at a lower level can hide higher ones by type or name.
  • AgE configurations can be composed in much more sophisticated ways than Spring ones. It is of course possible to include one file into another. However, it is also possible to define structural blocks, which can be overriden or extended during inclusion - this effectively allows mechanisms like inheritance and mixins! (The idea is similar to template inheritance in frameworks like Django).

 These topic are addressed in more detail further down this page.

Configuration header

Each configuration file should begin with the following header:

<?xml version="1.0" encoding="UTF-8"?>
<configuration xmlns="http://age.iisg.agh.edu.pl/schema/age"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://age.iisg.agh.edu.pl/schema/age http://age.iisg.agh.edu.pl/schema/age/age.xsd">
 ...
</configuration>

 Note that configuration schemas are versioned just like the platform itself. Any given version of AgE will only work with the same configuration file version. The age.xsd schema represent the most recent one, but you can find older ones at http://age.iisg.agh.edu.pl/schema/age.

Component definitions

Defining a component is very easy:

<component name="myComponent" class="foo.bar.ComponentClass" isSingleton="true" />

Component definitions are identified by their name, which needs to be unique in a given context. If no one is provided in the configuration, it will be randomly generated. 

Each component specifies the class which represents it. It can have a singleton or prototype scope - singleton components will be instantiated only once, while prototype definitions will instantiate a new object at each request to the IoC container. The default scope of components is singleton.

<agent> and <strategy> tags are shortcuts for, respectively, prototype and singleton scoped components.

 

You can define arguments to be passed to the constructor:

<component name="myComponent" class="foo.bar.ComponentClass" >
 <constructor-arg>
  <value class="String" value="someValue"/> 
 </constructor-arg> 
 <constructor-arg>
  <value class="Integer" value="123"/> 
 </constructor-arg>  
</component> 

Constructor arguments will be used in the order in which they are provided.

 

You can also set javabeans properties:

<component name="myComponent" class="foo.bar.ComponentClass" >
 <property name="someProperty">
  <value class="String" value="someValue"/> 
 </constructor-arg>  
</component> 

 

Dependencies between components can be resolved explicitly:

<component name="client" class="foo.bar.ClientClass" >
 <property name="service">
  <reference target="serviceImpl"/> 
 </constructor-arg>  
</component> 
<component name="serviceImpl" class="foo.bar.ServiceClass" > 

Otherwise, the IoC container will try to autowire dependencies.

 

Collection and map definitions

Collections and maps can be defined with the <array>, <list>, <set> and <map> tags:

<list>
 <value class="Integer" value="1"/>
 <value class="Integer" value="2"/>
 <value class="Integer" value="3"/> 
</list>

<map>
 <item>
  <itemKey>
   <value class="String" value="key" />
  </itemKey>
  <itemValue>
   <value class="String" value="value" />
  </itemValue>
 </item>
</map>   


These definitions can also be named and scope. However, they have a prototype scope by default. 

You can define collection elements with <reference> or <value> tags, as well as with directly nested definitions:

<list>
 <component name="component" class="foo.bar.ComponentClass" />
 <reference target="component" />
 <reference target="component" />
</list>

As a shortcut to repeating the same elements in a collection, you can use a <multiple> tag:

<list>
 <multiple count="3"> 
  <component name="component" class="foo.bar.ComponentClass" />
 </multiple>
</list>

Be careful when defining a collection or map containing prototype-scoped elements. As the collection or map is by default prototype-scoped itself, each reference to it will create a new instance, and along it, new instances of its elements.

This might be the desired behavior, if not, simply set the collection or map scope to singleton.

 

Hierarchical configurations

AgE configuration files are hierarchical - definitions can be nested within one another, which result in a tree-like memory model of configuration, unlike a flat one like in Spring. 

 

Definitions visibility is limited to siblings and the enclosing parent definition. As an example, see:

<component name="parent" class="foo.bar.ComponentClass">
 <component name="child" class="foo.bar.ComponentClass">
 <component name="sibling" class="foo.bar.ComponentClass"> 
</component> 
<component name="uncle" class="foo.bar.ComponentClass"/> 

In the above configuration, the 'child1' component will only be visible to 'parent' and 'sibling' components, but not to the 'uncle' one.

 

Definitions lower down the tree can also hide those higher up by type or name. This applies to siblings and the enclosing parent definition. As an example, see:

<component name="parentClient" class="foo.bar.ClientClass">
 <component name="localService" class="foo.bar.ServiceClass">
 <component name="childClient" class="foo.bar.ClientClass"> 
</component> 
<component name="uncleClient" class="foo.bar.ClientClass" /> 
<component name="globalService" class="foo.bar.ServiceClass"/> 

In the above configuration, 'uncleClient' dependency to 'ServiceClass' will be resolved to the 'globalService' component. However, both 'parentClient' and 'childClient' will resolve to the 'localService' component.

 

This hierarchy feature allows for a greater flexibility in configuration files, e.g. you can:

  • define global components and hide them with local ones if needed
  • provide different components with different service implementations
  • encapsulate parts of the configurations, resulting in less name or type collisions and more
  • decompose the configuration, resulting in an easier maintenance

 

For usability, nested components can be defined directly anywhere a single <reference> argument is valid, as in <property> or as collection elements. Note however, that they will still be visible at the given tree level  - you can thus still refer to them, so also watch out for possible collisions.

Configuration inheritance and mixins

 

Comparing to other frameworks, AgE introduces one outstanding feature: the possibility of mixins in configuration files.

Most frameworks allow users to include configuration files one in another. AgE takes this one step further: it is possible to define named structural blocks in configuration files. When including some other file, the blocks it contains can then be extended or overridden.

This feature introduces inheritance in configuration files, or more generally - mixins, as overridable inclusions might happen multiple times in multiple places.

One current limitation is that structural blocks must be name-unique in the resulting document. This makes it impossible to include multiple times any file which defines blocks.

 

As an example for basic includes, let say we have one configuration, like:

included.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration xmlns="http://age.iisg.agh.edu.pl/schema/age"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://age.iisg.agh.edu.pl/schema/age http://age.iisg.agh.edu.pl/schema/age/age.xsd">
 
 <component name="component" class="foo.bar.ComponentClass" />
</configuration>

You can include this file in some other, like:

including.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration xmlns="http://age.iisg.agh.edu.pl/schema/age"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://age.iisg.agh.edu.pl/schema/age http://age.iisg.agh.edu.pl/schema/age/age.xsd">
 
 <include target="classpath:included.xml" /> 
 <component name="component2" class="foo.bar.ComponentClass" />  
</configuration>

The resulting document seen by AgE will then look like:

resolved including.xml
<configuration>
 <component name="component" class="foo.bar.ComponentClass" />
 <component name="component2" class="foo.bar.ComponentClass" />  
</configuration>

You can target configuration files using multiple standard protocols: file://path, http://url, etc. It is also possible to refer to resources on the classpath using the pseudo-protocol classpath:path.

 

 

So far, nothing unusual. However, the fun begins when you define structural blocks in a configuration file.

As an example, let's take a base configuration, like:

base.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration xmlns="http://age.iisg.agh.edu.pl/schema/age"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://age.iisg.agh.edu.pl/schema/age http://age.iisg.agh.edu.pl/schema/age/age.xsd">
 
 <component name="component" class="foo.bar.ComponentClass" />
 <block name="someBlock">
  <component name="parentComponent" class="foo.bar.ComponentClass" />
 <\block>  
  
</configuration>

When this file is being included, it is possible to choose to override or extend the blocks in contains.

extending_child.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration xmlns="http://age.iisg.agh.edu.pl/schema/age"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://age.iisg.agh.edu.pl/schema/age http://age.iisg.agh.edu.pl/schema/age/age.xsd">
 
 <include target="classpath:base.xml" > 
  <block name="someBlock" override="false">
   <component name="childComponent" class="foo.bar.ComponentClass" /> 
  </block> 
 </include>  
</configuration>
overriding_child.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration xmlns="http://age.iisg.agh.edu.pl/schema/age"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://age.iisg.agh.edu.pl/schema/age http://age.iisg.agh.edu.pl/schema/age/age.xsd">
 
 <include target="classpath:base.xml" > 
  <block name="someBlock" override="true">
   <component name="childComponent" class="foo.bar.ComponentClass" /> 
  </block> 
 </include>  
</configuration>

Will be resolved by AgE, respectively, as:

resolved extending_child.xml
<configuration>
 <component name="component" class="foo.bar.ComponentClass" />
 <block name="someBlock">
  <component name="parentComponent" class="foo.bar.ComponentClass" /> 
  <component name="childComponent" class="foo.bar.ComponentClass" /> 
 </block> 
</configuration>
resolved overriding_child.xml
<configuration>
 <component name="component" class="foo.bar.ComponentClass" />
 <block name="someBlock"> 
  <component name="childComponent" class="foo.bar.ComponentClass" /> 
 </block> 
</configuration>

A document inherits all blocks from it parent, so they might be further extended or overridden in its own children.  It is also possible to nest blocks. The default behavior is override.

 

The include tag can be used anywhere a list of component definitions would be otherwise valid. It is thus possible to create basic configurations like:

base.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration xmlns="http://age.iisg.agh.edu.pl/schema/age"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://age.iisg.agh.edu.pl/schema/age http://age.iisg.agh.edu.pl/schema/age/age.xsd">
 
 <component name="childComponent" class="foo.bar.ComponentClass" /> 
  <property name="someProperty">
   <list>
    <block name="elements">
    </block>  
   </list>  
  </property>  
 </component>  

 <block name="someService">
  <component class="foo.bar.DefaultServiceClass" />
 </block>     
</configuration>

and customized configuration as needed:

<?xml version="1.0" encoding="UTF-8"?>
<configuration xmlns="http://age.iisg.agh.edu.pl/schema/age"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://age.iisg.agh.edu.pl/schema/age http://age.iisg.agh.edu.pl/schema/age/age.xsd">

 <include target="classpath:base.xml">
  <block name="elements">
   <multiple count="4">
    <component class="foo.bar.ElementClass" /> 
   </multiple> 
   <include target="classpath:more_elements.xml">   
  </block> 
  <block name="someService">
   <component class="foo.bar.CustomServiceClass" />
  </block> 
 </include>       
</configuration>

Placeholders

 

To further increase the reusability of configuration files, specific values can be defined as placeholders and their values extracted to property files.

As an example, let's take a factory component which creates an initial population of solutions for an evolutionary algorithm. An obvious property of such a factory is the initial population size. Instead of hardcoding that value in the configuration file, we can use a placeholder as follows:

<strategy class="org.jage.population.PopulationFactory">
  <property name="populationSize" type="Integer" value="${population.size}" />
</strategy>

The actual value of that property will be resolved at runtime. The container will look up a dictionary for a property named population.size and replace the placeholder before evaluating the whole configuration file.

The container creates a dictionary of properties in the following way:

  • First, all system properties are loaded.
  • Then, the container looks for a system property named age.config.properties, which consists in a comma-separated list of property file resources (e.g. -Dage.config.properties=classpath:some.properties,classpath:other.properties). The properties contained in each file are then loaded.

 

Note that the property values can thus be overwritten: property files take precedence over system properties, and each property file takes precedence over the ones earlier in the list.
The result is that just like with configuration files, you can define default properties and then override them for specific applications.