Configurator

Configurator is a tool to assemble configuration of Java objects in a GUI editor and then instantiate the configuration at runtime and parameterize it with substitution tokens and deployment profiles.

One of definitions of software development is Software development is a process of binding decisions to make them executable 1). With Configurator decisions binding is organized as follows:

  • At development time most stable decisions are bound in the code, e.g. that order total is the sum of order items plus tax amount and S&H 2), and that tax amount is the sum or order items multiplied by tax rate. Actual values of tax rate and S&H are not bound, but setters for these values are provided.
  • At assembly time a configuration file is created. At this time some configuration values can be bound by providing concrete values, binding of other configuration values can be deferred by using substitution tokens instead of values. Also, configuration values can be organized in groups called profiles.
  • Runtime is the final stage of binding. At this time the application provides values for substitution tokens and profile path.

While Configurator can be used by its own, the primary purpose of the product is

  • To provide foundation for more specific configuration editors.
  • The Configurator model to be the target of transformation from other models/sources to make those models executable.

For example, Euphrates model extends Configurator model. Also, Euphrates flow definitions get transformed to Configurator models for execution.

License

Version

Value proposition

Separation of non-functional concerns (configuration, qualities of service, deployment variations) core functional concerns (business logic).

Requirements

Resources and downloads

Product components

  • Configuration editor - Eclipse plug-in for editing configuration files.
  • Runtime libraries.

Problem statement

In modern applications significant part of code deals with configuration. For example, to make a connection with a database the application shall know:

  • Driver class
  • Connection URL
  • User name
  • Password

If an application wants to send a JMS message, then number of configuration parameters increases dramatically because of vendor-specific configuration parameters and quality of service parameters (e.g. persistent/non persistent, time to live).

So a real life application shall deal sometimes with hundreds if not thousands configuration parameters. Then, some applications shall be deployed to multiple environments (e.g. Development, Test, Production) with. Some parameters will be the same in all environments, some will differ by environment, some will be server-specific. Maintenance of dozens of slightly different configuration files becomes a nightmare.

With Configurator you can have a single configuration file parameterizable at deployment time with profile name and substitution tokens.

Tutorial

This tutorial shows how to create configuration for a simple class and then instantiate and configure the class from the configuration file.

Set up

  • Install the editor plug-in from the Update site as shown here.
  • Download runtime libraries.
  • Create Java project.
  • Add runtime libraries to the project build path.

Create business class

The code below shows out business class. Note that it is “pure business” - there is no code dealing with reading configuration.

Receipt.java
package com.hammurapi.test;
 
import java.util.Map;
 
public class Receipt {
 
	private String companyName;
	private double tax;
	private String promotion;
 
	public void setCompanyName(String companyName) {
		this.companyName = companyName;
	}	
 
	public void setTax(Double tax) {
		this.tax = tax;
	}
 
	public void setPromotion(String promotion) {
		this.promotion = promotion;
	}
 
	public void print(Map<String, Double> items) {
		System.out.println("\t\t"+companyName);
		double total = 0;
		for (Map.Entry<String, Double> entry: items.entrySet()) {
			System.out.println(entry.getKey()+":\t"+entry.getValue());
			total+=entry.getValue();
		}
		System.out.println("---------");
		System.out.println("Tax:\t"+tax+" %");
		System.out.println("Total:\t"+total*(1+tax/100));
		if (promotion!=null) {
			System.out.println();
			System.out.println("*** "+promotion+" ***");
		}		
	}
 
}

Create configuration file

Create a configuration for our business as shown here.

Edit configuration

This presentation shows how to populate configuration model in the editor.

This is the resulting configuration file:

My.hgconfig
<?xml version="1.0" encoding="UTF-8"?>
<com.hammurapi.config:ObjectDefinition 
    xmi:version="2.0" 
    xmlns:xmi="http://www.omg.org/XMI" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:com.hammurapi.config="http://www.hammurapi.com/config"
    description="Receipt printer"
    type="com.hammurapi.test.Receipt">
 
  <property 
      xsi:type="com.hammurapi.config:NamedObjectDefinition" 
      description="Company name" 
      name="companyName" value="HG Store"/>
 
  <property xsi:type="com.hammurapi.config:NamedObjectDefinition" name="promotion" value="$[promotion]"/>
 
  <profile name="st_johns" description="Saint Johns County">
    <property 
         xsi:type="com.hammurapi.config:NamedObjectDefinition"
         name="tax" 
         value="6.0"
         type=""/>
  </profile>
 
  <profile name="duval">
    <property xsi:type="com.hammurapi.config:NamedObjectDefinition" name="tax" value="7.0"/>
  </profile>
</com.hammurapi.config:ObjectDefinition>

Client code (application)

The code below shows how to instantiate our business object from configuration and instantiation time parameters.

MyApp.java
package com.hammurapi.test;
 
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
 
import org.eclipse.emf.ecore.resource.Resource;
 
import com.hammurapi.config.Factory;
import com.hammurapi.config.runtime.ConfigurationException;
import com.hammurapi.config.runtime.FactoryConfig;
import com.hammurapi.config.runtime.FactoryResult;
import com.hammurapi.config.runtime.MapTokenSource;
import com.hammurapi.config.runtime.ResourceLoader;
 
public class MyApp {
 
	/**
	 * @param args
	 * @throws ConfigurationException 
	 */
	public static void main(String[] args) throws Exception {
		Resource resource = ResourceLoader.load("My.hgconfig");
		Factory root = (Factory) resource.getContents().get(0);
		FactoryConfig fConfig = new FactoryConfig();
		fConfig.setProfilePath("st_johns");
 
		Map<String, String> tokens = new HashMap<String, String>();
		tokens.put("promotion", "Organic milk - buy one, get one free!");
		fConfig.setTokenSource(new MapTokenSource(tokens));
 
		FactoryResult<Object> fr = root.create(fConfig);
		try {
			Receipt receipt = (Receipt) fr.call();
 
			Map<String, Double> items = new TreeMap<String, Double>();
			items.put("Milk", 3.53);
			items.put("Bread", 1.82);
			receipt.print(items);
		} finally {
			fr.destroy();
		}
	}
 
}

Note that the configuration file is parameterized at instantiation time with

  • Profile name (St. Johns or Duval county). Tax rate is loaded from a profile.
  • Expansion token. Promotion comes from the expansion token.

This program produces the following output:

		HG Store
Bread:	1.82
Milk:	3.53
---------
Tax:	6.0 %
Total:	5.671

*** Organic milk - buy one, get one free! ***

If we change profile to duval, tax rate will change to 7.0%. This example is very simple and is intended primarily to demonstrate how to use the tool.

Application-specific configurations

Imagine you have a middleware JMS application called MyBroker. It listens for requests on JMS queues. When it receives a request, it communicates with a number of back-end systems also over JMS and sends a reply to requesting application. The application uses a thread pool for parallel processing of multiple requests. The application processes requests differently depending on request parameters and there are hundreds of different combinations. The application is deployed to multiple environments with different connection parameters, quality of service settings, and thread pool configurations. In order to improve manageability of the application you decided to use Configurator to configure the application.

While you can use Configurator AS-IS, it is better to create an application-specific ECore model based on the Configurator model. The model would have MyBroker root class with references to JmsConnection class for the front-end and back-end connections. It will also have a reference to a thread pool definition.

To do so you need to:

  • Download Configurator sources.
  • Import Configurator project into your workspace.
  • Create a model of your application and reference Configurator model from it.

Creating configuration models programmatically

Configurator models can be created programmatically. It can be needed in the following cases:

  • Configurator model is a target model of transformation (PSM). As mentioned earlier, Euphrates flows are transformed to Configurator models for execution.
  • Initial loading of a large configurator model from previous format (e.g. property file(s)).

The tutorial contains a class which shows how to create configurator models programmatically. The code of this class is below:

BuildModel.java
package com.hammurapi.test;
 
import java.io.File;
 
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;
 
import com.hammurapi.config.ConfigFactory;
import com.hammurapi.config.ObjectDefinition;
 
 
/**
 * This class builds configurator model programmatically and saves it to Generated.hgconfig
 * @author Pavel Vlasov
 *
 */
public class BuildModel {
 
	/**
	 * @param args
	 */
	public static void main(String[] args) throws Exception {
		ObjectDefinition objDef = ConfigFactory.eINSTANCE.createObjectDefinition();
		objDef.setDescription("Generated object definition");
		objDef.setType("java.lang.Integer");
		objDef.setValue("1234");
 
		ResourceSet resourceSet = new ResourceSetImpl();
		// Register the appropriate resource factory to handle all file extensions.
		//
		resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put(
				Resource.Factory.Registry.DEFAULT_EXTENSION, new XMIResourceFactoryImpl());
 
		URI uri = URI.createFileURI(new File("Generated.hgconfig").getAbsolutePath());
		Resource configResource = resourceSet.createResource(uri);
		EList<EObject> contents = configResource.getContents();
		contents.add(objDef);
		configResource.save(null);
	}
 
}

This code produces configuration file shown below:

<?xml version="1.0" encoding="ASCII"?>
<com.hammurapi.config:ObjectDefinition 
    xmi:version="2.0" 
    xmlns:xmi="http://www.omg.org/XMI" 
    xmlns:com.hammurapi.config="http://www.hammurapi.com/config" 
    description="Generated object definition" 
    value="1234" 
    type="java.lang.Integer"/>
2) Shipping and Handling
Last modified: 2010/04/02 19:32 by Pavel Vlasov
   
 
Except where otherwise noted, content on this wiki is licensed under the following license:CC Attribution-Noncommercial-Share Alike 3.0 Unported
Hammurapi Group