ACS-6329 Move generate-hazelcast-config.py to community-repo (#2328)

This commit is contained in:
Domenico Sibilio
2023-11-20 16:20:08 +01:00
committed by GitHub
parent 5490228d71
commit 9f4ec29f9c
5 changed files with 189 additions and 1 deletions

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<hazelcast xmlns="http://www.hazelcast.com/schema/config"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.hazelcast.com/schema/config
http://www.hazelcast.com/schema/config/hazelcast-config-5.3.xsd">
<cluster-name>Replace this with a secure value</cluster-name>
<management-center data-access-enabled="${alfresco.hazelcast.mancenter.enabled}">
<trusted-interfaces>
<interface>${alfresco.hazelcast.mancenter.url}</interface>
</trusted-interfaces>
</management-center>
<!-- Ephemeral lock store map definition -->
<map name="lockStore">
<backup-count>1</backup-count>
<!-- No overall size limit, since this would result in ephemeral locks being evicted. -->
<merge-policy>com.hazelcast.spi.merge.PutIfAbsentMergePolicy</merge-policy>
<eviction eviction-policy="NONE" max-size-policy="PER_NODE" size="0"/>
<!-- TTL here must match LockServiceImpl.MAX_EPHEMERAL_LOCK_SECONDS -->
<time-to-live-seconds>172800</time-to-live-seconds>
<max-idle-seconds>0</max-idle-seconds>
</map>
<!-- CACHES DEFINITION (DO NOT REMOVE THIS PLACEHOLDER) -->
</hazelcast>

View File

@@ -0,0 +1,21 @@
# These caches definitions are automatically adapted to XML configuration
# and used for integration test purposes. Be mindful when changing these!
# cache to test max items setting
cache.maxItemsCache.maxItems=1000
cache.maxItemsCache.timeToLiveSeconds=0
cache.maxItemsCache.maxIdleSeconds=0
cache.maxItemsCache.cluster.type=fully-distributed
cache.maxItemsCache.backup-count=1
cache.maxItemsCache.eviction-policy=LRU
cache.maxItemsCache.merge-policy=com.hazelcast.spi.merge.PutIfAbsentMergePolicy
cache.maxItemsCache.readBackupData=false
# cache to test time to live setting
cache.ttlCache.timeToLiveSeconds=1
cache.ttlCache.maxIdleSeconds=0
cache.ttlCache.cluster.type=fully-distributed
cache.ttlCache.backup-count=1
cache.ttlCache.eviction-policy=LRU
cache.ttlCache.merge-policy=com.hazelcast.spi.merge.PutIfAbsentMergePolicy
cache.ttlCache.readBackupData=false

View File

@@ -0,0 +1,141 @@
#!/usr/bin/env python
"""Python script to convert Hazelcast cache definitions from Java properties to XML"""
__author__="Domenico Sibilio"
import collections
import configparser
import xml.etree.ElementTree as ET
from xml.dom import minidom
from typing import List
from pathlib import Path
import argparse
ROOT_PATH = Path(__file__).parent
CONFIG_PATH = ROOT_PATH / 'ci-caches.properties'
OUTPUT_PATH = ROOT_PATH / 'alfresco-hazelcast-config.xml'
TEMPLATE_PATH = ROOT_PATH / 'alfresco-hazelcast-template.xml'
TEMPLATE_PLACEHOLDER = '<!-- CACHES DEFINITION (DO NOT REMOVE THIS PLACEHOLDER) -->'
PROPS_TO_XML = {
# time-to-live-seconds with value = x
'timeToLiveSeconds': 'time-to-live-seconds',
# max-idle-seconds with value = x
'maxIdleSeconds': 'max-idle-seconds',
# backup-count with value = x
'backup-count': 'backup-count',
# read-backup-data with value = x
'readBackupData': 'read-backup-data',
# merge-policy with value = x
'merge-policy': 'merge-policy',
# eviction with eviction-policy=x and max-size-policy=PER_NODE and size=${maxItems}
'eviction-policy': 'eviction',
# near-cache.eviction max-size-policy=ENTRY_COUNT, eviction-policy=LRU and size = x
'nearCache.maxSize': 'size',
# near-cache.max-idle-seconds with value = x
'nearCache.maxIdleSeconds': 'max-idle-seconds',
# near-cache.time-to-live-seconds with value = x
'nearCache.timeToLiveSeconds': 'time-to-live-seconds',
}
def get_prop(prop_key: str):
# shortcut to get the property within the default section
return config.get('default', prop_key)
def get_cache_name(sections: List[str]):
# get the cache name given the full property string split by '.'
return '.'.join(sections[0:get_cache_name_index(sections)+1])
def get_cache_name_index(sections: List[str]):
# returns the index where the cache name ends
# given the full property string split by '.'
for i, e in enumerate(sections):
if e.endswith('Cache'):
return i
return 1
def get_key(sections: List[str]):
# get the property key name given the full property string split by '.'
cn_index = get_cache_name_index(sections)
key_sections = sections[cn_index+1::]
return '.'.join(key_sections)
def prettify(xml_string: str):
# format and indent an xml string
prettified_xml = minidom.parseString(xml_string).toprettyxml(indent=' ')
return '\n'.join([line for line in prettified_xml.splitlines() if line.strip()])
# entrypoint
parser = argparse.ArgumentParser(description='A Python script to generate XML Hazelcast cache definitions starting from Java properties.')
parser.add_argument('-s', '--source', default=CONFIG_PATH, help='path to the Java properties file to convert')
args = parser.parse_args()
source_path = args.source
# add dummy section to properties
with open(source_path, 'r') as f:
cache_props = '[default]\n' + f.read()
config = configparser.ConfigParser()
# preserve property case
config.optionxform = str
# parse config file
config.read_string(cache_props)
# group properties by cache name
props_by_cache = collections.defaultdict(list)
for item in config.items('default'):
sections = item[0].split('.')
if sections[0] == 'cache':
cache_name = get_cache_name(sections)
key = get_key(sections)
value = item[1]
props_by_cache[cache_name].append((key, value))
# read template file
with open(TEMPLATE_PATH, 'r') as input:
template = input.read()
# perform template substitutions to apply the caches.properties configuration
map_configs = ''
for cache, props in props_by_cache.items():
props = dict(props)
if(props.get('cluster.type') == 'fully-distributed'):
map = ET.Element('map', name=cache)
near_cache = None
for prop, value in props.items():
xml_prop = PROPS_TO_XML.get(prop)
# handle eviction configuration
if prop == 'eviction-policy':
ET.SubElement(map, xml_prop,
{'eviction-policy': value,
'max-size-policy': 'PER_NODE',
'size': props.get('maxItems') if props.get('maxItems') else '0'})
# handle near-cache configuration
elif prop.startswith('nearCache'):
if near_cache is None:
near_cache = ET.SubElement(map, 'near-cache')
if prop.split('.')[1] == 'maxSize':
ET.SubElement(near_cache, 'eviction',
{'max-size-policy': 'ENTRY_COUNT',
'eviction-policy': 'LRU',
xml_prop: value})
else:
ET.SubElement(near_cache, xml_prop).text = value
# handle basic map configuration
elif xml_prop:
ET.SubElement(map, xml_prop).text = value
ET.SubElement(map, 'per-entry-stats-enabled').text = 'true'
map_configs += minidom.parseString(ET.tostring(map)).childNodes[0].toprettyxml(indent=' ')
template = template.replace(TEMPLATE_PLACEHOLDER, map_configs)
# produce actual Hazelcast config file
with open(OUTPUT_PATH, 'w') as output:
output.write(prettify(template))
print(f"Generated XML Hazelcast config: {OUTPUT_PATH}")