Version 1

    Infinispan 5.1 introduced a builder-style fluent configuration API and a new StAX-based XML configuration parser. These were however limited to the configuration of the core elements of Infinispan, relying on key/value properties for configuring any additional modules.

    Infinispan 5.2 extends the above so that all modules can define their own builders and parsers.

     

    Implementing a Configuration Builder for a Module

     

    First of all you need to write your configuration POJO. Here is a example:

     

    package org.infinispan.mymodule; 
    import org.infinispan.configuration.BuiltBy;  
    
    @BuiltBy(MyModuleConfigurationBuilder.class) 
    public class MyModuleConfiguration {
         final private String attribute;
    
         MyModuleConfiguration(String attribute) {
             this.attribute = attribute;
         }
    
         public String attribute() {
            return attribute;     
         }
    }
    

     

    Notice that the constructor uses the "default" Java visibility, which means that only classes within its package can create it. Also all fields are final, which enforce the immutability of the configuration. In order to build this object we need a builder:

     

     

    package org.infinispan.configuration.module;
    
    import org.infinispan.configuration.cache.AbstractModuleConfigurationBuilder;
    import org.infinispan.configuration.cache.ConfigurationBuilder;
    
    public class MyModuleConfigurationBuilder extends AbstractModuleConfigurationBuilder<MyModuleConfiguration> {
       private String attribute;
    
       public MyModuleConfigurationBuilder(ConfigurationBuilder builder) {
          super(builder);
       }
    
       public MyModuleConfigurationBuilder attribute(String attribute) {
          this.attribute = attribute;
          return this;
       }
    
       @Override
       public void validate() {
       }
    
       @Override
       public MyModuleConfiguration create() {
          return new MyModuleConfiguration(attribute);
       }
    
       @Override
       public MyModuleConfigurationBuilder read(MyModuleConfiguration template) {
          this.attribute(template.attribute());
          return this;
       }
    }
    

     

    A builder object must provide setter methods for all configurable aspects of your module. It must extend org.infinispan.configuration.cache.AbstractModuleConfigurationBuilder and be parameterized with the configuration POJO it creates. The configuration POJO also needs to be annotated with a @BuiltBy annotation to provide the opposite information.

     

    Using the above API, we can now configure it programmatically:

     

    ConfigurationBuilder builder = new ConfigurationBuilder();
    builder.addModule(MyModuleConfigurationBuilder.class).attribute("myValue");
    
    Configuration configuration = builder.build();
    EmbeddedCacheManager cm = new DefaultCacheManager(configuration);
    

     

    Modules can retrieve configuration from a cache as follows:

     

     

    String attributeValue = cache.getCacheConfiguration().module(MyModuleConfiguration.class).attribute();
    

     

    The above examples deal with per-cache configuration. Modules which need their configuration to be stored at the GlobalConfiguration level simply need builder which extend org.infinispan.configuration.cache.AbstractGlobalModuleConfigurationBuilder.

     

    Implementing a Configuration Parser for a Module

    As mentioned above, Infinispan uses StAX to parse its configuration. Since 5.2 this configuration can be extended by modules in two places: within the <modules> sub-element of the <global> element for GlobalConfiguration and within the <modules> sub-element of the <default> and <namedCache> elements for per-cache Configuration.

    First of all you need to write a parser for your module. The following is a skeleton:

     

    import javax.xml.stream.XMLStreamException;
    import org.infinispan.configuration.cache.ConfigurationBuilder;
    import org.infinispan.configuration.parsing.ConfigurationBuilderHolder;
    import org.infinispan.configuration.parsing.ConfigurationParser;
    import org.infinispan.configuration.parsing.Namespace;
    import org.infinispan.configuration.parsing.ParseUtils;
    import org.jboss.staxmapper.XMLExtendedStreamReader;
    
    public class MyConfigurationParser implements ConfigurationParser&lt;ConfigurationBuilderHolder&gt; {
    
       @Override
       public Namespace[] getSupportedNamespaces() {
          return new Namespace[] {
             new Namespace(Namespace.INFINISPAN_NS_BASE_URI, "mymodule", "mymodule", 5, 2),
             new Namespace("", "mymodule", 0, 0)
          };
       }
       @Override
       public void readElement(XMLExtendedStreamReader reader, ConfigurationBuilderHolder holder) throws XMLStreamException {
       }
    }
    

     

    As each module is responsible for parsing its own configuration, it must indicate the namespaces it supports. In the above example, the parser supports the mymodule root element both in the urn:infinispan:config:mymodule:5.2 namespace as well as a simple, namespace-less element (to allow configuration without requiring a schema, although this is not recommended). The versioning is important and, whenever you change your schema, you should provide a new parser for the new version. Also, parsers for previous versions should be maintained for backwards compatibility at least within a major release number.

    Before continuing we must also create a standard Java ServiceProvider file within the same Jar file which holds the module's parser. This file must be named META-INF/services/org.infinispan.configuration.parsing.ConfigurationParser and contain the fully-qualified class name of your parser.

    This allows Infinispan's parser registry to pick up the additional parser and register it with the appropriate namespace.

    When the main parser reads the following XML file:

     

    <default>
      <modules>
        <mymodule xmlns="urn:infinispan:config:mymodule:5.2" attribute="myvalue" />
      </modules>
    </default>
    

     

    it will invoke the MyConfigurationParser.readElement() method with the reader positioned at the mymodule element and with a holder for the currently parsed configuration.

    The following code parses the XML and constructs the appropriate builder:

     

    @Override
       public void readElement(XMLExtendedStreamReader reader, ConfigurationBuilderHolder holder) throws XMLStreamException {
          ConfigurationBuilder builder = holder.getCurrentConfigurationBuilder();
    
          Element element = Element.forName(reader.getLocalName());
          switch (element) {
          case MYMODULE: {
             parseMyModule(reader, builder.addModule(MyModuleConfigurationBuilder.class));
             break;
          }
          default: {
             throw ParseUtils.unexpectedElement(reader);
          }
          }
       }
    
       private void parseMyModule(XMLExtendedStreamReader reader, MyModuleConfigurationBuilder builder)
             throws XMLStreamException {
          for (int i = 0; i < reader.getAttributeCount(); i++) {
             ParseUtils.requireNoNamespaceAttribute(reader, i);
             String value = replaceProperties(reader.getAttributeValue(i));
             Attribute attribute = Attribute.forName(reader.getAttributeLocalName(i));
             switch (attribute) {
             case ATTRIBUTE: {
                builder.attribute(value);
                break;
             }
             default: {
                throw ParseUtils.unexpectedAttribute(reader, i);
             }
             }
          }
    
       }
    

     

    Element and Attribute are convenience enums so that a switch/case statement can be used.

     

    A module should also provide an XSD to describe the XML's schema:

     

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" version="1.0" targetNamespace="urn:infinispan:config:mymodule:5.2" xmlns:tns="urn:infinispan:config:mymodule:5.2" xmlns:config="urn:infinispan:config:5.2" xmlns:xs="http://www.w3.org/2001/XMLSchema">
      <xs:import namespace="urn:infinispan:config:5.2" schemaLocation="http://www.infinispan.org/schemas/infinispan-config-5.2.xsd" />
      <xs:complexType name="mymodule">
        <xs:complexContent>
          <xs:attribute name="attribute" type="xs:string" />
        </xs:complexContent>
      </xs:complexType>
    </xs:schema>
    

     

    Since the above schema is also importing the main Infinispan configuration schema, it can refer to types defined therein by prefixing them with the config namespace.

    Loaders and Stores

    Loaders and Stores can also provide their own builders and XML schemas in a similar fashion, but they need to adhere to stricter requirements.

     

    Firstly the configuration POJOs and their respective builders must implement the LoaderConfiguration or StoreConfiguration interfaces (there are convenience abstract classes such as AbstractLoaderConfiguration and AbstractStoreConfiguration which make this easier).

    Also, to provide backwards compatibility with the previous properties-based configuration, the configuration POJOs should also implement the org.infinispan.configuration.cache.LegacyLoaderAdapter<T> interface so that they can generate a traditional CacheLoaderConfig or CacheStoreConfig. This will become unnecessary in Infinispan 6.0 where the old org.infinispan.config API will be removed.