====== 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__"// (([[wp>Ivar_Jacobson|Ivar Jackobson]])). 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 ((Shipping and Handling)), 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, [[products:euphrates:start]] model extends Configurator model. Also, Euphrates flow definitions get transformed to Configurator models for execution. ===== License ===== [[wp>LGPL]] ===== Version ===== [[http://www.hammurapi.com/mantis/changelog_page.php?project_id=3|1.3.0]] ===== Value proposition ===== Separation of non-functional concerns (configuration, qualities of service, deployment variations) core functional concerns (business logic). ===== Requirements ===== * [[http://java.sun.com/javase/6/|Java 6]] * [[http://www.eclipse.org|Eclipse]] 3.5 for the configuration editor plug-in. ===== Resources and downloads ===== * [[http://www.hammurapi.com/products/config/com.hammurapi.config.zip|Runtime jar files.]] * [[http://www.hammurapi.com/products/config/com.hammurapi.config-src.zip|Sources]], includes Javadoc, model documentation, and jar files. * [[http://www.hammurapi.com/products/config/doc/model/index.html|Configuration model documentation]] * [[http://www.hammurapi.com/products/config/doc/api/index.html|Javadoc]] * [[products:update_site:start]] * [[http://www.hammurapi.com/products/config/com.hammurapi.config-tutorial.zip|Tutorial project]] * [[http://www.hammurapi.com/bb/viewforum.php?f=7|Discussion forum]] * [[http://www.hammurapi.com/mantis/|Issue tracking]] * [[http://www.hammurapi.com/mantis/roadmap_page.php?project_id=3|Roadmap]] ===== Product components ===== * Configuration editor - Eclipse plug-in for editing configuration files. * Runtime libraries. * [[configdoc]] ===== 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 [[products:update_site:start]] 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. 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 items) { System.out.println("\t\t"+companyName); double total = 0; for (Map.Entry 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: ==== Client code (application) ==== The code below shows how to instantiate our business object from configuration and instantiation time parameters. 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 tokens = new HashMap(); tokens.put("promotion", "Organic milk - buy one, get one free!"); fConfig.setTokenSource(new MapTokenSource(tokens)); FactoryResult fr = root.create(fConfig); try { Receipt receipt = (Receipt) fr.call(); Map items = new TreeMap(); 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 ([[wp>Platform-specific_model|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: 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 contents = configResource.getContents(); contents.add(objDef); configResource.save(null); } } This code produces configuration file shown below: