/* * Licensed to the Apache Software Foundation (ASF) under one or more contributor license * agreements. See the NOTICE file distributed with this work for additional information regarding * copyright ownership. The ASF licenses this file to You 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 org.apache.geode.distributed.internal; import static org.apache.geode.distributed.ConfigurationProperties.CLUSTER_CONFIGURATION_DIR; import static org.apache.geode.distributed.ConfigurationProperties.SECURITY_MANAGER; import static org.apache.geode.distributed.ConfigurationProperties.SECURITY_POST_PROCESSOR; import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.filefilter.DirectoryFileFilter; import org.apache.geode.CancelException; import org.apache.geode.cache.AttributesFactory; import org.apache.geode.cache.Cache; import org.apache.geode.cache.DataPolicy; import org.apache.geode.cache.DiskStore; import org.apache.geode.cache.Region; import org.apache.geode.cache.Scope; import org.apache.geode.cache.execute.ResultCollector; import org.apache.geode.distributed.DistributedLockService; import org.apache.geode.distributed.DistributedMember; import org.apache.geode.distributed.DistributedSystem; import org.apache.geode.distributed.internal.locks.DLockService; import org.apache.geode.internal.cache.GemFireCacheImpl; import org.apache.geode.internal.cache.InternalRegionArguments; import org.apache.geode.internal.cache.persistence.PersistentMemberID; import org.apache.geode.internal.cache.persistence.PersistentMemberManager; import org.apache.geode.internal.cache.persistence.PersistentMemberPattern; import org.apache.geode.internal.cache.xmlcache.CacheXmlGenerator; import org.apache.geode.internal.lang.StringUtils; import org.apache.geode.internal.logging.LogService; import org.apache.geode.management.internal.cli.CliUtil; import org.apache.geode.management.internal.configuration.callbacks.ConfigurationChangeListener; import org.apache.geode.management.internal.configuration.domain.Configuration; import org.apache.geode.management.internal.configuration.domain.SharedConfigurationStatus; import org.apache.geode.management.internal.configuration.domain.XmlEntity; import org.apache.geode.management.internal.configuration.functions.UploadJarFunction; import org.apache.geode.management.internal.configuration.messages.ConfigurationRequest; import org.apache.geode.management.internal.configuration.messages.ConfigurationResponse; import org.apache.geode.management.internal.configuration.messages.SharedConfigurationStatusResponse; import org.apache.geode.management.internal.configuration.utils.XmlUtils; import org.apache.logging.log4j.Logger; import org.w3c.dom.Document; import org.xml.sax.SAXException; import java.io.BufferedWriter; import java.io.File; import java.io.FileFilter; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.nio.file.Path; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Properties; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactoryConfigurationError; @SuppressWarnings({"deprecation", "unchecked"}) public class ClusterConfigurationService { private static final Logger logger = LogService.getLogger(); /** * Name of the directory where the shared configuration artifacts are stored */ public static final String CLUSTER_CONFIG_ARTIFACTS_DIR_NAME = "cluster_config"; private static final String CLUSTER_CONFIG_DISK_STORE_NAME = "cluster_config"; public static final String CLUSTER_CONFIG_DISK_DIR_PREFIX = "ConfigDiskDir_"; public static final String CLUSTER_CONFIG = "cluster"; /** * Name of the lock service used for shared configuration */ private static final String SHARED_CONFIG_LOCK_SERVICE_NAME = "__CLUSTER_CONFIG_LS"; /** * Name of the lock for locking the shared configuration */ public static final String SHARED_CONFIG_LOCK_NAME = "__CLUSTER_CONFIG_LOCK"; /** * Name of the region which is used to store the configuration information */ private static final String CONFIG_REGION_NAME = "_ConfigurationRegion"; private final String configDirPath; private final String configDiskDirPath; private final Set<PersistentMemberPattern> newerSharedConfigurationLocatorInfo = new HashSet<>(); private final AtomicReference<SharedConfigurationStatus> status = new AtomicReference<>(); private GemFireCacheImpl cache; private final DistributedLockService sharedConfigLockingService; public ClusterConfigurationService(Cache cache) throws IOException { this.cache = (GemFireCacheImpl) cache; Properties properties = cache.getDistributedSystem().getProperties(); // resolve the cluster config dir String clusterConfigRootDir = properties.getProperty(CLUSTER_CONFIGURATION_DIR); if (StringUtils.isBlank(clusterConfigRootDir)) { clusterConfigRootDir = System.getProperty("user.dir"); } else { File diskDir = new File(clusterConfigRootDir); if (!diskDir.exists() && !diskDir.mkdirs()) { throw new IOException("Cannot create directory : " + clusterConfigRootDir); } clusterConfigRootDir = diskDir.getCanonicalPath(); } // resolve the file paths String configDiskDirName = CLUSTER_CONFIG_DISK_DIR_PREFIX + cache.getDistributedSystem().getName(); configDirPath = FilenameUtils.concat(clusterConfigRootDir, CLUSTER_CONFIG_ARTIFACTS_DIR_NAME); configDiskDirPath = FilenameUtils.concat(clusterConfigRootDir, configDiskDirName); sharedConfigLockingService = getSharedConfigLockService(cache.getDistributedSystem()); status.set(SharedConfigurationStatus.NOT_STARTED); } /** * Gets or creates (if not created) shared configuration lock service */ private DistributedLockService getSharedConfigLockService(DistributedSystem ds) { DistributedLockService sharedConfigDls = DLockService.getServiceNamed(SHARED_CONFIG_LOCK_SERVICE_NAME); try { if (sharedConfigDls == null) { sharedConfigDls = DLockService.create(SHARED_CONFIG_LOCK_SERVICE_NAME, (InternalDistributedSystem) ds, true, true); } } catch (IllegalArgumentException e) { return DLockService.getServiceNamed(SHARED_CONFIG_LOCK_SERVICE_NAME); } return sharedConfigDls; } /** * Adds/replaces the xml entity in the shared configuration we don't need to trigger the change * listener for this modification, so it's ok to operate on the original configuration object */ public void addXmlEntity(XmlEntity xmlEntity, String[] groups) { lockSharedConfiguration(); try { Region<String, Configuration> configRegion = getConfigurationRegion(); if (groups == null || groups.length == 0) { groups = new String[] {ClusterConfigurationService.CLUSTER_CONFIG}; } for (String group : groups) { Configuration configuration = (Configuration) configRegion.get(group); if (configuration == null) { configuration = new Configuration(group); } String xmlContent = configuration.getCacheXmlContent(); if (xmlContent == null || xmlContent.isEmpty()) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); CacheXmlGenerator.generateDefault(pw); xmlContent = sw.toString(); } try { final Document doc = XmlUtils.createAndUpgradeDocumentFromXml(xmlContent); XmlUtils.addNewNode(doc, xmlEntity); configuration.setCacheXmlContent(XmlUtils.prettyXml(doc)); configRegion.put(group, configuration); } catch (Exception e) { logger.error("error updating cluster configuration for group " + group, e); } } } finally { unlockSharedConfiguration(); } } /** * Deletes the xml entity from the shared configuration. */ public void deleteXmlEntity(final XmlEntity xmlEntity, String[] groups) { lockSharedConfiguration(); try { Region<String, Configuration> configRegion = getConfigurationRegion(); // No group is specified, so delete in every single group if it exists. if (groups == null) { Set<String> groupSet = configRegion.keySet(); groups = groupSet.toArray(new String[groupSet.size()]); } for (String group : groups) { Configuration configuration = (Configuration) configRegion.get(group); if (configuration != null) { String xmlContent = configuration.getCacheXmlContent(); try { if (xmlContent != null && !xmlContent.isEmpty()) { Document doc = XmlUtils.createAndUpgradeDocumentFromXml(xmlContent); XmlUtils.deleteNode(doc, xmlEntity); configuration.setCacheXmlContent(XmlUtils.prettyXml(doc)); configRegion.put(group, configuration); } } catch (Exception e) { logger.error("error updating cluster configuration for group " + group, e); } } } } finally { unlockSharedConfiguration(); } } // we don't need to trigger the change listener for this modification, so it's ok to // operate on the original configuration object public void modifyXmlAndProperties(Properties properties, XmlEntity xmlEntity, String[] groups) { lockSharedConfiguration(); try { if (groups == null) { groups = new String[] {ClusterConfigurationService.CLUSTER_CONFIG}; } Region<String, Configuration> configRegion = getConfigurationRegion(); for (String group : groups) { Configuration configuration = configRegion.get(group); if (configuration == null) { configuration = new Configuration(group); } if (xmlEntity != null) { String xmlContent = configuration.getCacheXmlContent(); if (xmlContent == null || xmlContent.isEmpty()) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); CacheXmlGenerator.generateDefault(pw); xmlContent = sw.toString(); } try { Document doc = XmlUtils.createAndUpgradeDocumentFromXml(xmlContent); // Modify the cache attributes XmlUtils.modifyRootAttributes(doc, xmlEntity); // Change the xml content of the configuration and put it the config region configuration.setCacheXmlContent(XmlUtils.prettyXml(doc)); } catch (Exception e) { logger.error("error updating cluster configuration for group " + group, e); } } if (properties != null) { configuration.getGemfireProperties().putAll(properties); } configRegion.put(group, configuration); } } finally { unlockSharedConfiguration(); } } /** * Add jar information into the shared configuration and save the jars in the file system used * when deploying jars * * @return true on success */ public boolean addJarsToThisLocator(String[] jarNames, byte[][] jarBytes, String[] groups) { boolean success = true; lockSharedConfiguration(); try { if (groups == null) { groups = new String[] {ClusterConfigurationService.CLUSTER_CONFIG}; } Region<String, Configuration> configRegion = getConfigurationRegion(); for (String group : groups) { Configuration configuration = configRegion.get(group); if (configuration == null) { configuration = new Configuration(group); createConfigDirIfNecessary(group); } String groupDir = FilenameUtils.concat(configDirPath, group); for (int i = 0; i < jarNames.length; i++) { String filePath = FilenameUtils.concat(groupDir, jarNames[i]); File jarFile = new File(filePath); try { FileUtils.writeByteArrayToFile(jarFile, jarBytes[i]); } catch (IOException e) { logger.info(e); } } // update the record after writing the jars to the file system, since the listener // will need the jars on file to upload to other locators. Need to update the jars // using a new copy of the Configuration so that the change listener will pick up the jar // name changes. Configuration configurationCopy = new Configuration(configuration); configurationCopy.addJarNames(jarNames); configRegion.put(group, configurationCopy); } } catch (Exception e) { success = false; logger.info(e.getMessage(), e); } finally { unlockSharedConfiguration(); } return success; } /** * Removes the jar files from the shared configuration. used when undeploy jars * * @param jarNames Names of the jar files. * @param groups Names of the groups which had the jar file deployed. * @return true on success. */ public boolean removeJars(final String[] jarNames, String[] groups) { boolean success = true; lockSharedConfiguration(); try { Region<String, Configuration> configRegion = getConfigurationRegion(); if (groups == null) { groups = configRegion.keySet().stream().toArray(String[]::new); } for (String group : groups) { Configuration configuration = configRegion.get(group); if (configuration == null) { break; } Configuration configurationCopy = new Configuration(configuration); configurationCopy.removeJarNames(jarNames); configRegion.put(group, configurationCopy); } } catch (Exception e) { logger.info("Exception occurred while deleting the jar files", e); success = false; } finally { unlockSharedConfiguration(); } return success; } /** * read the jar bytes in the file system */ // used when creating cluster config response // and used when uploading the jars to another locator public byte[] getJarBytesFromThisLocator(String group, String jarName) throws Exception { Configuration configuration = getConfiguration(group); File jar = getPathToJarOnThisLocator(group, jarName).toFile(); if (configuration == null || !configuration.getJarNames().contains(jarName) || !jar.exists()) { return null; } return FileUtils.readFileToByteArray(jar); } // used in the cluster config change listener when jarnames are changed in the internal region public void downloadJarFromOtherLocators(String groupName, String jarName) throws Exception { logger.info("Getting Jar files from other locators"); DM dm = cache.getDistributionManager(); DistributedMember me = cache.getMyId(); Set<DistributedMember> locators = new HashSet<>(dm.getAllHostedLocatorsWithSharedConfiguration().keySet()); locators.remove(me); createConfigDirIfNecessary(groupName); byte[] jarBytes = locators.stream() .map((DistributedMember locator) -> downloadJarFromLocator(locator, groupName, jarName)) .filter(Objects::nonNull).findFirst().orElseThrow(() -> new IllegalStateException( "No locators have a deployed jar named " + jarName + " in " + groupName)); File jarToWrite = getPathToJarOnThisLocator(groupName, jarName).toFile(); FileUtils.writeByteArrayToFile(jarToWrite, jarBytes); } // used when creating cluster config response public Map<String, byte[]> getAllJarsFromThisLocator(Set<String> groups) throws Exception { Map<String, byte[]> jarNamesToJarBytes = new HashMap<>(); for (String group : groups) { Configuration groupConfig = getConfiguration(group); if (groupConfig == null) { break; } Set<String> jars = groupConfig.getJarNames(); for (String jar : jars) { byte[] jarBytes = getJarBytesFromThisLocator(group, jar); jarNamesToJarBytes.put(jar, jarBytes); } } return jarNamesToJarBytes; } /** * Creates the shared configuration service * * @param loadSharedConfigFromDir when set to true, loads the configuration from the share_config * directory */ public void initSharedConfiguration(boolean loadSharedConfigFromDir) throws Exception { status.set(SharedConfigurationStatus.STARTED); Region<String, Configuration> configRegion = this.getConfigurationRegion(); lockSharedConfiguration(); try { if (loadSharedConfigFromDir) { logger.info("Reading cluster configuration from '{}' directory", ClusterConfigurationService.CLUSTER_CONFIG_ARTIFACTS_DIR_NAME); loadSharedConfigurationFromDisk(); } else { persistSecuritySettings(configRegion); // for those groups that have jar files, need to download the jars from other locators // if it doesn't exist yet Set<String> groups = configRegion.keySet(); for (String group : groups) { Configuration config = configRegion.get(group); for (String jar : config.getJarNames()) { if (!(getPathToJarOnThisLocator(group, jar).toFile()).exists()) { downloadJarFromOtherLocators(group, jar); } } } } } finally { unlockSharedConfiguration(); } status.set(SharedConfigurationStatus.RUNNING); } private void persistSecuritySettings(final Region<String, Configuration> configRegion) { Properties securityProps = cache.getDistributedSystem().getSecurityProperties(); Configuration clusterPropertiesConfig = configRegion.get(ClusterConfigurationService.CLUSTER_CONFIG); if (clusterPropertiesConfig == null) { clusterPropertiesConfig = new Configuration(ClusterConfigurationService.CLUSTER_CONFIG); configRegion.put(ClusterConfigurationService.CLUSTER_CONFIG, clusterPropertiesConfig); } // put security-manager and security-post-processor in the cluster config Properties clusterProperties = clusterPropertiesConfig.getGemfireProperties(); if (securityProps.containsKey(SECURITY_MANAGER)) { clusterProperties.setProperty(SECURITY_MANAGER, securityProps.getProperty(SECURITY_MANAGER)); } if (securityProps.containsKey(SECURITY_POST_PROCESSOR)) { clusterProperties.setProperty(SECURITY_POST_PROCESSOR, securityProps.getProperty(SECURITY_POST_PROCESSOR)); } } /** * Creates a ConfigurationResponse based on the configRequest, configuration response contains the * requested shared configuration This method locks the ClusterConfigurationService */ public ConfigurationResponse createConfigurationReponse(final ConfigurationRequest configRequest) throws Exception { ConfigurationResponse configResponse = new ConfigurationResponse(); for (int i = 0; i < configRequest.getNumAttempts(); i++) { boolean isLocked = sharedConfigLockingService.lock(SHARED_CONFIG_LOCK_NAME, 5000, 5000); try { if (isLocked) { Set<String> groups = configRequest.getGroups(); groups.add(ClusterConfigurationService.CLUSTER_CONFIG); logger.info("Building up configuration response with following configurations: {}", groups); for (String group : groups) { Configuration configuration = getConfiguration(group); configResponse.addConfiguration(configuration); } Map<String, byte[]> jarNamesToJarBytes = getAllJarsFromThisLocator(groups); String[] jarNames = jarNamesToJarBytes.keySet().stream().toArray(String[]::new); byte[][] jarBytes = jarNamesToJarBytes.values().toArray(new byte[jarNames.length][]); configResponse.addJarsToBeDeployed(jarNames, jarBytes); configResponse.setFailedToGetSharedConfig(false); return configResponse; } } finally { sharedConfigLockingService.unlock(SHARED_CONFIG_LOCK_NAME); } } configResponse.setFailedToGetSharedConfig(true); return configResponse; } /** * Create a response containing the status of the Shared configuration and information about other * locators containing newer shared configuration data (if at all) * * @return {@link SharedConfigurationStatusResponse} containing the * {@link SharedConfigurationStatus} */ public SharedConfigurationStatusResponse createStatusResponse() { SharedConfigurationStatusResponse response = new SharedConfigurationStatusResponse(); response.setStatus(getStatus()); response.addWaitingLocatorInfo(newerSharedConfigurationLocatorInfo); return response; } /** * For tests only. TODO: clean this up and remove from production code * <p/> * Throws {@code AssertionError} wrapping any exception thrown by operation. */ public void destroySharedConfiguration() { try { Region<String, Configuration> configRegion = getConfigurationRegion(); if (configRegion != null) { configRegion.destroyRegion(); } DiskStore configDiskStore = this.cache.findDiskStore(CLUSTER_CONFIG_ARTIFACTS_DIR_NAME); if (configDiskStore != null) { configDiskStore.destroy(); File file = new File(configDiskDirPath); FileUtils.deleteDirectory(file); } FileUtils.deleteDirectory(new File(configDirPath)); } catch (Exception exception) { throw new AssertionError(exception); } } public Path getPathToJarOnThisLocator(String groupName, String jarName) { return new File(configDirPath).toPath().resolve(groupName).resolve(jarName); } public Configuration getConfiguration(String groupName) { Configuration configuration = getConfigurationRegion().get(groupName); return configuration; } public Map<String, Configuration> getEntireConfiguration() throws Exception { Set<String> keys = getConfigurationRegion().keySet(); return getConfigurationRegion().getAll(keys); } /** * Returns the path of Shared configuration directory * * @return {@link String} path of the shared configuration directory */ public String getSharedConfigurationDirPath() { return configDirPath; } /** * Gets the current status of the ClusterConfigurationService If the status is started , it * determines if the shared configuration is waiting for new configuration on other locators * * @return {@link SharedConfigurationStatus} */ public SharedConfigurationStatus getStatus() { SharedConfigurationStatus scStatus = this.status.get(); if (scStatus == SharedConfigurationStatus.STARTED) { PersistentMemberManager pmm = cache.getPersistentMemberManager(); Map<String, Set<PersistentMemberID>> waitingRegions = pmm.getWaitingRegions(); if (!waitingRegions.isEmpty()) { this.status.compareAndSet(SharedConfigurationStatus.STARTED, SharedConfigurationStatus.WAITING); Set<PersistentMemberID> persMemIds = waitingRegions.get(Region.SEPARATOR_CHAR + CONFIG_REGION_NAME); for (PersistentMemberID persMemId : persMemIds) { newerSharedConfigurationLocatorInfo.add(new PersistentMemberPattern(persMemId)); } } } return this.status.get(); } /** * Loads the internal region with the configuration in the configDirPath */ public void loadSharedConfigurationFromDisk() throws Exception { lockSharedConfiguration(); File[] groupNames = new File(configDirPath).listFiles((FileFilter) DirectoryFileFilter.INSTANCE); Map<String, Configuration> sharedConfiguration = new HashMap<String, Configuration>(); try { for (File groupName : groupNames) { Configuration configuration = readConfiguration(groupName); sharedConfiguration.put(groupName.getName(), configuration); } Region clusterRegion = getConfigurationRegion(); clusterRegion.clear(); clusterRegion.putAll(sharedConfiguration); // Overwrite the security settings using the locator's properties, ignoring whatever // in the import persistSecuritySettings(clusterRegion); } finally { unlockSharedConfiguration(); } } public void renameExistingSharedConfigDirectory() { File configDirFile = new File(configDirPath); if (configDirFile.exists()) { String configDirFileName2 = CLUSTER_CONFIG_ARTIFACTS_DIR_NAME + new SimpleDateFormat("yyyyMMddhhmm").format(new Date()) + "." + System.nanoTime(); File configDirFile2 = new File(configDirFile.getParent(), configDirFileName2); try { FileUtils.moveDirectory(configDirFile, configDirFile2); } catch (IOException e) { logger.info(e); } } } // Write the content of xml and properties into the file system for exporting purpose public void writeConfigToFile(final Configuration configuration) throws Exception { File configDir = createConfigDirIfNecessary(configuration.getConfigName()); File propsFile = new File(configDir, configuration.getPropertiesFileName()); BufferedWriter bw = new BufferedWriter(new FileWriter(propsFile)); configuration.getGemfireProperties().store(bw, null); bw.close(); File xmlFile = new File(configDir, configuration.getCacheXmlFileName()); FileUtils.writeStringToFile(xmlFile, configuration.getCacheXmlContent(), "UTF-8"); } private boolean lockSharedConfiguration() { return sharedConfigLockingService.lock(SHARED_CONFIG_LOCK_NAME, -1, -1); } private void unlockSharedConfiguration() { sharedConfigLockingService.unlock(SHARED_CONFIG_LOCK_NAME); } private byte[] downloadJarFromLocator(DistributedMember locator, String groupName, String jarName) { ResultCollector<byte[], List<byte[]>> rc = (ResultCollector<byte[], List<byte[]>>) CliUtil .executeFunction(new UploadJarFunction(), new Object[] {groupName, jarName}, locator); List<byte[]> result = rc.getResult(); // we should only get one byte[] back in the list return result.stream().filter(Objects::nonNull).findFirst().orElse(null); } /** * Gets the region containing the shared configuration data. The region is created , if it does * not exist already. Note : this could block if this locator contains stale persistent * configuration data. * * @return {@link Region} ConfigurationRegion, this should never be null */ private Region<String, Configuration> getConfigurationRegion() { Region<String, Configuration> configRegion = cache.getRegion(CONFIG_REGION_NAME); try { if (configRegion == null) { File diskDir = new File(configDiskDirPath); if (!diskDir.exists()) { if (!diskDir.mkdirs()) { throw new IOException("Cannot create directory at " + configDiskDirPath); } } File[] diskDirs = {diskDir}; cache.createDiskStoreFactory().setDiskDirs(diskDirs).setAutoCompact(true) .setMaxOplogSize(10).create(CLUSTER_CONFIG_DISK_STORE_NAME); AttributesFactory<String, Configuration> regionAttrsFactory = new AttributesFactory<String, Configuration>(); regionAttrsFactory.setDataPolicy(DataPolicy.PERSISTENT_REPLICATE); regionAttrsFactory.setCacheListener(new ConfigurationChangeListener(this)); regionAttrsFactory.setDiskStoreName(CLUSTER_CONFIG_DISK_STORE_NAME); regionAttrsFactory.setScope(Scope.DISTRIBUTED_ACK); InternalRegionArguments internalArgs = new InternalRegionArguments(); internalArgs.setIsUsedForMetaRegion(true); internalArgs.setMetaRegionWithTransactions(false); configRegion = cache.createVMRegion(CONFIG_REGION_NAME, regionAttrsFactory.create(), internalArgs); } } catch (CancelException e) { if (configRegion == null) { this.status.set(SharedConfigurationStatus.STOPPED); } throw e; // CONFIG: don't rethrow as Exception, keep it a subclass of CancelException } catch (Exception e) { if (configRegion == null) { this.status.set(SharedConfigurationStatus.STOPPED); } throw new RuntimeException("Error occurred while initializing cluster configuration", e); } return configRegion; } /** * Reads the configuration information from the shared configuration directory and returns a * {@link Configuration} object * * @return {@link Configuration} */ private Configuration readConfiguration(File groupConfigDir) throws SAXException, ParserConfigurationException, TransformerFactoryConfigurationError, TransformerException, IOException { Configuration configuration = new Configuration(groupConfigDir.getName()); File cacheXmlFull = new File(groupConfigDir, configuration.getCacheXmlFileName()); File propertiesFull = new File(groupConfigDir, configuration.getPropertiesFileName()); configuration.setCacheXmlFile(cacheXmlFull); configuration.setPropertiesFile(propertiesFull); Set<String> jarFileNames = Arrays.stream(groupConfigDir.list()) .filter((String filename) -> filename.endsWith(".jar")).collect(Collectors.toSet()); configuration.addJarNames(jarFileNames); return configuration; } /** * Creates a directory for this configuration if it doesn't already exist. */ private File createConfigDirIfNecessary(final String configName) throws Exception { File clusterConfigDir = new File(getSharedConfigurationDirPath()); if (!clusterConfigDir.exists()) { if (!clusterConfigDir.mkdirs()) { throw new IOException("Cannot create directory : " + getSharedConfigurationDirPath()); } } Path configDirPath = clusterConfigDir.toPath().resolve(configName); File configDir = configDirPath.toFile(); if (!configDir.exists()) { if (!configDir.mkdir()) { throw new IOException("Cannot create directory : " + configDirPath); } } return configDir; } // check if it's ok from populate the properties from one member to another public static boolean isMisConfigured(Properties fromProps, Properties toProps, String key) { String fromPropValue = fromProps.getProperty(key); String toPropValue = toProps.getProperty(key); // if this to prop is not specified, this is always OK. if (org.apache.commons.lang.StringUtils.isBlank(toPropValue)) return false; // to props is not blank, but from props is blank, NOT OK. if (org.apache.commons.lang.StringUtils.isBlank(fromPropValue)) return true; // at this point check for eqality return !fromPropValue.equals(toPropValue); } }