DUE TO SPAM, SIGN-UP IS DISABLED. Goto Selfserve wiki signup and request an account.
THIS PAGE IS A WORK IN PROGRESS.
...
Some of the class names are already out of date.
Overview
This page discusses generation and application of custom configuration elements, as well as their interaction with the ClusterConfigurationService interface.
Please note that, at time of writing, the ClusterConfigurationService is considered @Experimental. Method signatures are subject to change prior to full support.
Background
Historically, cluster member configuration would be saved in a cache.xml, which would be provided to each member at startup. This would require a cluster administrator to maintain individual XML files for each member.
Since Geode 1.0, we have provided as an alternative the cluster-configuration option. In essence, using cluster configuration allowed a cluster's locator(s) would create, store and distribute the necessary XML files to members that require them when the member attempts to join the cluster.
We wish to remove our current reliance on the XML format, and to make available to extension developers a programatic API to interact with the cluster configuration. This is discussed in more detail in the API proposal, linked below.
That public API will require an extension developer to indicate those classes that contain configuration information for their extension objects by having those classes implement CacheElement. Please note that these configuration classes are themselves distinct from the instantiated object whose configuration they represent.
Currently, we use the Java architecture for XML Binding (JAXB) to marshal and unmarshal these configuration objects to and from the XML maintained by the configuration service. As a result, configuration objects must contain the require JAXB annotations. If an extension developer's configuration is defined by an XSD, these files can be easily generated via xjc. An example generation script is provided below.
In a future iteration, the cluster configuration could sore configuration information not as XML, but as the raw configuration object itself. From that point onward, the configuration object's fields will not require the JAXB annotations, as marshaling and unmarshaling to XML will no longer occur.
Additional API proposal and discussion will occur as that date draws closer. In the interim, an extension developer's configuration classes will require the JAXB annotations so that it may interact properly with the existing services.
Note that all of the above discusses only those classes containing configuration information. To reiterate, these configuration classes are themselves distinct from any instantiated extension object or service. Until the internal reliance on XML has been overcome, it will remain that any extension class must implement Extension. In particular, this includes providing the XmlGenerator for your extension class.
WORK IN PROGRESS: It appears that Extension is an internal interface. I will now write a proposal about publicizing the Extension API, which will be linked here in a future revision.
Placement of Configuration Elements within the Configuration
The configuration format In the past, the format of a configuration, as represented internally as XML, is specified in the file cache-1.0.xsd. This XSD motivates the generation of our Java configuration objects, as we move away from our reliance on XML.
This specification allows for custom configuration elements to appear in one of two places: in the top-level "cache" or in the first child-level "region." Remember that separate
Separate (non-conflicting) configuration information can exist for the entire cluster as well as each member group; the designation of "cache" and "region" is orthogonal to the designation of members to groups. Do not confuse the "cache" level to mean a global configuration. Every member group will define its own Indeed, each group can define different aspects of the total "cache" configuration.
The "cache" level is intended to contain configuration elements that effect cache member state and operation, where the "region" level is intended to contain configuration elements that effect management of data. For instance, the Lucene Index configuration object will be is stored as a "region" element, since it acts on a region's data.
...
Creating Configuration Classes
WORK IN PROGRESS: The interface name and structure is probably going to change soon.
Whether your configuration element will exist at the "cache" level or at the "region" level, your custom configuration class should implement org.apache.geode.cache.configuration.CacheElement. This requires your configuration object to be Serializable and Identifiable<String>.
If your custom configuration elements are themselves defined by an XSD, compatible configuration classes can be generated using JAXB. A simple bindings file similar to that below can impose implementation of CacheElement on generated classes. Episode files may also be passed as a JAXB binding to indicate that Geode configuration classes have already been generated. These episode files can be found in <geode root>/etc/.Generation See the Resources section below for example bindings and scripts to generate classes using JAXB and the command-line tool xjc. Alternatively, generation can be performed by some IDEs, such as IDEA IntelliJ. Alternatively, the command-line tool xjc can be used to generate classes. Refer to xjc task in each of :geode-core, :geode-connectors, and :geode-lucene for example command-line xjc commands, reproduced below for your convenience, and XJC plugins exist for Maven, Ant, and Gradle build systems.
JAXB generation provides a good starting point for your configuration classes. However, manual adjustment of generated classes may will likely be required. For instance, if your configuration object does not define an id field, the generated class will need to be updated to implement a getId as part of the CacheElement interface.
Interacting with the Cluster Configuration Service
A typical use case is to create, modify, or remove your configuration object from the cache configuration service in a method belonging to a class extending GfshCommand. The LuceneIndexCommands.java class demonstrates these cases.
The following pattern is useful in many of these commands. First, the GfshCommand (acting on a locator to which you are connected) instantiates the configuration object, sets desired values, and distributes the configuration object (perhaps with additional parameters) to the cluster members that require configuration. Each member then uses the configuration object to create / update / halt the service that the configuration defines. Results are aggregated back to the locator. The locator then decides, based on reported success or failures, if the cache configuration should be updated. This is typically achieved by passing the ClusterConfigurationService the configuration object used. For instance:
| Code Block |
|---|
@CliCommand(value = LuceneCliStrings.LUCENE_CREATE_INDEX,
help = LuceneCliStrings.LUCENE_CREATE_INDEX__HELP)
@CliMetaData(relatedTopic = {CliStrings.TOPIC_GEODE_REGION, CliStrings.TOPIC_GEODE_DATA})
public Result createIndex(@CliOption(key = LuceneCliStrings.LUCENE__INDEX_NAME, mandatory = true,
help = LuceneCliStrings.LUCENE_CREATE_INDEX__NAME__HELP) final String indexName,
@CliOption(key = LuceneCliStrings.LUCENE__REGION_PATH, mandatory = true,
optionContext = ConverterHint.REGION_PATH,
help = LuceneCliStrings.LUCENE_CREATE_INDEX__REGION_HELP) final String regionPath,
@CliOption(key = LuceneCliStrings.LUCENE_CREATE_INDEX__FIELD, mandatory = true,
help = LuceneCliStrings.LUCENE_CREATE_INDEX__FIELD_HELP) final String[] fields,
@CliOption(key = LuceneCliStrings.LUCENE_CREATE_INDEX__ANALYZER,
help = LuceneCliStrings.LUCENE_CREATE_INDEX__ANALYZER_HELP) final String[] analyzers,
@CliOption(key = LuceneCliStrings.LUCENE_CREATE_INDEX__SERIALIZER,
help = LuceneCliStrings.LUCENE_CREATE_INDEX__SERIALIZER_HELP) final String serializer) {
// Every lucene index potentially writes to disk.
authorize(Resource.CLUSTER, Operation.MANAGE, LucenePermission.TARGET);
// Build Lucene Index Configuration object
Index indexConfig = new Index();
indexConfig.setName(indexName);
DeclarableType declarableSerializer = new DeclarableType();
declarableSerializer.setClassName(serializer);
indexConfig.setSerializer(declarableSerializer);
List<Index.Field> indexFields = new ArrayList<>();
for (int i = 0; i < analyzers.length; i++) {
Index.Field thisField = new Index.Field();
thisField.setAnalyzer(analyzers[i].trim());
thisField.setName(fields[i].trim());
}
indexConfig.getField().addAll(indexFields);
// Perform the index creation on relevant members.
LuceneIndexInfo indexInfo = new LuceneIndexInfo(indexConfig, regionPath);
ResultCollector<?, ?> rc = executeFunctionOnAllMembers(createIndexFunction, indexInfo);
List<CliFunctionResult> funcResults = (List<CliFunctionResult>) rc.getResult();
// Build results display
final TabularResultData tabularResult = ResultBuilder.createTabularResultData();
for (final CliFunctionResult cliFunctionResult : funcResults) {
tabularResult.accumulate("Member", cliFunctionResult.getMemberIdOrName());
if (cliFunctionResult.isSuccessful()) {
tabularResult.accumulate("Status", "Successfully created lucene index");
} else {
tabularResult.accumulate("Status", "Failed: " + cliFunctionResult.getMessage());
}
}
// Determine if the configuration was successful and, if so, persist the change.
boolean creationSuccessful =
funcResults.stream().map(CliFunctionResult::isSuccessful).reduce(true, (x, y) -> x && y);
if (creationSuccessful) {
getConfigurationService().saveCustomRegionElement("cache", regionPath, indexConfig);
}
return ResultBuilder.buildResult(tabularResult);
} |
See the documentation of the ClusterConfigurationService interface for more details.
Resources (TODO, resolve attachments / links)
- Files referenced:
- XSD
- Bindings
- Episode files
- xjc tasks in builds
- LuceneIndexCommands
- LuceneIndexConfig class
- Also refer to
- Cluster Configuration Service Interface
See the Cluster Configuration Service proposals for examples on how your configuration object will interact with the configuration persistence service.
Resources
- Example generation files. Unpack these in your Apache Geode root directory.
- Also refer to