http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/b6c305f8/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/BackupDataStoreHelper.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/BackupDataStoreHelper.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/BackupDataStoreHelper.java new file mode 100644 index 0000000..bf2d484 --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/BackupDataStoreHelper.java @@ -0,0 +1,74 @@ +/* + * 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.internal.admin.api.impl; + +import java.io.File; +import java.util.Map; +import java.util.Set; + +import org.apache.geode.cache.persistence.PersistentID; +import org.apache.geode.distributed.DistributedLockService; +import org.apache.geode.distributed.DistributedMember; +import org.apache.geode.distributed.internal.DM; +import org.apache.geode.internal.Assert; + +public class BackupDataStoreHelper { + + public static String LOCK_SERVICE_NAME = BackupDataStoreHelper.class.getSimpleName(); + + private static String LOCK_NAME = LOCK_SERVICE_NAME + "_token"; + + private static Object LOCK_SYNC = new Object(); + + @SuppressWarnings("rawtypes") + public static BackupDataStoreResult backupAllMembers(DM dm, Set recipients, File targetDir, + File baselineDir) { + FlushToDiskRequest.send(dm, recipients); + + boolean abort = true; + Map<DistributedMember, Set<PersistentID>> successfulMembers; + Map<DistributedMember, Set<PersistentID>> existingDataStores; + try { + existingDataStores = PrepareBackupRequest.send(dm, recipients); + abort = false; + } finally { + successfulMembers = FinishBackupRequest.send(dm, recipients, targetDir, baselineDir, abort); + } + return new BackupDataStoreResult(existingDataStores, successfulMembers); + } + + private static DistributedLockService getLockService(DM dm) { + DistributedLockService dls = DistributedLockService.getServiceNamed(LOCK_SERVICE_NAME); + if (dls == null) { + synchronized (LOCK_SYNC) { + dls = DistributedLockService.getServiceNamed(LOCK_SERVICE_NAME); + if (dls == null) { + // Create the DistributedLockService + dls = DistributedLockService.create(LOCK_SERVICE_NAME, dm.getSystem()); + } + } + } + Assert.assertTrue(dls != null); + return dls; + } + + public static boolean obtainLock(DM dm) { + return getLockService(dm).lock(LOCK_NAME, 0, -1); + } + + public static void releaseLock(DM dm) { + getLockService(dm).unlock(LOCK_NAME); + } +}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/b6c305f8/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/BackupDataStoreResult.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/BackupDataStoreResult.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/BackupDataStoreResult.java new file mode 100644 index 0000000..b61e5c8 --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/BackupDataStoreResult.java @@ -0,0 +1,48 @@ +/* + * 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.internal.admin.api.impl; + +import java.util.Map; +import java.util.Set; + +import org.apache.geode.cache.persistence.PersistentID; +import org.apache.geode.distributed.DistributedMember; + +public class BackupDataStoreResult { + + private Map<DistributedMember, Set<PersistentID>> existingDataStores; + + private Map<DistributedMember, Set<PersistentID>> successfulMembers; + + public BackupDataStoreResult(Map<DistributedMember, Set<PersistentID>> existingDataStores, + Map<DistributedMember, Set<PersistentID>> successfulMembers) { + this.existingDataStores = existingDataStores; + this.successfulMembers = successfulMembers; + } + + public Map<DistributedMember, Set<PersistentID>> getExistingDataStores() { + return this.existingDataStores; + } + + public Map<DistributedMember, Set<PersistentID>> getSuccessfulMembers() { + return this.successfulMembers; + } + + public String toString() { + return new StringBuilder().append(getClass().getSimpleName()).append("[") + .append("existingDataStores=").append(this.existingDataStores) + .append("; successfulMembers=").append(this.successfulMembers).append("]").toString(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/b6c305f8/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/BackupStatusImpl.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/BackupStatusImpl.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/BackupStatusImpl.java new file mode 100644 index 0000000..78736f7 --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/BackupStatusImpl.java @@ -0,0 +1,59 @@ +/* + * 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.internal.admin.api.impl; + +import java.io.Serializable; +import java.util.Map; +import java.util.Set; + +import org.apache.geode.internal.admin.api.BackupStatus; +import org.apache.geode.cache.persistence.PersistentID; +import org.apache.geode.distributed.DistributedMember; + +/** + * Holds the result of a backup operation. + * + * + */ +public class BackupStatusImpl implements BackupStatus, Serializable { + private static final long serialVersionUID = 3704162840296921840L; + + private Map<DistributedMember, Set<PersistentID>> backedUpDiskStores; + private Set<PersistentID> offlineDiskStores; + + public BackupStatusImpl(Map<DistributedMember, Set<PersistentID>> backedUpDiskStores, + Set<PersistentID> offlineDiskStores) { + super(); + this.backedUpDiskStores = backedUpDiskStores; + this.offlineDiskStores = offlineDiskStores; + } + + public Map<DistributedMember, Set<PersistentID>> getBackedUpDiskStores() { + return backedUpDiskStores; + } + + public Set<PersistentID> getOfflineDiskStores() { + return offlineDiskStores; + } + + @Override + public String toString() { + return "BackupStatus[backedUpDiskStores=" + backedUpDiskStores + ", offlineDiskStores=" + + offlineDiskStores + "]"; + } + + + +} http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/b6c305f8/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/CacheHealthConfigImpl.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/CacheHealthConfigImpl.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/CacheHealthConfigImpl.java new file mode 100644 index 0000000..a6c2257 --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/CacheHealthConfigImpl.java @@ -0,0 +1,91 @@ +/* + * 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.internal.admin.api.impl; + +import org.apache.geode.internal.admin.api.CacheHealthConfig; + +/** + * The implementation of <code>CacheHealthConfig</code> + * + * + * @since GemFire 3.5 + */ +public abstract class CacheHealthConfigImpl extends MemberHealthConfigImpl + implements CacheHealthConfig { + + /** + * The maximum number of milliseconds a <code>netSearch</code> operation can take before the cache + * member is considered to be unhealthy. + */ + private long maxNetSearchTime = DEFAULT_MAX_NET_SEARCH_TIME; + + /** + * The maximum mumber of milliseconds a cache <code>load</code> operation can take before the + * cache member is considered to be unhealthy. + */ + private long maxLoadTime = DEFAULT_MAX_LOAD_TIME; + + /** The minimum hit ratio of a healthy cache member. */ + private double minHitRatio = DEFAULT_MIN_HIT_RATIO; + + /** + * The maximum number of entries in the event delivery queue of a healthy cache member. + */ + private long maxEventQueueSize = DEFAULT_MAX_EVENT_QUEUE_SIZE; + + /////////////////////// Constructors /////////////////////// + + /** + * Creates a new <code>CacheHealthConfigImpl</code> with the default configuration. + */ + CacheHealthConfigImpl() { + + } + + ////////////////////// Instance Methods ///////////////////// + + public long getMaxNetSearchTime() { + return this.maxNetSearchTime; + } + + public void setMaxNetSearchTime(long maxNetSearchTime) { + this.maxNetSearchTime = maxNetSearchTime; + } + + public long getMaxLoadTime() { + return this.maxLoadTime; + } + + public void setMaxLoadTime(long maxLoadTime) { + this.maxLoadTime = maxLoadTime; + } + + public double getMinHitRatio() { + return this.minHitRatio; + } + + public void setMinHitRatio(double minHitRatio) { + this.minHitRatio = minHitRatio; + } + + public long getMaxEventQueueSize() { + return this.maxEventQueueSize; + } + + public void setMaxEventQueueSize(long maxEventQueueSize) { + this.maxEventQueueSize = maxEventQueueSize; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/b6c305f8/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/CacheHealthEvaluator.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/CacheHealthEvaluator.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/CacheHealthEvaluator.java new file mode 100644 index 0000000..8e30518 --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/CacheHealthEvaluator.java @@ -0,0 +1,306 @@ +/* + * 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.internal.admin.api.impl; + +import java.util.List; + +import org.apache.logging.log4j.Logger; + +import org.apache.geode.CancelException; +import org.apache.geode.internal.admin.api.CacheHealthConfig; +import org.apache.geode.internal.admin.api.GemFireHealthConfig; +import org.apache.geode.cache.CacheFactory; +import org.apache.geode.distributed.internal.DM; +import org.apache.geode.distributed.internal.InternalDistributedSystem; +import org.apache.geode.internal.OSProcess; +import org.apache.geode.internal.cache.CacheLifecycleListener; +import org.apache.geode.internal.cache.CachePerfStats; +import org.apache.geode.internal.cache.GemFireCacheImpl; +import org.apache.geode.internal.i18n.LocalizedStrings; +import org.apache.geode.internal.logging.LogService; + +/** + * Contains the logic for evaluating the health of a GemFire <code>Cache</code> instance according + * to the thresholds provided in a {@link CacheHealthConfig}. + * + * + * @since GemFire 3.5 + */ +class CacheHealthEvaluator extends AbstractHealthEvaluator implements CacheLifecycleListener { + + private static final Logger logger = LogService.getLogger(); + + /** The config from which we get the evaulation criteria */ + private CacheHealthConfig config; + + /** The description of the cache being evaluated */ + private String description; + + /** + * Statistics about the <code>Cache</code> instance. If no cache has been created in this VM, this + * field will be <code>null</code> + */ + private CachePerfStats cacheStats; + + /** The previous value of the netsearchTime stat (in nanoseconds) */ + private long prevNetsearchTime; + + /** The previous value of the netsearchedCompleted stat */ + private long prevNetsearchesCompleted; + + /** The previous value of the loadTime stat (in nanoseconds) */ + private long prevLoadTime; + + /** The previous value of the loadedCompleted stat */ + private long prevLoadsCompleted; + + /** The previous value of the gets stat */ + private long prevGets; + + ////////////////////// Constructors ////////////////////// + + /** + * Creates a new <code>CacheHealthEvaluator</code> + */ + CacheHealthEvaluator(GemFireHealthConfig config, DM dm) { + super(config, dm); + + this.config = config; + InternalDistributedSystem system = dm.getSystem(); + GemFireCacheImpl cache; + try { + cache = (GemFireCacheImpl) CacheFactory.getInstance(system); + + } catch (CancelException ex) { + // No cache in this VM + cache = null; + } + + initialize(cache, dm); + GemFireCacheImpl.addCacheLifecycleListener(this); + } + + //////////////////// Instance Methods //////////////////// + + @Override + protected String getDescription() { + return this.description; + } + + /** + * Initializes the state of this evaluator based on the given cache instance. + */ + private void initialize(GemFireCacheImpl cache, DM dm) { + StringBuffer sb = new StringBuffer(); + if (cache != null) { + this.cacheStats = cache.getCachePerfStats(); + + sb.append("Cache \""); + sb.append(cache.getName()); + sb.append("\""); + + } else { + sb.append("No Cache"); + } + + sb.append(" in member "); + sb.append(dm.getId()); + int pid = OSProcess.getId(); + if (pid != 0) { + sb.append(" with pid "); + sb.append(pid); + } + this.description = sb.toString(); + } + + public void cacheCreated(GemFireCacheImpl cache) { + InternalDistributedSystem system = (InternalDistributedSystem) cache.getDistributedSystem(); + DM dm = system.getDistributionManager(); + initialize(cache, dm); + } + + /** + * Checks to make sure that the average <code>netSearch</code> time during the previous health + * check interval is less than the {@linkplain CacheHealthConfig#getMaxNetSearchTime threshold}. + * If not, the status is "okay" health. + * + * @see CachePerfStats#getNetsearchTime + * @see CachePerfStats#getNetsearchesCompleted + */ + void checkNetSearchTime(List status) { + if (this.cacheStats == null || isFirstEvaluation() || this.cacheStats.isClosed()) { + return; + } + + long deltaNetsearchTime = this.cacheStats.getNetsearchTime() - this.prevNetsearchTime; + long deltaNetsearchesCompleted = + this.cacheStats.getNetsearchesCompleted() - this.prevNetsearchesCompleted; + + if (deltaNetsearchesCompleted != 0) { + long ratio = deltaNetsearchTime / deltaNetsearchesCompleted; + ratio /= 1000000; + long threshold = this.config.getMaxNetSearchTime(); + + if (ratio > threshold) { + String s = + LocalizedStrings.CacheHealthEvaluator_THE_AVERAGE_DURATION_OF_A_CACHE_NETSEARCH_0_MS_EXCEEDS_THE_THRESHOLD_1_MS + .toLocalizedString(new Object[] {ratio, threshold}); + status.add(okayHealth(s)); + } + } + } + + /** + * Checks to make sure that the average <code>load</code> time during the previous health check + * interval is less than the {@linkplain CacheHealthConfig#getMaxLoadTime threshold}. If not, the + * status is "okay" health. + * + * @see CachePerfStats#getLoadTime + * @see CachePerfStats#getLoadsCompleted + */ + void checkLoadTime(List status) { + if (this.cacheStats == null || isFirstEvaluation() || this.cacheStats.isClosed()) { + return; + } + + if (!isFirstEvaluation()) { + long deltaLoadTime = this.cacheStats.getLoadTime() - this.prevLoadTime; + long deltaLoadsCompleted = this.cacheStats.getLoadsCompleted() - this.prevLoadsCompleted; + + if (logger.isDebugEnabled()) { + logger.debug("Completed {} loads in {} ms", deltaLoadsCompleted, (deltaLoadTime / 1000000)); + } + + if (deltaLoadsCompleted != 0) { + long ratio = deltaLoadTime / deltaLoadsCompleted; + ratio /= 1000000; + long threshold = this.config.getMaxLoadTime(); + + if (ratio > threshold) { + String s = + LocalizedStrings.CacheHealthEvaluator_THE_AVERAGE_DURATION_OF_A_CACHE_LOAD_0_MS_EXCEEDS_THE_THRESHOLD_1_MS + .toLocalizedString(new Object[] {ratio, threshold}); + if (logger.isDebugEnabled()) { + logger.debug(s); + } + status.add(okayHealth(s)); + } + } + } + } + + /** + * Checks to make sure that the cache hit ratio during the previous health check interval is less + * than the {@linkplain CacheHealthConfig#getMinHitRatio threshold}. If not, the status is "okay" + * health. + * + * <P> + * + * The following formula is used to compute the hit ratio: + * + * <PRE> + * hitRatio = (gets - (loadsCompleted + netsearchesCompleted)) / (gets) + * </PRE> + * + * + * @see CachePerfStats#getGets + * @see CachePerfStats#getLoadsCompleted + * @see CachePerfStats#getNetsearchesCompleted + */ + void checkHitRatio(List status) { + if (this.cacheStats == null || isFirstEvaluation() || this.cacheStats.isClosed()) { + return; + } + + long deltaGets = this.cacheStats.getGets() - this.prevGets; + if (deltaGets != 0) { + long deltaLoadsCompleted = this.cacheStats.getLoadsCompleted() - this.prevLoadsCompleted; + long deltaNetsearchesCompleted = + this.cacheStats.getNetsearchesCompleted() - this.prevNetsearchesCompleted; + + double hits = (deltaGets - (deltaLoadsCompleted + deltaNetsearchesCompleted)); + double hitRatio = hits / deltaGets; + double threshold = this.config.getMinHitRatio(); + if (hitRatio < threshold) { + String s = "The hit ratio of this Cache (" + hitRatio + ") is below the threshold (" + + threshold + ")"; + status.add(okayHealth(s)); + } + } + } + + /** + * Checks to make sure that the {@linkplain CachePerfStats#getEventQueueSize cache event queue + * size} does not exceed the {@linkplain CacheHealthConfig#getMaxEventQueueSize threshold}. If it + * does, the status is "okay" health. + */ + void checkEventQueueSize(List status) { + if (this.cacheStats == null || isFirstEvaluation() || this.cacheStats.isClosed()) { + return; + } + + long eventQueueSize = this.cacheStats.getEventQueueSize(); + long threshold = this.config.getMaxEventQueueSize(); + if (eventQueueSize > threshold) { + String s = + LocalizedStrings.CacheHealthEvaluator_THE_SIZE_OF_THE_CACHE_EVENT_QUEUE_0_MS_EXCEEDS_THE_THRESHOLD_1_MS + .toLocalizedString( + new Object[] {Long.valueOf(eventQueueSize), Long.valueOf(threshold)}); + status.add(okayHealth(s)); + } + } + + + /** + * Updates the previous values of statistics + */ + private void updatePrevious() { + if (this.cacheStats != null && !this.cacheStats.isClosed()) { + this.prevLoadTime = this.cacheStats.getLoadTime(); + this.prevLoadsCompleted = this.cacheStats.getLoadsCompleted(); + this.prevNetsearchTime = this.cacheStats.getNetsearchTime(); + this.prevNetsearchesCompleted = this.cacheStats.getNetsearchesCompleted(); + this.prevGets = this.cacheStats.getGets(); + + } else { + this.prevLoadTime = 0L; + this.prevLoadsCompleted = 0L; + this.prevNetsearchTime = 0L; + this.prevNetsearchesCompleted = 0L; + this.prevGets = 0L; + } + } + + @Override + protected void check(List status) { + + checkNetSearchTime(status); + checkLoadTime(status); + checkHitRatio(status); + checkEventQueueSize(status); + + updatePrevious(); + } + + @Override + public void close() { + GemFireCacheImpl.removeCacheLifecycleListener(this); + } + + @Override + public void cacheClosed(GemFireCacheImpl cache) { + // do nothing + } +} http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/b6c305f8/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/CacheServerConfigImpl.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/CacheServerConfigImpl.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/CacheServerConfigImpl.java new file mode 100644 index 0000000..1eadc79 --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/CacheServerConfigImpl.java @@ -0,0 +1,133 @@ +/* + * 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.internal.admin.api.impl; + +import org.apache.geode.internal.admin.api.CacheServerConfig; +import org.apache.geode.internal.admin.api.CacheVmConfig; +import org.apache.geode.internal.admin.GemFireVM; + +import static org.apache.geode.distributed.ConfigurationProperties.*; + +/** + * An implementation of <code>CacheVmConfig</code> + * + * @since GemFire 4.0 + */ +public class CacheServerConfigImpl extends ManagedEntityConfigImpl + implements CacheVmConfig, CacheServerConfig { + + /** + * Declarative caching XML file that is used to initialize the Cache in the cache server. + */ + private String cacheXMLFile; + + /** Extra classpath for the cache server */ + private String classpath; + + /////////////////////// Constructors /////////////////////// + + /** + * Creates a new <code>CacheServerConfigImpl</code> with the default configuration settings. + */ + public CacheServerConfigImpl() { + this.cacheXMLFile = null; + this.classpath = null; + } + + /** + * Creates a new <code>CacheServerConfigImpl</code> for a running cache server. + */ + public CacheServerConfigImpl(GemFireVM vm) { + super(vm); + + String name = CACHE_XML_FILE; + this.cacheXMLFile = vm.getConfig().getAttribute(name); + this.classpath = null; + } + + /** + * Copy constructor + */ + public CacheServerConfigImpl(CacheServerConfig other) { + super(other); + this.cacheXMLFile = other.getCacheXMLFile(); + this.classpath = other.getClassPath(); + } + + /** + * Copy constructor + */ + public CacheServerConfigImpl(CacheVmConfig other) { + super(other); + this.cacheXMLFile = other.getCacheXMLFile(); + this.classpath = other.getClassPath(); + } + + ////////////////////// Instance Methods ////////////////////// + + public String getCacheXMLFile() { + return this.cacheXMLFile; + } + + public void setCacheXMLFile(String cacheXMLFile) { + checkReadOnly(); + this.cacheXMLFile = cacheXMLFile; + configChanged(); + } + + public String getClassPath() { + return this.classpath; + } + + public void setClassPath(String classpath) { + checkReadOnly(); + this.classpath = classpath; + configChanged(); + } + + @Override + public void validate() { + super.validate(); + + // Nothing to validate really. Cache.xml file could live on + // different file system. + } + + /** + * Currently, listeners are not supported on the locator config. + */ + @Override + protected void configChanged() { + + } + + @Override + public Object clone() throws CloneNotSupportedException { + return new CacheServerConfigImpl((CacheVmConfig) this); + } + + @Override + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append(super.toString()); + sb.append(" cacheXMLFile="); + sb.append(this.getCacheXMLFile()); + sb.append(" classPath="); + sb.append(this.getClassPath()); + + return sb.toString(); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/b6c305f8/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/CacheServerImpl.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/CacheServerImpl.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/CacheServerImpl.java new file mode 100644 index 0000000..f5cb704 --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/CacheServerImpl.java @@ -0,0 +1,196 @@ +/* + * 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.internal.admin.api.impl; + +import org.apache.geode.distributed.internal.DM; +import org.apache.geode.distributed.internal.DistributionManager; +import org.apache.geode.internal.admin.GemFireVM; +import org.apache.geode.internal.admin.api.AdminException; +import org.apache.geode.internal.admin.api.CacheServer; +import org.apache.geode.internal.admin.api.CacheServerConfig; +import org.apache.geode.internal.admin.api.CacheVm; +import org.apache.geode.internal.admin.api.CacheVmConfig; +import org.apache.geode.internal.admin.api.ManagedEntityConfig; +import org.apache.geode.internal.admin.api.SystemMemberType; +import org.apache.geode.internal.admin.remote.RemoteApplicationVM; + +import static org.apache.geode.distributed.ConfigurationProperties.*; + +/** + * Implements the administrative interface to a cache server. + * + * @since GemFire 3.5 + */ +public class CacheServerImpl extends ManagedSystemMemberImpl implements CacheVm, CacheServer { + + /** How many new <code>CacheServer</code>s have been created? */ + private static int newCacheServers = 0; + + /////////////////////// Instance Fields /////////////////////// + + /** The configuration object for this cache server */ + private final CacheServerConfigImpl config; + + ///////////////////////// Constructors //////////////////////// + + /** + * Creates a new <code>CacheServerImpl</code> that represents a non-existsing (unstarted) cache + * server in a given distributed system. + */ + public CacheServerImpl(AdminDistributedSystemImpl system, CacheVmConfig config) + throws AdminException { + + super(system, config); + + this.config = (CacheServerConfigImpl) config; + this.config.setManagedEntity(this); + } + + /** + * Creates a new <code>CacheServerImpl</code> that represents an existing dedicated cache server + * in a given distributed system. + */ + public CacheServerImpl(AdminDistributedSystemImpl system, GemFireVM vm) throws AdminException { + + super(system, vm); + this.config = new CacheServerConfigImpl(vm); + } + + ////////////////////// Instance Methods ////////////////////// + + @Override + public SystemMemberType getType() { + return SystemMemberType.CACHE_VM; + } + + public String getNewId() { + synchronized (CacheServerImpl.class) { + return "CacheVm" + (++newCacheServers); + } + } + + public void start() throws AdminException { + if (!needToStart()) { + return; + } + + this.config.validate(); + this.controller.start(this); + this.config.setManagedEntity(this); + } + + public void stop() { + if (!needToStop()) { + return; + } + + this.controller.stop(this); + // NOTE: DistributedSystem nodeLeft will then set this.manager to null + this.config.setManagedEntity(null); + } + + public boolean isRunning() { + DM dm = ((AdminDistributedSystemImpl) getDistributedSystem()).getDistributionManager(); + if (dm == null) { + try { + return this.controller.isRunning(this); + } catch (IllegalStateException e) { + return false; + } + } + return ((DistributionManager) dm).getDistributionManagerIdsIncludingAdmin() + .contains(getDistributedMember()); + } + + public CacheServerConfig getConfig() { + return this.config; + } + + public CacheVmConfig getVmConfig() { + return this.config; + } + + //////////////////////// Command execution //////////////////////// + + public ManagedEntityConfig getEntityConfig() { + return this.getConfig(); + } + + public String getEntityType() { + // Fix bug 32564 + return "Cache Vm"; + } + + public String getStartCommand() { + StringBuffer sb = new StringBuffer(); + sb.append(this.controller.getProductExecutable(this, "cacheserver")); + sb.append(" start -dir="); + sb.append(this.getConfig().getWorkingDirectory()); + + String file = this.getConfig().getCacheXMLFile(); + if (file != null && file.length() > 0) { + sb.append(" "); + sb.append(CACHE_XML_FILE); + sb.append("="); + sb.append(file); + } + + String classpath = this.getConfig().getClassPath(); + if (classpath != null && classpath.length() > 0) { + sb.append(" -classpath="); + sb.append(classpath); + } + + appendConfiguration(sb); + + return sb.toString().trim(); + } + + public String getStopCommand() { + StringBuffer sb = new StringBuffer(); + sb.append(this.controller.getProductExecutable(this, "cacheserver")); + sb.append(" stop -dir="); + sb.append(this.getConfig().getWorkingDirectory()); + + return sb.toString().trim(); + } + + public String getIsRunningCommand() { + StringBuffer sb = new StringBuffer(); + sb.append(this.controller.getProductExecutable(this, "cacheserver")); + sb.append(" status -dir="); + sb.append(this.getConfig().getWorkingDirectory()); + + return sb.toString().trim(); + } + + /** + * Find whether this server is primary for given client (durableClientId) + * + * @param durableClientId - durable-id of the client + * @return true if the server is primary for given client + * + * @since GemFire 5.6 + */ + public boolean isPrimaryForDurableClient(String durableClientId) { + RemoteApplicationVM vm = (RemoteApplicationVM) this.getGemFireVM(); + boolean isPrimary = false; + if (vm != null) { + isPrimary = vm.isPrimaryForDurableClient(durableClientId); + } + return isPrimary; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/b6c305f8/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/ConfigurationParameterImpl.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/ConfigurationParameterImpl.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/ConfigurationParameterImpl.java new file mode 100755 index 0000000..9d94209 --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/ConfigurationParameterImpl.java @@ -0,0 +1,282 @@ +/* + * 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.internal.admin.api.impl; + +import org.apache.geode.internal.admin.api.ConfigurationParameter; +import org.apache.geode.internal.admin.api.UnmodifiableConfigurationException; +import org.apache.geode.internal.i18n.LocalizedStrings; + +import java.io.File; +// import java.net.InetAddress; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * A single configuration parameter of a system member. + * + * @since GemFire 3.5 + * + */ +public class ConfigurationParameterImpl implements ConfigurationParameter { + + /** Identifying name of this configuration parameter */ + protected String name; + /** Full description of this configuration parameter */ + protected String description; + /** The current value */ + protected Object value; + /** Class type of the value */ + protected Class type; + /** True if this is modifiable; false if read-only */ + protected boolean userModifiable; + /** List of listeners to notify when value changes */ + private final List listeners = new ArrayList(); + + // ------------------------------------------------------------------------- + // Constructor(s) + // ------------------------------------------------------------------------- + + /** + * Constructs new <code>ConfigurationParameterImpl</code>. + * + * @param name the name of this parameter which cannot change + * @param description full description to use + * @param value the value of this parameter + * @param type the class type of the value + * @param userModifiable true if this is modifiable; false if read-only + */ + protected ConfigurationParameterImpl(String name, String description, Object value, Class type, + boolean userModifiable) { + if (name == null || name.length() == 0) { + throw new IllegalArgumentException( + LocalizedStrings.ConfigurationParameterImpl_CONFIGURATIONPARAMETER_NAME_MUST_BE_SPECIFIED + .toLocalizedString()); + } + + this.name = name; + setInternalState(description, value, type, userModifiable); + } + + /** + * Constructs new <code>ConfigurationParameterImpl</code>. + * + * @param name the name of this parameter which cannot change + * @param value the value of this parameter + */ + protected ConfigurationParameterImpl(String name, Object value) { + if (name == null || name.length() == 0) { + throw new IllegalArgumentException( + LocalizedStrings.ConfigurationParameterImpl_CONFIGURATIONPARAMETER_NAME_MUST_BE_SPECIFIED + .toLocalizedString()); + } + + this.name = name; + setInternalState(name, value, value.getClass(), true); + } + + /** Constructor to allow serialization by subclass */ + protected ConfigurationParameterImpl() {} + + // ------------------------------------------------------------------------- + // Attribute accessors and mutators + // ------------------------------------------------------------------------- + + public String getName() { + return this.name; + } + + public String getDescription() { + return this.description; + } + + public Object getValue() { + return this.value; + } + + public String getValueAsString() { + if (isString()) { + return (String) this.value; + } else if (isInetAddress()) { + return InetAddressUtil.toString(this.value); + } else if (isFile()) { + return this.value.toString(); + } else if (isOctal()) { + String strVal = Integer.toOctalString(((Integer) this.value).intValue()); + if (!strVal.startsWith("0")) { + strVal = "0" + strVal; + } + return strVal; + } else if (isArray()) { + List list = Arrays.asList((Object[]) this.value); + return list.toString(); + } else { + return this.value.toString(); + } + } + + public Class getValueType() { + return this.type; + } + + public boolean isModifiable() { + return this.userModifiable; + } + + public boolean isArray() { + return "manager-parameters".equals(this.name) || "manager-classpaths".equals(this.name); + } + + public boolean isInetAddress() { + return java.net.InetAddress.class.isAssignableFrom(this.type); + } + + public boolean isFile() { + return java.io.File.class.equals(this.type); + } + + public boolean isOctal() { + return "shared-memory-permissions".equals(this.name); + } + + public boolean isString() { + return java.lang.String.class.equals(this.type); + } + + public void setValue(Object value) throws UnmodifiableConfigurationException { + if (!isModifiable()) { + throw new UnmodifiableConfigurationException( + LocalizedStrings.ConfigurationParameterImpl_0_IS_NOT_A_MODIFIABLE_CONFIGURATION_PARAMETER + .toLocalizedString(getName())); + } + if (value == null) { + throw new IllegalArgumentException( + LocalizedStrings.ConfigurationParameterImpl_UNABLE_TO_SET_0_TO_NULL_VALUE + .toLocalizedString(getName())); + } + if (!getValueType().equals(value.getClass())) { + throw new IllegalArgumentException( + LocalizedStrings.ConfigurationParameterImpl_UNABLE_TO_SET_TYPE_0_WITH_TYPE_1 + .toLocalizedString( + new Object[] {getValueType().getName(), value.getClass().getName()})); + } + + if (value instanceof String && !isString()) { + // we need to check what the type should be and convert to it... + setValueFromString((String) value); + } else { + this.value = value; + } + fireConfigurationParameterValueChanged(this); + } + + // ------------------------------------------------------------------------- + // Operations for handling the registration of listeners + // Note: this is only for use within impl pkg and subclass pkgs + // ------------------------------------------------------------------------- + + /** Adds the listener for any changes to this configuration parameter. */ + public void addConfigurationParameterListener(ConfigurationParameterListener listener) { + if (!this.listeners.contains(listener)) { + this.listeners.add(listener); + } + } + + /** Removes the listener if it's currently registered. */ + public void removeConfigurationParameterListener(ConfigurationParameterListener listener) { + if (this.listeners.contains(listener)) { + this.listeners.remove(listener); + } + } + + // ------------------------------------------------------------------------- + // Implementation methods + // ------------------------------------------------------------------------- + + protected void setValueFromString(String newValue) { + if (newValue == null) { + throw new IllegalArgumentException( + LocalizedStrings.ConfigurationParameterImpl_UNABLE_TO_SET_0_TO_NULL_VALUE + .toLocalizedString(getName())); + } + + if (isInetAddress()) { + this.value = InetAddressUtil.toInetAddress(newValue); + } else if (isFile()) { + this.value = new File(newValue); + } else if (isOctal()) { + if (!newValue.startsWith("0")) { + newValue = "0" + newValue; + } + this.value = Integer.valueOf(Integer.parseInt(newValue, 8)); + } else if (isArray()) { + // parse it TODO + throw new IllegalArgumentException( + LocalizedStrings.ConfigurationParameterImpl_SETTING_ARRAY_VALUE_FROM_DELIMITED_STRING_IS_NOT_SUPPORTED + .toLocalizedString()); + } else { + this.value = newValue; + } + } + + /** + * Fires changed configuration parameter to registered listeners. + * + * @param parm the configuration parameter the changed + */ + protected void fireConfigurationParameterValueChanged(ConfigurationParameter parm) { + ConfigurationParameterListener[] listeners = (ConfigurationParameterListener[]) this.listeners + .toArray(new ConfigurationParameterListener[0]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].configurationParameterValueChanged(parm); + } + } + + /** + * Sets the internal state of this configuration parameter. + * + * @param description full description to use + * @param value the value of this parameter + * @param type the class type of the value + * @param userModifiable true if this is modifiable; false if read-only + */ + protected void setInternalState(String description, Object value, Class type, + boolean userModifiable) { + if (description == null || description.length() == 0) { + throw new IllegalArgumentException( + LocalizedStrings.ConfigurationParameterImpl_CONFIGURATIONPARAMETER_DESCRIPTION_MUST_BE_SPECIFIED + .toLocalizedString()); + } + this.description = description; + this.type = type; + this.userModifiable = userModifiable; + + if (value == null) { + throw new IllegalArgumentException( + LocalizedStrings.ConfigurationParameterImpl_UNABLE_TO_SET_0_TO_NULL_VALUE + .toLocalizedString(getName())); + } + + this.value = value; + } + + @Override + public String toString() { + return this.name; + } + +} + http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/b6c305f8/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/ConfigurationParameterListener.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/ConfigurationParameterListener.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/ConfigurationParameterListener.java new file mode 100755 index 0000000..0b32df0 --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/ConfigurationParameterListener.java @@ -0,0 +1,30 @@ +/* + * 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.internal.admin.api.impl; + +import org.apache.geode.internal.admin.api.ConfigurationParameter; + +/** + * Listens to value changes of a {@link ConfigurationParameter}. This is for internal use only to + * allow a {@link SystemMemberImpl} to keep track of configuration changes made through + * {@link ConfigurationParameterImpl#setValue}. + * + * @since GemFire 3.5 + * + */ +public interface ConfigurationParameterListener { + public void configurationParameterValueChanged(ConfigurationParameter parm); +} + http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/b6c305f8/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DisabledManagedEntityController.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DisabledManagedEntityController.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DisabledManagedEntityController.java new file mode 100755 index 0000000..aab92af --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DisabledManagedEntityController.java @@ -0,0 +1,94 @@ +/* + * 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.internal.admin.api.impl; + +import org.apache.logging.log4j.Logger; + +import org.apache.geode.internal.admin.api.DistributedSystemConfig; +import org.apache.geode.internal.logging.LogService; +import org.apache.geode.internal.logging.log4j.LogMarker; + +/** + * This is a disabled implementation of ManagedEntityController for bug #47909. + * + * The old ManagedEntityController was a concrete class which has been renamed to + * ManagedEntityControllerImpl. The build.xml now skips building ManagedEntityControllerImpl. If + * ManagedEntityControllerImpl is not found in the classpath then the code uses + * DisabledManagedEntityController as a place holder. + * + */ +class DisabledManagedEntityController implements ManagedEntityController { + + private static final Logger logger = LogService.getLogger(); + + private static final String EXCEPTION_MESSAGE = + "Local and remote OS command invocations are disabled for the Admin API."; + + DisabledManagedEntityController() {} + + @Override + public void start(InternalManagedEntity entity) { + if (logger.isTraceEnabled(LogMarker.MANAGED_ENTITY)) { + logger.warn(LogMarker.MANAGED_ENTITY, "DisabledManagedEntityController#start {}", + EXCEPTION_MESSAGE); + } + throw new UnsupportedOperationException(EXCEPTION_MESSAGE); + } + + @Override + public void stop(InternalManagedEntity entity) { + if (logger.isTraceEnabled(LogMarker.MANAGED_ENTITY)) { + logger.warn(LogMarker.MANAGED_ENTITY, "DisabledManagedEntityController#stop {}", + EXCEPTION_MESSAGE); + } + throw new UnsupportedOperationException(EXCEPTION_MESSAGE); + } + + @Override + public boolean isRunning(InternalManagedEntity entity) { + if (logger.isTraceEnabled(LogMarker.MANAGED_ENTITY)) { + logger.warn(LogMarker.MANAGED_ENTITY, "DisabledManagedEntityController#isRunning {}", + EXCEPTION_MESSAGE); + } + throw new UnsupportedOperationException(EXCEPTION_MESSAGE); + } + + @Override + public String getLog(DistributionLocatorImpl locator) { + if (logger.isTraceEnabled(LogMarker.MANAGED_ENTITY)) { + logger.warn(LogMarker.MANAGED_ENTITY, "DisabledManagedEntityController#getLog {}", + EXCEPTION_MESSAGE); + } + throw new UnsupportedOperationException(EXCEPTION_MESSAGE); + } + + @Override + public String buildSSLArguments(DistributedSystemConfig config) { + if (logger.isTraceEnabled(LogMarker.MANAGED_ENTITY)) { + logger.warn(LogMarker.MANAGED_ENTITY, "DisabledManagedEntityController#buildSSLArguments {}", + EXCEPTION_MESSAGE); + } + throw new UnsupportedOperationException(EXCEPTION_MESSAGE); + } + + @Override + public String getProductExecutable(InternalManagedEntity entity, String executable) { + if (logger.isTraceEnabled(LogMarker.MANAGED_ENTITY)) { + logger.warn(LogMarker.MANAGED_ENTITY, + "DisabledManagedEntityController#getProductExecutable {}", EXCEPTION_MESSAGE); + } + throw new UnsupportedOperationException(EXCEPTION_MESSAGE); + } +} http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/b6c305f8/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributedSystemConfigImpl.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributedSystemConfigImpl.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributedSystemConfigImpl.java new file mode 100755 index 0000000..0298c3e --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/impl/DistributedSystemConfigImpl.java @@ -0,0 +1,1114 @@ +/* + * 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.internal.admin.api.impl; + +import org.apache.geode.distributed.internal.DistributionConfig; +import org.apache.geode.distributed.internal.DistributionConfigImpl; +import org.apache.geode.internal.admin.api.AdminXmlException; +import org.apache.geode.internal.admin.api.CacheServerConfig; +import org.apache.geode.internal.admin.api.CacheVmConfig; +import org.apache.geode.internal.admin.api.DistributedSystemConfig; +import org.apache.geode.internal.admin.api.DistributionLocator; +import org.apache.geode.internal.admin.api.DistributionLocatorConfig; +import org.apache.geode.internal.i18n.LocalizedStrings; +import org.apache.geode.internal.logging.InternalLogWriter; +import org.apache.geode.internal.logging.LogConfig; +import org.apache.geode.internal.logging.LogService; +import org.apache.geode.internal.logging.LogWriterImpl; +import org.apache.logging.log4j.Logger; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.*; + +import static org.apache.geode.distributed.ConfigurationProperties.*; + +/** + * An implementation of the configuration object for an <code>AdminDistributedSystem</code>. After a + * config has been used to create an <code>AdminDistributedSystem</code> most of the configuration + * attributes cannot be changed. However, some operations (such as getting information about GemFire + * managers and distribution locators) are "passed through" to the + * <code>AdminDistributedSystem</code> associated with this configuration object. + * + * @since GemFire 3.5 + */ +public class DistributedSystemConfigImpl implements DistributedSystemConfig { + + private static final Logger logger = LogService.getLogger(); + + private String entityConfigXMLFile = DEFAULT_ENTITY_CONFIG_XML_FILE; + private String systemId = DEFAULT_SYSTEM_ID; + private String mcastAddress = DEFAULT_MCAST_ADDRESS; + private int mcastPort = DEFAULT_MCAST_PORT; + private int ackWaitThreshold = DEFAULT_ACK_WAIT_THRESHOLD; + private int ackSevereAlertThreshold = DEFAULT_ACK_SEVERE_ALERT_THRESHOLD; + private String locators = DEFAULT_LOCATORS; + private String bindAddress = DEFAULT_BIND_ADDRESS; + private String serverBindAddress = DEFAULT_BIND_ADDRESS; + private String remoteCommand = DEFAULT_REMOTE_COMMAND; + private boolean disableTcp = DEFAULT_DISABLE_TCP; + private boolean enableNetworkPartitionDetection = DEFAULT_ENABLE_NETWORK_PARTITION_DETECTION; + private boolean disableAutoReconnect = DEFAULT_DISABLE_AUTO_RECONNECT; + private int memberTimeout = DEFAULT_MEMBER_TIMEOUT; + private String membershipPortRange = getMembershipPortRangeString(DEFAULT_MEMBERSHIP_PORT_RANGE); + private int tcpPort = DEFAULT_TCP_PORT; + + private String logFile = DEFAULT_LOG_FILE; + private String logLevel = DEFAULT_LOG_LEVEL; + private int logDiskSpaceLimit = DEFAULT_LOG_DISK_SPACE_LIMIT; + private int logFileSizeLimit = DEFAULT_LOG_FILE_SIZE_LIMIT; + private int refreshInterval = DEFAULT_REFRESH_INTERVAL; + private Properties gfSecurityProperties = new Properties(); + + /** + * Listeners to notify when this DistributedSystemConfig changes + */ + private Set listeners = new HashSet(); + + /** + * Configs for CacheServers that this system config is aware of + */ + private Set cacheServerConfigs = new HashSet(); + + /** + * Configs for the managed distribution locators in the distributed system + */ + private Set locatorConfigs = new HashSet(); + + /** + * The display name of this distributed system + */ + private String systemName = DEFAULT_NAME; + + /** + * The admin distributed system object that is configured by this config object. + * + * @since GemFire 4.0 + */ + private AdminDistributedSystemImpl system; + + /** + * The GemFire log writer used by the distributed system + */ + private InternalLogWriter logWriter; + + /////////////////////// Static Methods /////////////////////// + + /** + * Filters out all properties that are unique to the admin <code>DistributedSystemConfig</code> + * that are not present in the internal <code>DistributionConfig</code>. + * + * @since GemFire 4.0 + */ + private static Properties filterOutAdminProperties(Properties props) { + + Properties props2 = new Properties(); + for (Enumeration names = props.propertyNames(); names.hasMoreElements();) { + String name = (String) names.nextElement(); + if (!(ENTITY_CONFIG_XML_FILE_NAME.equals(name) || REFRESH_INTERVAL_NAME.equals(name) + || REMOTE_COMMAND_NAME.equals(name))) { + String value = props.getProperty(name); + if ((name != null) && (value != null)) { + props2.setProperty(name, value); + } + } + } + + return props2; + } + + //////////////////////// Constructors //////////////////////// + + /** + * Creates a new <code>DistributedSystemConfigImpl</code> based on the configuration stored in a + * <code>DistributedSystem</code>'s <code>DistributionConfig</code>. + */ + public DistributedSystemConfigImpl(DistributionConfig distConfig, String remoteCommand) { + if (distConfig == null) { + throw new IllegalArgumentException( + LocalizedStrings.DistributedSystemConfigImpl_DISTRIBUTIONCONFIG_MUST_NOT_BE_NULL + .toLocalizedString()); + } + + this.mcastAddress = InetAddressUtil.toString(distConfig.getMcastAddress()); + this.mcastPort = distConfig.getMcastPort(); + this.locators = distConfig.getLocators(); + this.membershipPortRange = getMembershipPortRangeString(distConfig.getMembershipPortRange()); + + this.systemName = distConfig.getName(); + + this.sslEnabled = distConfig.getClusterSSLEnabled(); + this.sslCiphers = distConfig.getClusterSSLCiphers(); + this.sslProtocols = distConfig.getClusterSSLProtocols(); + this.sslAuthenticationRequired = distConfig.getClusterSSLRequireAuthentication(); + + this.logFile = distConfig.getLogFile().getPath(); + this.logLevel = LogWriterImpl.levelToString(distConfig.getLogLevel()); + this.logDiskSpaceLimit = distConfig.getLogDiskSpaceLimit(); + this.logFileSizeLimit = distConfig.getLogFileSizeLimit(); + + basicSetBindAddress(distConfig.getBindAddress()); + this.tcpPort = distConfig.getTcpPort(); + + this.disableTcp = distConfig.getDisableTcp(); + + this.remoteCommand = remoteCommand; + this.serverBindAddress = distConfig.getServerBindAddress(); + this.enableNetworkPartitionDetection = distConfig.getEnableNetworkPartitionDetection(); + this.memberTimeout = distConfig.getMemberTimeout(); + this.refreshInterval = DistributedSystemConfig.DEFAULT_REFRESH_INTERVAL; + this.gfSecurityProperties = (Properties) distConfig.getSSLProperties().clone(); + } + + /** + * Zero-argument constructor to be used only by subclasses. + * + * @since GemFire 4.0 + */ + protected DistributedSystemConfigImpl() { + + } + + /** + * Creates a new <code>DistributedSystemConifgImpl</code> whose configuration is specified by the + * given <code>Properties</code> object. + */ + protected DistributedSystemConfigImpl(Properties props) { + this(props, false); + } + + /** + * Creates a new <code>DistributedSystemConifgImpl</code> whose configuration is specified by the + * given <code>Properties</code> object. + * + * @param props The configuration properties specified by the caller + * @param ignoreGemFirePropsFile whether to skip loading distributed system properties from + * gemfire.properties file + * + * @since GemFire 6.5 + */ + protected DistributedSystemConfigImpl(Properties props, boolean ignoreGemFirePropsFile) { + this(new DistributionConfigImpl(filterOutAdminProperties(props), ignoreGemFirePropsFile), + DEFAULT_REMOTE_COMMAND); + String remoteCommand = props.getProperty(REMOTE_COMMAND_NAME); + if (remoteCommand != null) { + this.remoteCommand = remoteCommand; + } + + String entityConfigXMLFile = props.getProperty(ENTITY_CONFIG_XML_FILE_NAME); + if (entityConfigXMLFile != null) { + this.entityConfigXMLFile = entityConfigXMLFile; + } + + String refreshInterval = props.getProperty(REFRESH_INTERVAL_NAME); + if (refreshInterval != null) { + try { + this.refreshInterval = Integer.parseInt(refreshInterval); + } catch (NumberFormatException nfEx) { + throw new IllegalArgumentException( + LocalizedStrings.DistributedSystemConfigImpl_0_IS_NOT_A_VALID_INTEGER_1 + .toLocalizedString(new Object[] {refreshInterval, REFRESH_INTERVAL_NAME})); + } + } + } + + ////////////////////// Instance Methods ////////////////////// + + /** + * Returns the <code>LogWriterI18n</code> to be used when administering the distributed system. + * Returns null if nothing has been provided via <code>setInternalLogWriter</code>. + * + * @since GemFire 4.0 + */ + public InternalLogWriter getInternalLogWriter() { + // LOG: used only for sharing between IDS, AdminDSImpl and AgentImpl -- to prevent multiple + // banners, etc. + synchronized (this) { + return this.logWriter; + } + } + + /** + * Sets the <code>LogWriterI18n</code> to be used when administering the distributed system. + */ + public void setInternalLogWriter(InternalLogWriter logWriter) { + // LOG: used only for sharing between IDS, AdminDSImpl and AgentImpl -- to prevent multiple + // banners, etc. + synchronized (this) { + this.logWriter = logWriter; + } + } + + public LogConfig createLogConfig() { + return new LogConfig() { + @Override + public int getLogLevel() { + return LogWriterImpl.levelNameToCode(DistributedSystemConfigImpl.this.getLogLevel()); + } + + @Override + public File getLogFile() { + return new File(DistributedSystemConfigImpl.this.getLogFile()); + } + + @Override + public int getLogFileSizeLimit() { + return DistributedSystemConfigImpl.this.getLogFileSizeLimit(); + } + + @Override + public int getLogDiskSpaceLimit() { + return DistributedSystemConfigImpl.this.getLogDiskSpaceLimit(); + } + + @Override + public String getName() { + return DistributedSystemConfigImpl.this.getSystemName(); + } + + @Override + public String toLoggerString() { + return DistributedSystemConfigImpl.this.toString(); + } + }; + } + + /** + * Marks this config object as "read only". Attempts to modify a config object will result in a + * {@link IllegalStateException} being thrown. + * + * @since GemFire 4.0 + */ + void setDistributedSystem(AdminDistributedSystemImpl system) { + this.system = system; + } + + /** + * Checks to see if this config object is "read only". If it is, then an + * {@link IllegalStateException} is thrown. + * + * @since GemFire 4.0 + */ + protected void checkReadOnly() { + if (this.system != null) { + throw new IllegalStateException( + LocalizedStrings.DistributedSystemConfigImpl_A_DISTRIBUTEDSYSTEMCONFIG_OBJECT_CANNOT_BE_MODIFIED_AFTER_IT_HAS_BEEN_USED_TO_CREATE_AN_ADMINDISTRIBUTEDSYSTEM + .toLocalizedString()); + } + } + + public String getEntityConfigXMLFile() { + return this.entityConfigXMLFile; + } + + public void setEntityConfigXMLFile(String xmlFile) { + checkReadOnly(); + this.entityConfigXMLFile = xmlFile; + configChanged(); + } + + /** + * Parses the XML configuration file that describes managed entities. + * + * @throws AdminXmlException If a problem is encountered while parsing the XML file. + */ + private void parseEntityConfigXMLFile() { + String fileName = this.entityConfigXMLFile; + File xmlFile = new File(fileName); + if (!xmlFile.exists()) { + if (DEFAULT_ENTITY_CONFIG_XML_FILE.equals(fileName)) { + // Default doesn't exist, no big deal + return; + } else { + throw new AdminXmlException( + LocalizedStrings.DistributedSystemConfigImpl_ENTITY_CONFIGURATION_XML_FILE_0_DOES_NOT_EXIST + .toLocalizedString(fileName)); + } + } + + try { + InputStream is = new FileInputStream(xmlFile); + try { + ManagedEntityConfigXmlParser.parse(is, this); + } finally { + is.close(); + } + } catch (IOException ex) { + throw new AdminXmlException( + LocalizedStrings.DistributedSystemConfigImpl_WHILE_PARSING_0.toLocalizedString(fileName), + ex); + } + } + + public String getSystemId() { + return this.systemId; + } + + public void setSystemId(String systemId) { + checkReadOnly(); + this.systemId = systemId; + configChanged(); + } + + /** + * Returns the multicast address for the system + */ + public String getMcastAddress() { + return this.mcastAddress; + } + + public void setMcastAddress(String mcastAddress) { + checkReadOnly(); + this.mcastAddress = mcastAddress; + configChanged(); + } + + /** + * Returns the multicast port for the system + */ + public int getMcastPort() { + return this.mcastPort; + } + + public void setMcastPort(int mcastPort) { + checkReadOnly(); + this.mcastPort = mcastPort; + configChanged(); + } + + public int getAckWaitThreshold() { + return this.ackWaitThreshold; + } + + public void setAckWaitThreshold(int seconds) { + checkReadOnly(); + this.ackWaitThreshold = seconds; + configChanged(); + } + + public int getAckSevereAlertThreshold() { + return this.ackSevereAlertThreshold; + } + + public void setAckSevereAlertThreshold(int seconds) { + checkReadOnly(); + this.ackSevereAlertThreshold = seconds; + configChanged(); + } + + /** + * Returns the comma-delimited list of locators for the system + */ + public String getLocators() { + return this.locators; + } + + public void setLocators(String locators) { + checkReadOnly(); + if (locators == null) { + this.locators = ""; + } else { + this.locators = locators; + } + configChanged(); + } + + /** + * Returns the value for membership-port-range + * + * @return the value for the Distributed System property membership-port-range + */ + public String getMembershipPortRange() { + return this.membershipPortRange; + } + + /** + * Sets the Distributed System property membership-port-range + * + * @param membershipPortRangeStr the value for membership-port-range given as two numbers + * separated by a minus sign. + */ + public void setMembershipPortRange(String membershipPortRangeStr) { + /* + * FIXME: Setting attributes in DistributedSystemConfig has no effect on DistributionConfig + * which is actually used for connection with DS. This is true for all such attributes. Should + * be addressed in the Admin Revamp if we want these 'set' calls to affect anything. Then we can + * use the validation code in DistributionConfigImpl code. + */ + checkReadOnly(); + if (membershipPortRangeStr == null) { + this.membershipPortRange = getMembershipPortRangeString(DEFAULT_MEMBERSHIP_PORT_RANGE); + } else { + try { + if (validateMembershipRange(membershipPortRangeStr)) { + this.membershipPortRange = membershipPortRangeStr; + } else { + throw new IllegalArgumentException( + LocalizedStrings.DistributedSystemConfigImpl_INVALID_VALUE_FOR_MEMBERSHIP_PORT_RANGE + .toLocalizedString( + new Object[] {membershipPortRangeStr, MEMBERSHIP_PORT_RANGE_NAME})); + } + } catch (Exception e) { + if (logger.isDebugEnabled()) { + logger.debug(e.getMessage(), e); + } + } + } + } + + public void setTcpPort(int port) { + checkReadOnly(); + this.tcpPort = port; + configChanged(); + } + + public int getTcpPort() { + return this.tcpPort; + } + + /** + * Validates the given string - which is expected in the format as two numbers separated by a + * minus sign - in to an integer array of length 2 with first element as lower end & second + * element as upper end of the range. + * + * @param membershipPortRange membership-port-range given as two numbers separated by a minus + * sign. + * @return true if the membership-port-range string is valid, false otherwise + */ + private boolean validateMembershipRange(String membershipPortRange) { + int[] range = null; + if (membershipPortRange != null && membershipPortRange.trim().length() > 0) { + String[] splitted = membershipPortRange.split("-"); + range = new int[2]; + range[0] = Integer.parseInt(splitted[0].trim()); + range[1] = Integer.parseInt(splitted[1].trim()); + // NumberFormatException if any could be thrown + + if (range[0] < 0 || range[0] >= range[1] || range[1] < 0 || range[1] > 65535) { + range = null; + } + } + return range != null; + } + + /** + * @return the String representation of membershipPortRange with lower & upper limits of the port + * range separated by '-' e.g. 1-65535 + */ + private static String getMembershipPortRangeString(int[] membershipPortRange) { + String membershipPortRangeString = ""; + if (membershipPortRange != null && membershipPortRange.length == 2) { + membershipPortRangeString = membershipPortRange[0] + "-" + membershipPortRange[1]; + } + + return membershipPortRangeString; + } + + public String getBindAddress() { + return this.bindAddress; + } + + public void setBindAddress(String bindAddress) { + checkReadOnly(); + basicSetBindAddress(bindAddress); + configChanged(); + } + + public String getServerBindAddress() { + return this.serverBindAddress; + } + + public void setServerBindAddress(String bindAddress) { + checkReadOnly(); + basicSetServerBindAddress(bindAddress); + configChanged(); + } + + public boolean getDisableTcp() { + return this.disableTcp; + } + + public void setDisableTcp(boolean flag) { + checkReadOnly(); + disableTcp = flag; + configChanged(); + } + + public void setEnableNetworkPartitionDetection(boolean newValue) { + checkReadOnly(); + this.enableNetworkPartitionDetection = newValue; + configChanged(); + } + + public boolean getEnableNetworkPartitionDetection() { + return this.enableNetworkPartitionDetection; + } + + public void setDisableAutoReconnect(boolean newValue) { + checkReadOnly(); + this.disableAutoReconnect = newValue; + configChanged(); + } + + public boolean getDisableAutoReconnect() { + return this.disableAutoReconnect; + } + + public int getMemberTimeout() { + return this.memberTimeout; + } + + public void setMemberTimeout(int value) { + checkReadOnly(); + this.memberTimeout = value; + configChanged(); + } + + private void basicSetBindAddress(String bindAddress) { + if (!validateBindAddress(bindAddress)) { + throw new IllegalArgumentException( + LocalizedStrings.DistributedSystemConfigImpl_INVALID_BIND_ADDRESS_0 + .toLocalizedString(bindAddress)); + } + this.bindAddress = bindAddress; + } + + private void basicSetServerBindAddress(String bindAddress) { + if (!validateBindAddress(bindAddress)) { + throw new IllegalArgumentException( + LocalizedStrings.DistributedSystemConfigImpl_INVALID_BIND_ADDRESS_0 + .toLocalizedString(bindAddress)); + } + this.serverBindAddress = bindAddress; + } + + /** + * Returns the remote command setting to use for remote administration + */ + public String getRemoteCommand() { + return this.remoteCommand; + } + + /** + * Sets the remote command for this config object. This attribute may be modified after this + * config object has been used to create an admin distributed system. + */ + public void setRemoteCommand(String remoteCommand) { + if (!ALLOW_ALL_REMOTE_COMMANDS) { + checkRemoteCommand(remoteCommand); + } + this.remoteCommand = remoteCommand; + configChanged(); + } + + private static final boolean ALLOW_ALL_REMOTE_COMMANDS = + Boolean.getBoolean(DistributionConfig.GEMFIRE_PREFIX + "admin.ALLOW_ALL_REMOTE_COMMANDS"); + private static final String[] LEGAL_REMOTE_COMMANDS = {"rsh", "ssh"}; + private static final String ILLEGAL_REMOTE_COMMAND_RSH_OR_SSH = + "Allowed remote commands include \"rsh {HOST} {CMD}\" or \"ssh {HOST} {CMD}\" with valid rsh or ssh switches. Invalid: "; + + private final void checkRemoteCommand(final String remoteCommand) { + if (remoteCommand == null || remoteCommand.isEmpty()) { + return; + } + final String command = remoteCommand.toLowerCase().trim(); + if (!command.contains("{host}") || !command.contains("{cmd}")) { + throw new IllegalArgumentException(ILLEGAL_REMOTE_COMMAND_RSH_OR_SSH + remoteCommand); + } + + final StringTokenizer tokenizer = new StringTokenizer(command, " "); + final ArrayList<String> array = new ArrayList<String>(); + for (int i = 0; tokenizer.hasMoreTokens(); i++) { + String string = tokenizer.nextToken(); + if (i == 0) { + // first element must be rsh or ssh + boolean found = false; + for (int j = 0; j < LEGAL_REMOTE_COMMANDS.length; j++) { + if (string.contains(LEGAL_REMOTE_COMMANDS[j])) { + // verify command is at end of string + if (!(string.endsWith(LEGAL_REMOTE_COMMANDS[j]) + || string.endsWith(LEGAL_REMOTE_COMMANDS[j] + ".exe"))) { + throw new IllegalArgumentException(ILLEGAL_REMOTE_COMMAND_RSH_OR_SSH + remoteCommand); + } + found = true; + } + } + if (!found) { + throw new IllegalArgumentException(ILLEGAL_REMOTE_COMMAND_RSH_OR_SSH + remoteCommand); + } + } else { + final boolean isSwitch = string.startsWith("-"); + final boolean isHostOrCmd = string.equals("{host}") || string.equals("{cmd}"); + + // additional elements must be switches or values-for-switches or {host} or user@{host} or + // {cmd} + if (!isSwitch && !isHostOrCmd) { + final String previous = + (array == null || array.isEmpty()) ? null : array.get(array.size() - 1); + final boolean isValueForSwitch = previous != null && previous.startsWith("-"); + final boolean isHostWithUser = string.contains("@") && string.endsWith("{host}"); + + if (!(isValueForSwitch || isHostWithUser)) { + throw new IllegalArgumentException(ILLEGAL_REMOTE_COMMAND_RSH_OR_SSH + remoteCommand); + } + } + } + array.add(string); + } + } + + public String getSystemName() { + return this.systemName; + } + + public void setSystemName(final String systemName) { + checkReadOnly(); + this.systemName = systemName; + configChanged(); + } + + /** + * Returns an array of configurations for statically known CacheServers + * + * @since GemFire 4.0 + */ + public CacheServerConfig[] getCacheServerConfigs() { + return (CacheServerConfig[]) this.cacheServerConfigs + .toArray(new CacheServerConfig[this.cacheServerConfigs.size()]); + } + + public CacheVmConfig[] getCacheVmConfigs() { + return (CacheVmConfig[]) this.cacheServerConfigs + .toArray(new CacheVmConfig[this.cacheServerConfigs.size()]); + } + + /** + * Creates the configuration for a CacheServer + * + * @since GemFire 4.0 + */ + public CacheServerConfig createCacheServerConfig() { + CacheServerConfig config = new CacheServerConfigImpl(); + addCacheServerConfig(config); + return config; + } + + public CacheVmConfig createCacheVmConfig() { + return (CacheVmConfig) createCacheServerConfig(); + } + + /** + * Adds the configuration for a CacheServer + * + * @since GemFire 4.0 + */ + private void addCacheServerConfig(CacheServerConfig managerConfig) { + checkReadOnly(); + + if (managerConfig == null) + return; + for (Iterator iter = this.cacheServerConfigs.iterator(); iter.hasNext();) { + CacheServerConfigImpl impl = (CacheServerConfigImpl) iter.next(); + if (impl.equals(managerConfig)) { + return; + } + } + this.cacheServerConfigs.add(managerConfig); + configChanged(); + } + + /** + * Removes the configuration for a CacheServer + * + * @since GemFire 4.0 + */ + public void removeCacheServerConfig(CacheServerConfig managerConfig) { + removeCacheVmConfig((CacheVmConfig) managerConfig); + } + + public void removeCacheVmConfig(CacheVmConfig managerConfig) { + checkReadOnly(); + this.cacheServerConfigs.remove(managerConfig); + configChanged(); + } + + /** + * Returns the configurations of all managed distribution locators + */ + public DistributionLocatorConfig[] getDistributionLocatorConfigs() { + if (this.system != null) { + DistributionLocator[] locators = this.system.getDistributionLocators(); + DistributionLocatorConfig[] configs = new DistributionLocatorConfig[locators.length]; + for (int i = 0; i < locators.length; i++) { + configs[i] = locators[i].getConfig(); + } + return configs; + + } else { + Object[] array = new DistributionLocatorConfig[this.locatorConfigs.size()]; + return (DistributionLocatorConfig[]) this.locatorConfigs.toArray(array); + } + } + + /** + * Creates the configuration for a DistributionLocator + */ + public DistributionLocatorConfig createDistributionLocatorConfig() { + checkReadOnly(); + DistributionLocatorConfig config = new DistributionLocatorConfigImpl(); + addDistributionLocatorConfig(config); + return config; + } + + /** + * Adds the configuration for a DistributionLocator + */ + private void addDistributionLocatorConfig(DistributionLocatorConfig config) { + checkReadOnly(); + this.locatorConfigs.add(config); + configChanged(); + } + + /** + * Removes the configuration for a DistributionLocator + */ + public void removeDistributionLocatorConfig(DistributionLocatorConfig config) { + checkReadOnly(); + this.locatorConfigs.remove(config); + configChanged(); + } + + /** + * Validates the bind address. The address may be a host name or IP address, but it must not be + * empty and must be usable for creating an InetAddress. Cannot have a leading '/' (which + * InetAddress.toString() produces). + * + * @param bindAddress host name or IP address to validate + */ + public static boolean validateBindAddress(String bindAddress) { + if (bindAddress == null || bindAddress.length() == 0) + return true; + if (InetAddressUtil.validateHost(bindAddress) == null) + return false; + return true; + } + + public synchronized void configChanged() { + ConfigListener[] clients = null; + synchronized (this.listeners) { + clients = (ConfigListener[]) listeners.toArray(new ConfigListener[this.listeners.size()]); + } + for (int i = 0; i < clients.length; i++) { + try { + clients[i].configChanged(this); + } catch (Exception e) { + logger.warn(e.getMessage(), e); + } + } + } + + /** + * Registers listener for notification of changes in this config. + */ + public void addListener(ConfigListener listener) { + synchronized (this.listeners) { + this.listeners.add(listener); + } + } + + /** + * Removes previously registered listener of this config. + */ + public void removeListener(ConfigListener listener) { + synchronized (this.listeners) { + this.listeners.remove(listener); + } + } + + // ------------------------------------------------------------------------- + // SSL support... + // ------------------------------------------------------------------------- + private boolean sslEnabled = DistributionConfig.DEFAULT_SSL_ENABLED; + private String sslProtocols = DistributionConfig.DEFAULT_SSL_PROTOCOLS; + private String sslCiphers = DistributionConfig.DEFAULT_SSL_CIPHERS; + private boolean sslAuthenticationRequired = DistributionConfig.DEFAULT_SSL_REQUIRE_AUTHENTICATION; + private Properties sslProperties = new Properties(); + + public boolean isSSLEnabled() { + return this.sslEnabled; + } + + public void setSSLEnabled(boolean enabled) { + checkReadOnly(); + this.sslEnabled = enabled; + configChanged(); + } + + public String getSSLProtocols() { + return this.sslProtocols; + } + + public void setSSLProtocols(String protocols) { + checkReadOnly(); + this.sslProtocols = protocols; + configChanged(); + } + + public String getSSLCiphers() { + return this.sslCiphers; + } + + public void setSSLCiphers(String ciphers) { + checkReadOnly(); + this.sslCiphers = ciphers; + configChanged(); + } + + public boolean isSSLAuthenticationRequired() { + return this.sslAuthenticationRequired; + } + + public void setSSLAuthenticationRequired(boolean authRequired) { + checkReadOnly(); + this.sslAuthenticationRequired = authRequired; + configChanged(); + } + + public Properties getSSLProperties() { + return this.sslProperties; + } + + public void setSSLProperties(Properties sslProperties) { + checkReadOnly(); + this.sslProperties = sslProperties; + if (this.sslProperties == null) { + this.sslProperties = new Properties(); + } + configChanged(); + } + + public void addSSLProperty(String key, String value) { + checkReadOnly(); + this.sslProperties.put(key, value); + configChanged(); + } + + public void removeSSLProperty(String key) { + checkReadOnly(); + this.sslProperties.remove(key); + configChanged(); + } + + /** + * @return the gfSecurityProperties + * @since GemFire 6.6.3 + */ + public Properties getGfSecurityProperties() { + return gfSecurityProperties; + } + + public String getLogFile() { + return this.logFile; + } + + public void setLogFile(String logFile) { + checkReadOnly(); + this.logFile = logFile; + configChanged(); + } + + public String getLogLevel() { + return this.logLevel; + } + + public void setLogLevel(String logLevel) { + checkReadOnly(); + this.logLevel = logLevel; + configChanged(); + } + + public int getLogDiskSpaceLimit() { + return this.logDiskSpaceLimit; + } + + public void setLogDiskSpaceLimit(int limit) { + checkReadOnly(); + this.logDiskSpaceLimit = limit; + configChanged(); + } + + public int getLogFileSizeLimit() { + return this.logFileSizeLimit; + } + + public void setLogFileSizeLimit(int limit) { + checkReadOnly(); + this.logFileSizeLimit = limit; + configChanged(); + } + + /** + * Returns the refreshInterval in seconds + */ + public int getRefreshInterval() { + return this.refreshInterval; + } + + /** + * Sets the refreshInterval in seconds + */ + public void setRefreshInterval(int timeInSecs) { + checkReadOnly(); + this.refreshInterval = timeInSecs; + configChanged(); + } + + /** + * Makes sure that the mcast port and locators are correct and consistent. + * + * @throws IllegalArgumentException If configuration is not valid + */ + public void validate() { + if (this.getMcastPort() < MIN_MCAST_PORT || this.getMcastPort() > MAX_MCAST_PORT) { + throw new IllegalArgumentException( + LocalizedStrings.DistributedSystemConfigImpl_MCASTPORT_MUST_BE_AN_INTEGER_INCLUSIVELY_BETWEEN_0_AND_1 + .toLocalizedString( + new Object[] {Integer.valueOf(MIN_MCAST_PORT), Integer.valueOf(MAX_MCAST_PORT)})); + } + + // disabled in 5.1 - multicast and locators can be used together + // if (!DEFAULT_LOCATORS.equals(this.getLocators()) && + // this.mcastPort > 0) { + // throw new IllegalArgumentException( + // "mcastPort must be zero when locators are specified"); + // } + + LogWriterImpl.levelNameToCode(this.logLevel); + + if (this.logFileSizeLimit < MIN_LOG_FILE_SIZE_LIMIT + || this.logFileSizeLimit > MAX_LOG_FILE_SIZE_LIMIT) { + throw new IllegalArgumentException( + LocalizedStrings.DistributedSystemConfigImpl_LOGFILESIZELIMIT_MUST_BE_AN_INTEGER_BETWEEN_0_AND_1 + .toLocalizedString(new Object[] {Integer.valueOf(MIN_LOG_FILE_SIZE_LIMIT), + Integer.valueOf(MAX_LOG_FILE_SIZE_LIMIT)})); + } + + if (this.logDiskSpaceLimit < MIN_LOG_DISK_SPACE_LIMIT + || this.logDiskSpaceLimit > MAX_LOG_DISK_SPACE_LIMIT) { + throw new IllegalArgumentException( + LocalizedStrings.DistributedSystemConfigImpl_LOGDISKSPACELIMIT_MUST_BE_AN_INTEGER_BETWEEN_0_AND_1 + .toLocalizedString(new Object[] {Integer.valueOf(MIN_LOG_DISK_SPACE_LIMIT), + Integer.valueOf(MAX_LOG_DISK_SPACE_LIMIT)})); + } + + parseEntityConfigXMLFile(); + } + + /** + * Makes a deep copy of this config object. + */ + @Override + public Object clone() throws CloneNotSupportedException { + DistributedSystemConfigImpl other = (DistributedSystemConfigImpl) super.clone(); + other.system = null; + other.cacheServerConfigs = new HashSet(); + other.locatorConfigs = new HashSet(); + + DistributionLocatorConfig[] myLocators = this.getDistributionLocatorConfigs(); + for (int i = 0; i < myLocators.length; i++) { + DistributionLocatorConfig locator = myLocators[i]; + other.addDistributionLocatorConfig((DistributionLocatorConfig) locator.clone()); + } + + CacheServerConfig[] myCacheServers = this.getCacheServerConfigs(); + for (int i = 0; i < myCacheServers.length; i++) { + CacheServerConfig locator = myCacheServers[i]; + other.addCacheServerConfig((CacheServerConfig) locator.clone()); + } + + return other; + } + + @Override + public String toString() { + StringBuffer buf = new StringBuffer(1000); + String lf = System.getProperty("line.separator"); + if (lf == null) + lf = ","; + + buf.append("DistributedSystemConfig("); + buf.append(lf); + buf.append(" system-name="); + buf.append(String.valueOf(this.systemName)); + buf.append(lf); + buf.append(" " + MCAST_ADDRESS + "="); + buf.append(String.valueOf(this.mcastAddress)); + buf.append(lf); + buf.append(" " + MCAST_PORT + "="); + buf.append(String.valueOf(this.mcastPort)); + buf.append(lf); + buf.append(" " + LOCATORS + "="); + buf.append(String.valueOf(this.locators)); + buf.append(lf); + buf.append(" " + MEMBERSHIP_PORT_RANGE_NAME + "="); + buf.append(getMembershipPortRange()); + buf.append(lf); + buf.append(" " + BIND_ADDRESS + "="); + buf.append(String.valueOf(this.bindAddress)); + buf.append(lf); + buf.append(" " + TCP_PORT + "=" + this.tcpPort); + buf.append(lf); + buf.append(" " + DISABLE_TCP + "="); + buf.append(String.valueOf(this.disableTcp)); + buf.append(lf); + buf.append(" " + DISABLE_AUTO_RECONNECT + "="); + buf.append(String.valueOf(this.disableAutoReconnect)); + buf.append(lf); + buf.append(" " + REMOTE_COMMAND_NAME + "="); + buf.append(String.valueOf(this.remoteCommand)); + buf.append(lf); + buf.append(" " + CLUSTER_SSL_ENABLED + "="); + buf.append(String.valueOf(this.sslEnabled)); + buf.append(lf); + buf.append(" " + CLUSTER_SSL_CIPHERS + "="); + buf.append(String.valueOf(this.sslCiphers)); + buf.append(lf); + buf.append(" " + CLUSTER_SSL_PROTOCOLS + "="); + buf.append(String.valueOf(this.sslProtocols)); + buf.append(lf); + buf.append(" " + CLUSTER_SSL_REQUIRE_AUTHENTICATION + "="); + buf.append(String.valueOf(this.sslAuthenticationRequired)); + buf.append(lf); + buf.append(" " + LOG_FILE_NAME + "="); + buf.append(String.valueOf(this.logFile)); + buf.append(lf); + buf.append(" " + LOG_LEVEL_NAME + "="); + buf.append(String.valueOf(this.logLevel)); + buf.append(lf); + buf.append(" " + LOG_DISK_SPACE_LIMIT_NAME + "="); + buf.append(String.valueOf(this.logDiskSpaceLimit)); + buf.append(lf); + buf.append(" " + LOG_FILE_SIZE_LIMIT_NAME + "="); + buf.append(String.valueOf(this.logFileSizeLimit)); + buf.append(lf); + buf.append(" " + REFRESH_INTERVAL_NAME + "="); + buf.append(String.valueOf(this.refreshInterval)); + buf.append(")"); + return buf.toString(); + } +} +