/* * Copyright (c) 2008-2017, Hazelcast, Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.hazelcast.config; import com.hazelcast.config.CacheSimpleConfig.ExpiryPolicyFactoryConfig; import com.hazelcast.config.CacheSimpleConfig.ExpiryPolicyFactoryConfig.DurationConfig; import com.hazelcast.config.CacheSimpleConfig.ExpiryPolicyFactoryConfig.TimedExpiryPolicyFactoryConfig; import com.hazelcast.logging.ILogger; import com.hazelcast.logging.Logger; import com.hazelcast.nio.serialization.DataSerializableFactory; import com.hazelcast.nio.serialization.PortableFactory; import com.hazelcast.util.CollectionUtil; import com.hazelcast.util.MapUtil; import javax.xml.transform.OutputKeys; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import java.io.StringReader; import java.io.StringWriter; import java.util.ArrayDeque; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.Set; import static com.hazelcast.nio.IOUtil.closeResource; import static com.hazelcast.util.Preconditions.isNotNull; import static com.hazelcast.util.StringUtil.isNullOrEmpty; /** * The ConfigXmlGenerator is responsible for transforming a {@link Config} to a Hazelcast XML string. */ public class ConfigXmlGenerator { protected static final String MASK_FOR_SESITIVE_DATA = "****"; private static final ILogger LOGGER = Logger.getLogger(ConfigXmlGenerator.class); private static final int INDENT = 5; private final boolean formatted; /** * Creates a ConfigXmlGenerator that will format the code. */ public ConfigXmlGenerator() { this(true); } /** * Creates a ConfigXmlGenerator. * * @param formatted true if the XML should be formatted, false otherwise. */ public ConfigXmlGenerator(boolean formatted) { this.formatted = formatted; } /** * Generates the XML string based on some Config. * * @param config the configuration. * @return the XML string. */ public String generate(Config config) { isNotNull(config, "Config"); final StringBuilder xml = new StringBuilder(); final XmlGenerator gen = new XmlGenerator(xml); xml.append("<hazelcast ") .append("xmlns=\"http://www.hazelcast.com/schema/config\"\n") .append("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n") .append("xsi:schemaLocation=\"http://www.hazelcast.com/schema/config ") .append("http://www.hazelcast.com/schema/config/hazelcast-config-3.9.xsd\">"); gen.open("group") .node("name", config.getGroupConfig().getName()) .node("password", MASK_FOR_SESITIVE_DATA) .close() .node("license-key", MASK_FOR_SESITIVE_DATA) .node("instance-name", config.getInstanceName()); if (config.getManagementCenterConfig() != null) { final ManagementCenterConfig mcConfig = config.getManagementCenterConfig(); gen.node("management-center", mcConfig.getUrl(), "enabled", mcConfig.isEnabled(), "update-interval", mcConfig.getUpdateInterval()); } gen.appendProperties(config.getProperties()); wanReplicationXmlGenerator(gen, config); networkConfigXmlGenerator(gen, config); mapConfigXmlGenerator(gen, config); replicatedMapConfigXmlGenerator(gen, config); cacheConfigXmlGenerator(gen, config); queueXmlGenerator(gen, config); multiMapXmlGenerator(gen, config); collectionXmlGenerator(gen, "list", config.getListConfigs().values()); collectionXmlGenerator(gen, "set", config.getSetConfigs().values()); topicXmlGenerator(gen, config); semaphoreXmlGenerator(gen, config); lockXmlGenerator(gen, config); ringbufferXmlGenerator(gen, config); executorXmlGenerator(gen, config); durableExecutorXmlGenerator(gen, config); scheduledExecutorXmlGenerator(gen, config); partitionGroupXmlGenerator(gen, config); cardinalityEstimatorXmlGenerator(gen, config); listenerXmlGenerator(gen, config); serializationXmlGenerator(gen, config); reliableTopicXmlGenerator(gen, config); liteMemberXmlGenerator(gen, config); nativeMemoryXmlGenerator(gen, config); servicesXmlGenerator(gen, config); hotRestartXmlGenerator(gen, config); xml.append("</hazelcast>"); return format(xml.toString(), INDENT); } private static void collectionXmlGenerator(XmlGenerator gen, String type, Collection<? extends CollectionConfig> configs) { if (CollectionUtil.isNotEmpty(configs)) { for (CollectionConfig c : configs) { gen.open(type, "name", c.getName()) .node("statistics-enabled", c.isStatisticsEnabled()) .node("max-size", c.getMaxSize()) .node("backup-count", c.getBackupCount()) .node("async-backup-count", c.getAsyncBackupCount()); appendItemListenerConfigs(gen, c.getItemListenerConfigs()); gen.close(); } } } private static void replicatedMapConfigXmlGenerator(XmlGenerator gen, Config config) { for (ReplicatedMapConfig r : config.getReplicatedMapConfigs().values()) { gen.open("replicatedmap", "name", r.getName()) .node("in-memory-format", r.getInMemoryFormat()) .node("concurrency-level", r.getConcurrencyLevel()) .node("replication-delay-millis", r.getReplicationDelayMillis()) .node("async-fillup", r.isAsyncFillup()) .node("statistics-enabled", r.isStatisticsEnabled()); if (!r.getListenerConfigs().isEmpty()) { gen.open("entry-listeners"); for (ListenerConfig lc : r.getListenerConfigs()) { gen.node("entry-listener", classNameOrImplClass(lc.getClassName(), lc.getImplementation()), "include-value", lc.isIncludeValue(), "local", lc.isLocal()); } gen.close(); } gen.close(); } } private static void listenerXmlGenerator(XmlGenerator gen, Config config) { if (config.getListenerConfigs().isEmpty()) { return; } gen.open("listeners"); for (ListenerConfig lc : config.getListenerConfigs()) { gen.node("listener", classNameOrImplClass(lc.getClassName(), lc.getImplementation())); } gen.close(); } @SuppressWarnings({"checkstyle:npathcomplexity"}) private static void serializationXmlGenerator(XmlGenerator gen, Config config) { final SerializationConfig c = config.getSerializationConfig(); if (c == null) { return; } gen.open("serialization") .node("portable-version", c.getPortableVersion()) .node("use-native-byte-order", c.isUseNativeByteOrder()) .node("byte-order", c.getByteOrder()) .node("enable-compression", c.isEnableCompression()) .node("enable-shared-object", c.isEnableSharedObject()) .node("allow-unsafe", c.isAllowUnsafe()); final Map<Integer, String> dsfClasses = c.getDataSerializableFactoryClasses(); final Map<Integer, DataSerializableFactory> dsfImpls = c.getDataSerializableFactories(); if (!MapUtil.isNullOrEmpty(dsfClasses) || !MapUtil.isNullOrEmpty(dsfImpls)) { gen.open("data-serializable-factories"); appendSerializationFactory(gen, "data-serializable-factory", dsfClasses); appendSerializationFactory(gen, "data-serializable-factory", dsfImpls); gen.close(); } final Map<Integer, String> portableClasses = c.getPortableFactoryClasses(); final Map<Integer, PortableFactory> portableImpls = c.getPortableFactories(); if (!MapUtil.isNullOrEmpty(portableClasses) || !MapUtil.isNullOrEmpty(portableImpls)) { gen.open("portable-factories"); appendSerializationFactory(gen, "portable-factory", portableClasses); appendSerializationFactory(gen, "portable-factory", portableImpls); gen.close(); } final Collection<SerializerConfig> serializers = c.getSerializerConfigs(); final GlobalSerializerConfig globalSerializerConfig = c.getGlobalSerializerConfig(); if (CollectionUtil.isNotEmpty(serializers) || globalSerializerConfig != null) { gen.open("serializers"); if (globalSerializerConfig != null) { gen.node("global-serializer", classNameOrImplClass( globalSerializerConfig.getClassName(), globalSerializerConfig.getImplementation()), "override-java-serialization", globalSerializerConfig.isOverrideJavaSerialization()); } if (CollectionUtil.isNotEmpty(serializers)) { for (SerializerConfig serializer : serializers) { gen.node("serializer", null, "type-class", classNameOrClass(serializer.getTypeClassName(), serializer.getTypeClass()), "class-name", classNameOrImplClass(serializer.getClassName(), serializer.getImplementation())); } } gen.close(); } gen.node("check-class-def-errors", c.isCheckClassDefErrors()) .close(); } private static String classNameOrClass(String className, Class clazz) { return !isNullOrEmpty(className) ? className : clazz != null ? clazz.getName() : null; } private static String classNameOrImplClass(String className, Object impl) { return !isNullOrEmpty(className) ? className : impl != null ? impl.getClass().getName() : null; } private static void partitionGroupXmlGenerator(XmlGenerator gen, Config config) { final PartitionGroupConfig pg = config.getPartitionGroupConfig(); if (pg == null) { return; } gen.open("partition-group", "enabled", pg.isEnabled(), "group-type", pg.getGroupType()); final Collection<MemberGroupConfig> configs = pg.getMemberGroupConfigs(); if (CollectionUtil.isNotEmpty(configs)) { for (MemberGroupConfig mgConfig : configs) { gen.open("member-group"); for (String iface : mgConfig.getInterfaces()) { gen.node("interface", iface); } gen.close(); } } gen.close(); } private static void executorXmlGenerator(XmlGenerator gen, Config config) { for (ExecutorConfig ex : config.getExecutorConfigs().values()) { gen.open("executor-service", "name", ex.getName()) .node("statistics-enabled", ex.isStatisticsEnabled()) .node("pool-size", ex.getPoolSize()) .node("queue-capacity", ex.getQueueCapacity()) .close(); } } private static void durableExecutorXmlGenerator(XmlGenerator gen, Config config) { for (DurableExecutorConfig ex : config.getDurableExecutorConfigs().values()) { gen.open("durable-executor-service", "name", ex.getName()) .node("pool-size", ex.getPoolSize()) .node("durability", ex.getDurability()) .node("capacity", ex.getCapacity()) .close(); } } private static void scheduledExecutorXmlGenerator(XmlGenerator gen, Config config) { for (ScheduledExecutorConfig ex : config.getScheduledExecutorConfigs().values()) { gen.open("scheduled-executor-service", "name", ex.getName()) .node("pool-size", ex.getPoolSize()) .node("durability", ex.getDurability()) .node("capacity", ex.getCapacity()) .close(); } } private static void cardinalityEstimatorXmlGenerator(XmlGenerator gen, Config config) { for (CardinalityEstimatorConfig ex : config.getCardinalityEstimatorConfigs().values()) { gen.open("cardinality-estimator", "name", ex.getName()) .node("backup-count", ex.getBackupCount()) .node("async-backup-count", ex.getAsyncBackupCount()) .close(); } } private static void semaphoreXmlGenerator(XmlGenerator gen, Config config) { for (SemaphoreConfig sc : config.getSemaphoreConfigs()) { gen.open("semaphore", "name", sc.getName()) .node("initial-permits", sc.getInitialPermits()) .node("backup-count", sc.getBackupCount()) .node("async-backup-count", sc.getAsyncBackupCount()) .close(); } } private static void topicXmlGenerator(XmlGenerator gen, Config config) { for (TopicConfig t : config.getTopicConfigs().values()) { gen.open("topic", "name", t.getName()) .node("statistics-enabled", t.isStatisticsEnabled()) .node("global-ordering-enabled", t.isGlobalOrderingEnabled()); if (!t.getMessageListenerConfigs().isEmpty()) { gen.open("message-listeners"); for (ListenerConfig lc : t.getMessageListenerConfigs()) { gen.node("message-listener", classNameOrImplClass(lc.getClassName(), lc.getImplementation())); } gen.close(); } gen.node("multi-threading-enabled", t.isMultiThreadingEnabled()); gen.close(); } } private static void reliableTopicXmlGenerator(XmlGenerator gen, Config config) { for (ReliableTopicConfig t : config.getReliableTopicConfigs().values()) { gen.open("reliable-topic", "name", t.getName()) .node("statistics-enabled", t.isStatisticsEnabled()) .node("read-batch-size", t.getReadBatchSize()) .node("topic-overload-policy", t.getTopicOverloadPolicy()); if (!t.getMessageListenerConfigs().isEmpty()) { gen.open("message-listeners"); for (ListenerConfig lc : t.getMessageListenerConfigs()) { gen.node("message-listener", classNameOrImplClass(lc.getClassName(), lc.getImplementation())); } gen.close(); } gen.close(); } } private static void multiMapXmlGenerator(XmlGenerator gen, Config config) { for (MultiMapConfig mm : config.getMultiMapConfigs().values()) { gen.open("multimap", "name", mm.getName()) .node("backup-count", mm.getBackupCount()) .node("async-backup-count", mm.getAsyncBackupCount()) .node("statistics-enabled", mm.isStatisticsEnabled()) .node("binary", mm.isBinary()) .node("value-collection-type", mm.getValueCollectionType()); if (!mm.getEntryListenerConfigs().isEmpty()) { gen.open("entry-listeners"); for (EntryListenerConfig lc : mm.getEntryListenerConfigs()) { gen.node("entry-listener", classNameOrImplClass(lc.getClassName(), lc.getImplementation()), "include-value", lc.isIncludeValue(), "local", lc.isLocal()); } gen.close(); } // if (mm.getPartitioningStrategyConfig() != null) { // xml.append("<partition-strategy>"); // PartitioningStrategyConfig psc = mm.getPartitioningStrategyConfig(); // if (psc.getPartitioningStrategy() != null) { // xml.append(psc.getPartitioningStrategy().getClass().getName()); // } else { // xml.append(psc.getPartitioningStrategyClass()); // } // xml.append("</partition-strategy>"); // } gen.close(); } } private static void queueXmlGenerator(XmlGenerator gen, Config config) { final Collection<QueueConfig> qCfgs = config.getQueueConfigs().values(); for (QueueConfig q : qCfgs) { gen.open("queue", "name", q.getName()) .node("statistics-enabled", q.isStatisticsEnabled()) .node("max-size", q.getMaxSize()) .node("backup-count", q.getBackupCount()) .node("async-backup-count", q.getAsyncBackupCount()) .node("empty-queue-ttl", q.getEmptyQueueTtl()); appendItemListenerConfigs(gen, q.getItemListenerConfigs()); final QueueStoreConfig storeConfig = q.getQueueStoreConfig(); if (storeConfig != null) { gen.open("queue-store", "enabled", storeConfig.isEnabled()) .node("class-name", storeConfig.getClassName()) .node("factory-class-name", storeConfig.getFactoryClassName()) .appendProperties(storeConfig.getProperties()) .close(); } gen.node("quorum-ref", q.getQuorumName()) .close(); } } private static void lockXmlGenerator(XmlGenerator gen, Config config) { for (LockConfig c : config.getLockConfigs().values()) { gen.open("lock", "name", c.getName()) .node("quorum-ref", c.getQuorumName()) .close(); } } private static void ringbufferXmlGenerator(XmlGenerator gen, Config config) { final Collection<RingbufferConfig> configs = config.getRingbufferConfigs().values(); for (RingbufferConfig rbConfig : configs) { gen.open("ringbuffer", "name", rbConfig.getName()) .node("capacity", rbConfig.getCapacity()) .node("time-to-live-seconds", rbConfig.getTimeToLiveSeconds()) .node("backup-count", rbConfig.getBackupCount()) .node("async-backup-count", rbConfig.getAsyncBackupCount()) .node("in-memory-format", rbConfig.getInMemoryFormat()); final RingbufferStoreConfig storeConfig = rbConfig.getRingbufferStoreConfig(); if (storeConfig != null) { gen.open("ringbuffer-store", "enabled", storeConfig.isEnabled()) .node("class-name", storeConfig.getClassName()) .node("factory-class-name", storeConfig.getFactoryClassName()) .appendProperties(storeConfig.getProperties()); gen.close(); } gen.close(); } } private static void wanReplicationXmlGenerator(XmlGenerator gen, Config config) { for (WanReplicationConfig wan : config.getWanReplicationConfigs().values()) { gen.open("wan-replication", "name", wan.getName()); for (WanPublisherConfig p : wan.getWanPublisherConfigs()) { gen.open("wan-publisher", "group-name", p.getGroupName()) .node("class-name", p.getClassName()) .node("queue-full-behavior", p.getQueueFullBehavior()) .node("queue-capacity", p.getQueueCapacity()) .appendProperties(p.getProperties()); awsConfigXmlGenerator(gen, p.getAwsConfig()); discoveryStrategyConfigXmlGenerator(gen, p.getDiscoveryConfig()); gen.close(); } final WanConsumerConfig consumerConfig = wan.getWanConsumerConfig(); if (consumerConfig != null) { gen.open("wan-consumer") .node("class-name", classNameOrImplClass(consumerConfig.getClassName(), consumerConfig.getImplementation())) .appendProperties(consumerConfig.getProperties()) .close(); } gen.close(); } } private static void networkConfigXmlGenerator(XmlGenerator gen, Config config) { final NetworkConfig netCfg = config.getNetworkConfig(); gen.open("network") .node("public-address", netCfg.getPublicAddress()) .node("port", netCfg.getPort(), "port-count", netCfg.getPortCount(), "auto-increment", netCfg.isPortAutoIncrement()) .node("reuse-address", netCfg.isReuseAddress()); final Collection<String> outboundPortDefinitions = netCfg.getOutboundPortDefinitions(); if (CollectionUtil.isNotEmpty(outboundPortDefinitions)) { gen.open("outbound-ports"); for (String def : outboundPortDefinitions) { gen.node("ports", def); } gen.close(); } final JoinConfig join = netCfg.getJoin(); gen.open("join"); multicastConfigXmlGenerator(gen, join); tcpConfigXmlGenerator(gen, join); awsConfigXmlGenerator(gen, join.getAwsConfig()); discoveryStrategyConfigXmlGenerator(gen, join.getDiscoveryConfig()); gen.close(); interfacesConfigXmlGenerator(gen, netCfg); sslConfigXmlGenerator(gen, netCfg); socketInterceptorConfigXmlGenerator(gen, netCfg); symmetricEncInterceptorConfigXmlGenerator(gen, netCfg); gen.close(); } private static void mapConfigXmlGenerator(XmlGenerator gen, Config config) { final Collection<MapConfig> mCfgs = config.getMapConfigs().values(); for (MapConfig m : mCfgs) { final String cacheDeserializedVal = m.getCacheDeserializedValues() != null ? m.getCacheDeserializedValues().name().replaceAll("_", "-") : null; gen.open("map", "name", m.getName()) .node("in-memory-format", m.getInMemoryFormat()) .node("statistics-enabled", m.isStatisticsEnabled()) .node("optimize-queries", m.isOptimizeQueries()) .node("cache-deserialized-values", cacheDeserializedVal) .node("backup-count", m.getBackupCount()) .node("async-backup-count", m.getAsyncBackupCount()) .node("time-to-live-seconds", m.getTimeToLiveSeconds()) .node("max-idle-seconds", m.getMaxIdleSeconds()) .node("eviction-policy", m.getEvictionPolicy()) .node("max-size", m.getMaxSizeConfig().getSize(), "policy", m.getMaxSizeConfig().getMaxSizePolicy()) .node("eviction-percentage", m.getEvictionPercentage()) .node("min-eviction-check-millis", m.getMinEvictionCheckMillis()) .node("merge-policy", m.getMergePolicy()) .node("read-backup-data", m.isReadBackupData()); appendHotRestartConfig(gen, m.getHotRestartConfig()); mapStoreConfigXmlGenerator(gen, m); mapNearCacheConfigXmlGenerator(gen, m.getNearCacheConfig()); wanReplicationConfigXmlGenerator(gen, m.getWanReplicationRef()); mapIndexConfigXmlGenerator(gen, m); mapAttributeConfigXmlGenerator(gen, m); mapEntryListenerConfigXmlGenerator(gen, m); mapPartitionLostListenerConfigXmlGenerator(gen, m); mapPartitionStrategyConfigXmlGenerator(gen, m); gen.close(); } } private static void appendHotRestartConfig(XmlGenerator gen, HotRestartConfig m) { gen.open("hot-restart", "enabled", m != null && m.isEnabled()) .node("fsync", m != null && m.isFsync()) .close(); } private static void cacheConfigXmlGenerator(XmlGenerator gen, Config config) { for (CacheSimpleConfig c : config.getCacheConfigs().values()) { gen.open("cache", "name", c.getName()); if (c.getKeyType() != null) { gen.node("key-type", null, "class-name", c.getKeyType()); } if (c.getValueType() != null) { gen.node("value-type", null, "class-name", c.getValueType()); } gen.node("statistics-enabled", c.isStatisticsEnabled()) .node("management-enabled", c.isManagementEnabled()) .node("read-through", c.isReadThrough()) .node("write-through", c.isWriteThrough()); checkAndFillCacheLoaderFactoryConfigXml(gen, c.getCacheLoaderFactory()); checkAndFillCacheLoaderConfigXml(gen, c.getCacheLoader()); checkAndFillCacheWriterFactoryConfigXml(gen, c.getCacheWriterFactory()); checkAndFillCacheWriterConfigXml(gen, c.getCacheWriter()); cacheExpiryPolicyFactoryConfigXmlGenerator(gen, c.getExpiryPolicyFactoryConfig()); gen.open("cache-entry-listeners"); for (CacheSimpleEntryListenerConfig el : c.getCacheEntryListeners()) { gen.open("cache-entry-listener", "old-value-required", el.isOldValueRequired(), "synchronous", el.isSynchronous()) .node("cache-entry-listener-factory", null, "class-name", el.getCacheEntryListenerFactory()) .node("cache-entry-event-filter-factory", null, "class-name", el.getCacheEntryEventFilterFactory()) .close(); } gen.close() .node("in-memory-format", c.getInMemoryFormat()) .node("backup-count", c.getBackupCount()) .node("async-backup-count", c.getAsyncBackupCount()); evictionConfigXmlGenerator(gen, c.getEvictionConfig()); wanReplicationConfigXmlGenerator(gen, c.getWanReplicationRef()); gen.node("quorum-ref", c.getQuorumName()); cachePartitionLostListenerConfigXmlGenerator(gen, c.getPartitionLostListenerConfigs()); gen.node("merge-policy", c.getMergePolicy()); appendHotRestartConfig(gen, c.getHotRestartConfig()); gen.node("disable-per-entry-invalidation-events", c.isDisablePerEntryInvalidationEvents()) .close(); } } private static void checkAndFillCacheWriterFactoryConfigXml(XmlGenerator gen, String cacheWriter) { if (isNullOrEmpty(cacheWriter)) { return; } gen.node("cache-writer-factory", null, "class-name", cacheWriter); } private static void checkAndFillCacheWriterConfigXml(XmlGenerator gen, String cacheWriter) { if (isNullOrEmpty(cacheWriter)) { return; } gen.node("cache-writer", null, "class-name", cacheWriter); } private static void checkAndFillCacheLoaderFactoryConfigXml(XmlGenerator gen, String cacheLoader) { if (isNullOrEmpty(cacheLoader)) { return; } gen.node("cache-loader-factory", null, "class-name", cacheLoader); } private static void checkAndFillCacheLoaderConfigXml(XmlGenerator gen, String cacheLoader) { if (isNullOrEmpty(cacheLoader)) { return; } gen.node("cache-loader", null, "class-name", cacheLoader); } private static void cacheExpiryPolicyFactoryConfigXmlGenerator(XmlGenerator gen, ExpiryPolicyFactoryConfig config) { if (config == null) { return; } if (!isNullOrEmpty(config.getClassName())) { gen.node("expiry-policy-factory", null, "class-name", config.getClassName()); } else { final TimedExpiryPolicyFactoryConfig timedConfig = config.getTimedExpiryPolicyFactoryConfig(); if (timedConfig != null && timedConfig.getExpiryPolicyType() != null && timedConfig.getDurationConfig() != null) { final DurationConfig duration = timedConfig.getDurationConfig(); gen.open("expiry-policy-factory") .node("timed-expiry-policy-factory", null, "expiry-policy-type", timedConfig.getExpiryPolicyType(), "duration-amount", duration.getDurationAmount(), "time-unit", duration.getTimeUnit().name()) .close(); } } } private static void cachePartitionLostListenerConfigXmlGenerator(XmlGenerator gen, List<CachePartitionLostListenerConfig> configs) { if (configs.isEmpty()) { return; } gen.open("partition-lost-listeners"); for (CachePartitionLostListenerConfig c : configs) { gen.node("partition-lost-listener", classNameOrImplClass(c.getClassName(), c.getImplementation())); } gen.close(); } private static void mapPartitionStrategyConfigXmlGenerator(XmlGenerator gen, MapConfig m) { if (m.getPartitioningStrategyConfig() != null) { final PartitioningStrategyConfig psc = m.getPartitioningStrategyConfig(); gen.node("partition-strategy", classNameOrImplClass(psc.getPartitioningStrategyClass(), psc.getPartitioningStrategy())); } } private static void mapEntryListenerConfigXmlGenerator(XmlGenerator gen, MapConfig m) { if (!m.getEntryListenerConfigs().isEmpty()) { gen.open("entry-listeners"); for (EntryListenerConfig lc : m.getEntryListenerConfigs()) { gen.node("entry-listener", classNameOrImplClass(lc.getClassName(), lc.getImplementation()), "include-value", lc.isIncludeValue(), "local", lc.isLocal()); } gen.close(); } } private static void mapPartitionLostListenerConfigXmlGenerator(XmlGenerator gen, MapConfig m) { if (!m.getPartitionLostListenerConfigs().isEmpty()) { gen.open("partition-lost-listeners"); for (MapPartitionLostListenerConfig c : m.getPartitionLostListenerConfigs()) { gen.node("partition-lost-listener", classNameOrImplClass(c.getClassName(), c.getImplementation())); } gen.close(); } } private static void mapIndexConfigXmlGenerator(XmlGenerator gen, MapConfig m) { if (!m.getMapIndexConfigs().isEmpty()) { gen.open("indexes"); for (MapIndexConfig indexCfg : m.getMapIndexConfigs()) { gen.node("index", indexCfg.getAttribute(), "ordered", indexCfg.isOrdered()); } gen.close(); } } private static void mapAttributeConfigXmlGenerator(XmlGenerator gen, MapConfig m) { if (!m.getMapAttributeConfigs().isEmpty()) { gen.open("attributes"); for (MapAttributeConfig attributeCfg : m.getMapAttributeConfigs()) { gen.node("attribute", attributeCfg.getName(), "extractor", attributeCfg.getExtractor()); } gen.close(); } } private static void wanReplicationConfigXmlGenerator(XmlGenerator gen, WanReplicationRef wan) { if (wan != null) { gen.open("wan-replication-ref", "name", wan.getName()) .node("merge-policy", wan.getMergePolicy()); final List<String> filters = wan.getFilters(); if (CollectionUtil.isNotEmpty(filters)) { gen.open("filters"); for (String f : filters) { gen.node("filter-impl", f); } gen.close(); } gen.node("republishing-enabled", wan.isRepublishingEnabled()) .close(); } } private static void mapStoreConfigXmlGenerator(XmlGenerator gen, MapConfig m) { if (m.getMapStoreConfig() != null) { final MapStoreConfig s = m.getMapStoreConfig(); final String clazz = s.getImplementation() != null ? s.getImplementation().getClass().getName() : s.getClassName(); final String factoryClass = s.getFactoryImplementation() != null ? s.getFactoryImplementation().getClass().getName() : s.getFactoryClassName(); gen.open("map-store", "enabled", s.isEnabled()) .node("class-name", clazz) .node("factory-class-name", factoryClass) .node("write-delay-seconds", s.getWriteDelaySeconds()) .node("write-batch-size", s.getWriteBatchSize()) .appendProperties(s.getProperties()) .close(); } } private static void mapNearCacheConfigXmlGenerator(XmlGenerator gen, NearCacheConfig n) { if (n != null) { gen.open("near-cache") .node("in-memory-format", n.getInMemoryFormat()) .node("invalidate-on-change", n.isInvalidateOnChange()) .node("time-to-live-seconds", n.getTimeToLiveSeconds()) .node("max-idle-seconds", n.getMaxIdleSeconds()); evictionConfigXmlGenerator(gen, n.getEvictionConfig()); gen .node("eviction-policy", n.getEvictionPolicy()) .node("max-size", n.getMaxSize()) .node("cache-local-entries", n.isCacheLocalEntries()); gen.close(); } } private static void evictionConfigXmlGenerator(XmlGenerator gen, EvictionConfig e) { if (e == null) { return; } final String comparatorClassName = !isNullOrEmpty(e.getComparatorClassName()) ? e.getComparatorClassName() : null; gen.node("eviction", null, "size", e.getSize(), "max-size-policy", e.getMaximumSizePolicy(), "eviction-policy", e.getEvictionPolicy(), "comparator-class-name", comparatorClassName); } private static void multicastConfigXmlGenerator(XmlGenerator gen, JoinConfig join) { final MulticastConfig mcast = join.getMulticastConfig(); gen.open("multicast", "enabled", mcast.isEnabled(), "loopbackModeEnabled", mcast.isLoopbackModeEnabled()) .node("multicast-group", mcast.getMulticastGroup()) .node("multicast-port", mcast.getMulticastPort()) .node("multicast-timeout-seconds", mcast.getMulticastTimeoutSeconds()) .node("multicast-time-to-live", mcast.getMulticastTimeToLive()); if (!mcast.getTrustedInterfaces().isEmpty()) { gen.open("trusted-interfaces"); for (String trustedInterface : mcast.getTrustedInterfaces()) { gen.node("interface", trustedInterface); } gen.close(); } gen.close(); } private static void tcpConfigXmlGenerator(XmlGenerator gen, JoinConfig join) { final TcpIpConfig c = join.getTcpIpConfig(); gen.open("tcp-ip", "enabled", c.isEnabled(), "connection-timeout-seconds", c.getConnectionTimeoutSeconds()) .open("member-list"); for (String m : c.getMembers()) { gen.node("member", m); } gen.close() .node("required-member", c.getRequiredMember()) .close(); } private static void awsConfigXmlGenerator(XmlGenerator gen, AwsConfig c) { if (c == null) { return; } gen.open("aws", "enabled", c.isEnabled()) .node("access-key", c.getAccessKey()) .node("secret-key", c.getSecretKey()) .node("iam-role", c.getIamRole()) .node("region", c.getRegion()) .node("host-header", c.getHostHeader()) .node("security-group-name", c.getSecurityGroupName()) .node("tag-key", c.getTagKey()) .node("tag-value", c.getTagValue()) .close(); } private static void discoveryStrategyConfigXmlGenerator(XmlGenerator gen, DiscoveryConfig c) { if (c == null) { return; } gen.open("discovery-strategies"); final String nodeFilterClass = classNameOrImplClass(c.getNodeFilterClass(), c.getNodeFilter()); if (nodeFilterClass != null) { gen.node("node-filter", null, "class", nodeFilterClass); } final Collection<DiscoveryStrategyConfig> configs = c.getDiscoveryStrategyConfigs(); if (CollectionUtil.isNotEmpty(configs)) { for (DiscoveryStrategyConfig config : configs) { gen.open("discovery-strategy", "class", classNameOrImplClass(config.getClassName(), config.getDiscoveryStrategyFactory()), "enabled", "true") .appendProperties(config.getProperties()) .close(); } } gen.close(); } private static void interfacesConfigXmlGenerator(XmlGenerator gen, NetworkConfig netCfg) { final InterfacesConfig interfaces = netCfg.getInterfaces(); gen.open("interfaces", "enabled", interfaces.isEnabled()); for (String i : interfaces.getInterfaces()) { gen.node("interface", i); } gen.close(); } private static void sslConfigXmlGenerator(XmlGenerator gen, NetworkConfig netCfg) { final SSLConfig ssl = netCfg.getSSLConfig(); gen.open("ssl", "enabled", ssl != null && ssl.isEnabled()); if (ssl != null) { Properties props = new Properties(); props.putAll(ssl.getProperties()); if (props.containsKey("keyStorePassword")) { props.setProperty("keyStorePassword", MASK_FOR_SESITIVE_DATA); } gen.node("factory-class-name", classNameOrImplClass(ssl.getFactoryClassName(), ssl.getFactoryImplementation())) .appendProperties(props); } gen.close(); } private static void socketInterceptorConfigXmlGenerator(XmlGenerator gen, NetworkConfig netCfg) { final SocketInterceptorConfig socket = netCfg.getSocketInterceptorConfig(); gen.open("socket-interceptor", "enabled", socket != null && socket.isEnabled()); if (socket != null) { gen.node("class-name", classNameOrImplClass(socket.getClassName(), socket.getImplementation())) .appendProperties(socket.getProperties()); } gen.close(); } private static void symmetricEncInterceptorConfigXmlGenerator(XmlGenerator gen, NetworkConfig netCfg) { final SymmetricEncryptionConfig sec = netCfg.getSymmetricEncryptionConfig(); if (sec == null) { return; } gen.open("symmetric-encryption", "enabled", sec.isEnabled()) .node("algorithm", sec.getAlgorithm()) .node("salt", MASK_FOR_SESITIVE_DATA) .node("password", MASK_FOR_SESITIVE_DATA) .node("iteration-count", sec.getIterationCount()) .close(); } private static void hotRestartXmlGenerator(XmlGenerator gen, Config config) { HotRestartPersistenceConfig hrCfg = config.getHotRestartPersistenceConfig(); if (hrCfg == null) { gen.node("hot-restart-persistence", "enabled", "false"); return; } gen.open("hot-restart-persistence", "enabled", hrCfg.isEnabled()) .node("base-dir", hrCfg.getBaseDir().getAbsolutePath()); if (hrCfg.getBackupDir() != null) { gen.node("backup-dir", hrCfg.getBackupDir().getAbsolutePath()); } gen.node("parallelism", hrCfg.getParallelism()) .node("validation-timeout-seconds", hrCfg.getValidationTimeoutSeconds()) .node("data-load-timeout-seconds", hrCfg.getDataLoadTimeoutSeconds()) .node("cluster-data-recovery-policy", hrCfg.getClusterDataRecoveryPolicy()) .close(); } private static void nativeMemoryXmlGenerator(XmlGenerator gen, Config config) { NativeMemoryConfig nativeMemoryConfig = config.getNativeMemoryConfig(); if (nativeMemoryConfig == null) { gen.node("native-memory", null, "enabled", "false"); return; } gen.open("native-memory", "enabled", nativeMemoryConfig.isEnabled(), "allocator-type", nativeMemoryConfig.getAllocatorType()) .node("size", null, "unit", nativeMemoryConfig.getSize().getUnit(), "value", nativeMemoryConfig.getSize().getValue()) .node("min-block-size", nativeMemoryConfig.getMinBlockSize()) .node("page-size", nativeMemoryConfig.getPageSize()) .node("metadata-space-percentage", nativeMemoryConfig.getMetadataSpacePercentage()) .close(); } private static void servicesXmlGenerator(XmlGenerator gen, Config config) { final ServicesConfig c = config.getServicesConfig(); if (c == null) { return; } gen.open("services", "enable-defaults", c.isEnableDefaults()); if (CollectionUtil.isNotEmpty(c.getServiceConfigs())) { for (ServiceConfig serviceConfig : c.getServiceConfigs()) { gen.open("service", "enabled", serviceConfig.isEnabled()) .node("name", serviceConfig.getName()) .node("class-name", classNameOrImplClass(serviceConfig.getClassName(), serviceConfig.getImplementation())) .appendProperties(serviceConfig.getProperties()) .close(); } } gen.close(); } private static void liteMemberXmlGenerator(XmlGenerator gen, Config config) { gen.node("lite-member", null, "enabled", config.isLiteMember()); } private String format(final String input, int indent) { if (!formatted) { return input; } StreamResult xmlOutput = null; try { final Source xmlInput = new StreamSource(new StringReader(input)); xmlOutput = new StreamResult(new StringWriter()); TransformerFactory transformerFactory = TransformerFactory.newInstance(); /* Older versions of Xalan still use this method of setting indent values. * Attempt to make this work but don't completely fail if it's a problem. */ try { transformerFactory.setAttribute("indent-number", indent); } catch (IllegalArgumentException e) { if (LOGGER.isFinestEnabled()) { LOGGER.finest("Failed to set indent-number attribute; cause: " + e.getMessage()); } } Transformer transformer = transformerFactory.newTransformer(); transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); /* Newer versions of Xalan will look for a fully-qualified output property in order to specify amount of * indentation to use. Attempt to make this work as well but again don't completely fail if it's a problem. */ try { transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", Integer.toString(indent)); } catch (IllegalArgumentException e) { if (LOGGER.isFinestEnabled()) { LOGGER.finest("Failed to set indent-amount property; cause: " + e.getMessage()); } } transformer.transform(xmlInput, xmlOutput); return xmlOutput.getWriter().toString(); } catch (Exception e) { LOGGER.warning(e); return input; } finally { if (xmlOutput != null) { closeResource(xmlOutput.getWriter()); } } } private static void appendItemListenerConfigs(XmlGenerator gen, Collection<ItemListenerConfig> configs) { if (CollectionUtil.isNotEmpty(configs)) { gen.open("item-listeners"); for (ItemListenerConfig lc : configs) { gen.node("item-listener", lc.getClassName(), "include-value", lc.isIncludeValue()); } gen.close(); } } private static void appendSerializationFactory(XmlGenerator gen, String elementName, Map<Integer, ?> factoryMap) { if (MapUtil.isNullOrEmpty(factoryMap)) { return; } for (Entry<Integer, ?> factory : factoryMap.entrySet()) { final Object value = factory.getValue(); final String className = value instanceof String ? (String) value : value.getClass().getName(); gen.node(elementName, className, "factory-id", factory.getKey().toString()); } } private static final class XmlGenerator { private final StringBuilder xml; private final ArrayDeque<String> openNodes = new ArrayDeque<String>(); private XmlGenerator(StringBuilder xml) { this.xml = xml; } XmlGenerator open(String name, Object... attributes) { appendOpenNode(xml, name, attributes); openNodes.addLast(name); return this; } XmlGenerator node(String name, Object contents, Object... attributes) { appendNode(xml, name, contents, attributes); return this; } XmlGenerator close() { appendCloseNode(xml, openNodes.pollLast()); return this; } XmlGenerator appendProperties(Properties props) { if (!props.isEmpty()) { open("properties"); Set keys = props.keySet(); for (Object key : keys) { node("property", props.getProperty(key.toString()), "name", key.toString()); } close(); } return this; } XmlGenerator appendProperties(Map<String, Comparable> props) { if (!MapUtil.isNullOrEmpty(props)) { open("properties"); for (Entry<String, Comparable> entry : props.entrySet()) { node("property", entry.getValue(), "name", entry.getKey()); } close(); } return this; } private static void appendOpenNode(StringBuilder xml, String name, Object... attributes) { xml.append('<').append(name); appendAttributes(xml, attributes); xml.append('>'); } private static void appendCloseNode(StringBuilder xml, String name) { xml.append("</").append(name).append('>'); } private static void appendAttributes(StringBuilder xml, Object... attributes) { for (int i = 0; i < attributes.length; ) { xml.append(" ").append(attributes[i++]).append("=\"").append(attributes[i++]).append("\""); } } private static void appendNode(StringBuilder xml, String name, Object contents, Object... attributes) { if (contents != null || attributes.length > 0) { xml.append('<').append(name); for (int i = 0; i < attributes.length; ) { final Object key = attributes[i++]; final Object val = attributes[i++]; if (val != null) { xml.append(" ").append(key).append("=\"").append(val).append("\""); } } if (contents != null) { xml.append('>').append(contents).append("</").append(name).append('>'); } else { xml.append("/>"); } } } } }