blob: ff93f32cecf408a5d00e51b06540e1a02f6f31b6 [file] [log] [blame]
/*
* 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.management.internal.cli.commands;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.springframework.shell.core.annotation.CliCommand;
import org.springframework.shell.core.annotation.CliOption;
import org.apache.geode.cache.ExpirationAction;
import org.apache.geode.cache.configuration.CacheConfig;
import org.apache.geode.cache.configuration.CacheElement;
import org.apache.geode.cache.configuration.DeclarableType;
import org.apache.geode.cache.configuration.RegionAttributesType;
import org.apache.geode.cache.configuration.RegionAttributesType.EvictionAttributes;
import org.apache.geode.cache.configuration.RegionAttributesType.ExpirationAttributesType;
import org.apache.geode.cache.configuration.RegionConfig;
import org.apache.geode.distributed.ConfigurationPersistenceService;
import org.apache.geode.distributed.DistributedMember;
import org.apache.geode.management.cli.CliMetaData;
import org.apache.geode.management.cli.ConverterHint;
import org.apache.geode.management.cli.SingleGfshCommand;
import org.apache.geode.management.configuration.ClassName;
import org.apache.geode.management.internal.cli.functions.CliFunctionResult;
import org.apache.geode.management.internal.cli.functions.RegionAlterFunction;
import org.apache.geode.management.internal.cli.i18n.CliStrings;
import org.apache.geode.management.internal.cli.result.model.ResultModel;
import org.apache.geode.management.internal.exceptions.EntityNotFoundException;
import org.apache.geode.security.ResourcePermission.Operation;
import org.apache.geode.security.ResourcePermission.Resource;
public class AlterRegionCommand extends SingleGfshCommand {
@CliCommand(value = CliStrings.ALTER_REGION, help = CliStrings.ALTER_REGION__HELP)
@CliMetaData(relatedTopic = CliStrings.TOPIC_GEODE_REGION)
public ResultModel alterRegion(
@CliOption(key = CliStrings.ALTER_REGION__REGION, mandatory = true,
optionContext = ConverterHint.REGION_PATH,
help = CliStrings.ALTER_REGION__REGION__HELP) String regionPath,
@CliOption(key = {CliStrings.GROUP, CliStrings.GROUPS},
optionContext = ConverterHint.MEMBERGROUP,
help = CliStrings.ALTER_REGION__GROUP__HELP) String[] groups,
@CliOption(key = CliStrings.ALTER_REGION__ENTRYEXPIRATIONIDLETIME,
help = CliStrings.ALTER_REGION__ENTRYEXPIRATIONIDLETIME__HELP) Integer entryExpirationIdleTime,
@CliOption(key = CliStrings.ALTER_REGION__ENTRYEXPIRATIONIDLETIMEACTION,
specifiedDefaultValue = "INVALIDATE",
help = CliStrings.ALTER_REGION__ENTRYEXPIRATIONIDLETIMEACTION__HELP) ExpirationAction entryExpirationIdleTimeAction,
@CliOption(key = CliStrings.ALTER_REGION__ENTRYEXPIRATIONTIMETOLIVE,
help = CliStrings.ALTER_REGION__ENTRYEXPIRATIONTIMETOLIVE__HELP) Integer entryExpirationTTL,
@CliOption(key = CliStrings.ALTER_REGION__ENTRYEXPIRATIONTTLACTION,
specifiedDefaultValue = "INVALIDATE",
help = CliStrings.ALTER_REGION__ENTRYEXPIRATIONTTLACTION__HELP) ExpirationAction entryExpirationTTLAction,
@CliOption(key = CliStrings.ENTRY_IDLE_TIME_CUSTOM_EXPIRY, specifiedDefaultValue = "",
help = CliStrings.ENTRY_IDLE_TIME_CUSTOM_EXPIRY_HELP) ClassName entryIdleTimeCustomExpiry,
@CliOption(key = CliStrings.ENTRY_TTL_CUSTOM_EXPIRY, specifiedDefaultValue = "",
help = CliStrings.ENTRY_TTL_CUSTOM_EXPIRY_HELP) ClassName entryTTLCustomExpiry,
@CliOption(key = CliStrings.ALTER_REGION__REGIONEXPIRATIONIDLETIME,
help = CliStrings.ALTER_REGION__REGIONEXPIRATIONIDLETIME__HELP) Integer regionExpirationIdleTime,
@CliOption(key = CliStrings.ALTER_REGION__REGIONEXPIRATIONIDLETIMEACTION,
specifiedDefaultValue = "INVALIDATE",
help = CliStrings.ALTER_REGION__REGIONEXPIRATIONIDLETIMEACTION__HELP) ExpirationAction regionExpirationIdleTimeAction,
@CliOption(key = CliStrings.ALTER_REGION__REGIONEXPIRATIONTTL,
help = CliStrings.ALTER_REGION__REGIONEXPIRATIONTTL__HELP) Integer regionExpirationTTL,
@CliOption(key = CliStrings.ALTER_REGION__REGIONEXPIRATIONTTLACTION,
specifiedDefaultValue = "INVALIDATE",
help = CliStrings.ALTER_REGION__REGIONEXPIRATIONTTLACTION__HELP) ExpirationAction regionExpirationTTLAction,
@CliOption(key = CliStrings.ALTER_REGION__CACHELISTENER, specifiedDefaultValue = "",
// split the input only with comma outside of json string
optionContext = "splittingRegex=,(?![^{]*\\})",
help = CliStrings.ALTER_REGION__CACHELISTENER__HELP) ClassName[] cacheListeners,
@CliOption(key = CliStrings.ALTER_REGION__CACHELOADER, specifiedDefaultValue = "",
help = CliStrings.ALTER_REGION__CACHELOADER__HELP) ClassName cacheLoader,
@CliOption(key = CliStrings.ALTER_REGION__CACHEWRITER, specifiedDefaultValue = "",
help = CliStrings.ALTER_REGION__CACHEWRITER__HELP) ClassName cacheWriter,
@CliOption(key = CliStrings.ALTER_REGION__ASYNCEVENTQUEUEID, specifiedDefaultValue = "",
help = CliStrings.ALTER_REGION__ASYNCEVENTQUEUEID__HELP) String[] asyncEventQueueIds,
@CliOption(key = CliStrings.ALTER_REGION__GATEWAYSENDERID, specifiedDefaultValue = "",
help = CliStrings.ALTER_REGION__GATEWAYSENDERID__HELP) String[] gatewaySenderIds,
@CliOption(key = CliStrings.ALTER_REGION__CLONINGENABLED, specifiedDefaultValue = "true",
help = CliStrings.ALTER_REGION__CLONINGENABLED__HELP) Boolean cloningEnabled,
@CliOption(key = CliStrings.ALTER_REGION__EVICTIONMAX, specifiedDefaultValue = "0",
help = CliStrings.ALTER_REGION__EVICTIONMAX__HELP) Integer evictionMax) {
authorize(Resource.DATA, Operation.MANAGE, regionPath);
Set<DistributedMember> targetMembers = findMembers(groups, null);
if (targetMembers.isEmpty()) {
return ResultModel.createError(CliStrings.NO_MEMBERS_FOUND_MESSAGE);
}
ConfigurationPersistenceService ccService = getConfigurationPersistenceService();
if (groups == null) {
groups = new String[] {"cluster"};
}
// Check that the specified region is in all the groups (normally we should pass in just one
// group for alter region command
if (ccService != null) {
for (String group : groups) {
CacheConfig clusterConfig = ccService.getCacheConfig(group);
RegionConfig regionConfig = null;
if (clusterConfig != null) {
// we always know that regionPath starts with a "/", so we need to strip it out before we
// pass it in to look for the regionConfig
regionConfig =
CacheElement.findElement(clusterConfig.getRegions(), regionPath.substring(1));
}
if (regionConfig == null) {
throw new EntityNotFoundException(
String.format("%s does not exist in group %s", regionPath, group));
}
}
}
RegionConfig deltaConfig = new RegionConfig();
deltaConfig.setName(regionPath);
RegionAttributesType regionAttributesType = new RegionAttributesType();
deltaConfig.setRegionAttributes(regionAttributesType);
regionAttributesType.setEntryIdleTime(ExpirationAttributesType.generate(entryExpirationIdleTime,
(entryExpirationIdleTimeAction == null) ? null
: entryExpirationIdleTimeAction.toXmlString(),
entryIdleTimeCustomExpiry));
regionAttributesType.setEntryTimeToLive(ExpirationAttributesType.generate(entryExpirationTTL,
(entryExpirationTTLAction == null) ? null : entryExpirationTTLAction.toXmlString(),
entryTTLCustomExpiry));
regionAttributesType.setRegionIdleTime(
ExpirationAttributesType.generate(regionExpirationIdleTime,
(regionExpirationIdleTimeAction == null) ? null
: regionExpirationIdleTimeAction.toXmlString(),
null));
regionAttributesType.setRegionTimeToLive(
ExpirationAttributesType.generate(regionExpirationTTL,
(regionExpirationTTLAction == null) ? null : regionExpirationTTLAction.toXmlString(),
null));
if (cacheLoader != null) {
regionAttributesType.setCacheLoader(
new DeclarableType(cacheLoader.getClassName(), cacheLoader.getInitProperties()));
}
if (cacheWriter != null) {
regionAttributesType.setCacheWriter(
new DeclarableType(cacheWriter.getClassName(), cacheWriter.getInitProperties()));
}
if (cacheListeners != null) {
regionAttributesType.getCacheListeners().addAll(
Arrays.stream(cacheListeners)
.map(l -> new DeclarableType(l.getClassName(), l.getInitProperties()))
.collect(Collectors.toList()));
}
if (gatewaySenderIds != null) {
regionAttributesType.setGatewaySenderIds(StringUtils.join(gatewaySenderIds, ","));
}
if (asyncEventQueueIds != null) {
regionAttributesType.setAsyncEventQueueIds(StringUtils.join(asyncEventQueueIds, ","));
}
if (cloningEnabled != null) {
regionAttributesType.setCloningEnabled(cloningEnabled);
}
if (evictionMax != null && evictionMax < 0) {
throw new IllegalArgumentException(CliStrings.format(
CliStrings.ALTER_REGION__MSG__SPECIFY_POSITIVE_INT_FOR_EVICTIONMAX_0_IS_NOT_VALID,
evictionMax));
}
if (evictionMax != null) {
EvictionAttributes evictionAttributes =
new EvictionAttributes();
EvictionAttributes.LruEntryCount lruEntryCount =
new EvictionAttributes.LruEntryCount();
lruEntryCount.setMaximum(evictionMax.toString());
evictionAttributes.setLruEntryCount(lruEntryCount);
regionAttributesType.setEvictionAttributes(evictionAttributes);
}
List<CliFunctionResult> regionAlterResults =
executeAndGetFunctionResult(new RegionAlterFunction(), deltaConfig, targetMembers);
ResultModel result = ResultModel.createMemberStatusResult(regionAlterResults);
result.setConfigObject(deltaConfig);
return result;
}
@Override
public boolean updateConfigForGroup(String group, CacheConfig cacheConfig, Object configObject) {
RegionConfig deltaConfig = (RegionConfig) configObject;
RegionConfig existingConfig = CacheElement.findElement(cacheConfig.getRegions(),
deltaConfig.getId());
RegionAttributesType deltaAttributes = deltaConfig.getRegionAttributes();
RegionAttributesType existingAttributes = existingConfig.getRegionAttributes();
existingAttributes.setEntryIdleTime(
ExpirationAttributesType.combine(existingAttributes.getEntryIdleTime(),
deltaAttributes.getEntryIdleTime()));
existingAttributes.setEntryTimeToLive(
ExpirationAttributesType.combine(existingAttributes.getEntryTimeToLive(),
deltaAttributes.getEntryTimeToLive()));
existingAttributes.setRegionIdleTime(
ExpirationAttributesType.combine(existingAttributes.getRegionIdleTime(),
deltaAttributes.getRegionIdleTime()));
existingAttributes.setRegionTimeToLive(
ExpirationAttributesType.combine(existingAttributes.getRegionTimeToLive(),
deltaAttributes.getRegionTimeToLive()));
if (deltaAttributes.getCacheLoader() != null) {
if (deltaAttributes.getCacheLoader().equals(DeclarableType.EMPTY)) {
existingAttributes.setCacheLoader(null);
} else {
existingAttributes.setCacheLoader(deltaAttributes.getCacheLoader());
}
}
if (deltaAttributes.getCacheWriter() != null) {
if (deltaAttributes.getCacheWriter().equals(DeclarableType.EMPTY)) {
existingAttributes.setCacheWriter(null);
} else {
existingAttributes.setCacheWriter(deltaAttributes.getCacheWriter());
}
}
if (!deltaAttributes.getCacheListeners().isEmpty()) {
existingAttributes.getCacheListeners().clear();
// only add the new cache listeners to the list when it's an EMPTY cache listener
if (!deltaAttributes.getCacheListeners().get(0).equals(DeclarableType.EMPTY)) {
existingAttributes.getCacheListeners().addAll(deltaAttributes.getCacheListeners());
}
}
if (deltaAttributes.getGatewaySenderIds() != null) {
existingAttributes.setGatewaySenderIds(deltaAttributes.getGatewaySenderIds());
}
if (deltaAttributes.getAsyncEventQueueIds() != null) {
existingAttributes.setAsyncEventQueueIds(deltaAttributes.getAsyncEventQueueIds());
}
if (deltaAttributes.isCloningEnabled() != null) {
existingAttributes.setCloningEnabled(deltaAttributes.isCloningEnabled());
}
EvictionAttributes evictionAttributes = deltaAttributes.getEvictionAttributes();
if (evictionAttributes != null) {
// we only set the max in the delta's lruEntryCount in the alter region command
String newMax = evictionAttributes.getLruEntryCount().getMaximum();
EvictionAttributes existingEviction = existingAttributes.getEvictionAttributes();
// we only alter the max value if there is an existing eviction attributes
if (existingEviction != null) {
if (existingEviction.getLruEntryCount() != null) {
existingEviction.getLruEntryCount().setMaximum(newMax);
}
if (existingEviction.getLruMemorySize() != null) {
existingEviction.getLruMemorySize().setMaximum(newMax);
}
}
}
return true;
}
}