From a1cb166941f57efab43e64401ba20d443599e4cc Mon Sep 17 00:00:00 2001 From: Juan Jose Ramos Cassella Date: Wed, 7 Mar 2018 14:39:19 +0000 Subject: [PATCH] GEODE-4771: Defaults in ConfigurePDXCommand - Refactored `ConfigurePDXCommand` to allow unit testing. - Added a custom `Interceptor` to validate command input. - Added unit and integration tests for `ConfigurePDXCommand`. - Fixed help strings for `auto-serializable-classes` and `portable-auto-serializable-classes`. - Fixed `ConfigurePDXCommand` to set `check-portability=false` when `--auto-serializable-classes` is used. --- .../cli/commands/ConfigurePDXCommand.java | 111 +++-- .../internal/cli/i18n/CliStrings.java | 5 +- .../ConfigurePDXCommandIntegrationTest.java | 130 ++++++ .../cli/commands/ConfigurePDXCommandTest.java | 379 ++++++++++++++++++ 4 files changed, 592 insertions(+), 33 deletions(-) create mode 100644 geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/ConfigurePDXCommandIntegrationTest.java create mode 100644 geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/ConfigurePDXCommandTest.java diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/ConfigurePDXCommand.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/ConfigurePDXCommand.java index 0c11448f40b4..43719f94ca87 100644 --- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/ConfigurePDXCommand.java +++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/ConfigurePDXCommand.java @@ -28,6 +28,8 @@ import org.apache.geode.internal.cache.xmlcache.CacheXmlGenerator; import org.apache.geode.management.cli.CliMetaData; import org.apache.geode.management.cli.Result; +import org.apache.geode.management.internal.cli.AbstractCliAroundInterceptor; +import org.apache.geode.management.internal.cli.GfshParseResult; import org.apache.geode.management.internal.cli.i18n.CliStrings; import org.apache.geode.management.internal.cli.result.InfoResultData; import org.apache.geode.management.internal.cli.result.ResultBuilder; @@ -37,8 +39,42 @@ import org.apache.geode.security.ResourcePermission; public class ConfigurePDXCommand extends GfshCommand { + + /** + * + * @param checkPortability + * @param patterns + */ + protected ReflectionBasedAutoSerializer createReflectionBasedAutoSerializer( + boolean checkPortability, String[] patterns) { + return new ReflectionBasedAutoSerializer(checkPortability, patterns); + } + + /** + * @param forParsing if true then this creation is used for parsing xml; if false then it is used + * for generating xml. + * @since GemFire 5.7 + */ + protected CacheCreation getCacheCreation(boolean forParsing) { + return new CacheCreation(forParsing); + } + + /** + * Creates the XmlEntity associated to the PDX configuration. + */ + protected XmlEntity createXmlEntity(CacheCreation cache) { + final StringWriter stringWriter = new StringWriter(); + final PrintWriter printWriter = new PrintWriter(stringWriter); + CacheXmlGenerator.generate(cache, printWriter, true, false, false); + printWriter.close(); + String xmlDefinition = stringWriter.toString(); + + return XmlEntity.builder().withType(CacheXml.PDX).withConfig(xmlDefinition).build(); + } + @CliCommand(value = CliStrings.CONFIGURE_PDX, help = CliStrings.CONFIGURE_PDX__HELP) - @CliMetaData(relatedTopic = CliStrings.TOPIC_GEODE_REGION) + @CliMetaData(relatedTopic = CliStrings.TOPIC_GEODE_REGION, + interceptor = "org.apache.geode.management.internal.cli.commands.ConfigurePDXCommand$Interceptor") @ResourceOperation(resource = ResourcePermission.Resource.CLUSTER, operation = ResourcePermission.Operation.MANAGE) public Result configurePDX( @@ -49,26 +85,26 @@ public Result configurePDX( @CliOption(key = CliStrings.CONFIGURE_PDX__DISKSTORE, specifiedDefaultValue = "", help = CliStrings.CONFIGURE_PDX__DISKSTORE__HELP) String diskStore, @CliOption(key = CliStrings.CONFIGURE_PDX__AUTO__SERIALIZER__CLASSES, - help = CliStrings.CONFIGURE_PDX__AUTO__SERIALIZER__CLASSES__HELP) String[] patterns, + help = CliStrings.CONFIGURE_PDX__AUTO__SERIALIZER__CLASSES__HELP) String[] nonPortableClassesPatterns, @CliOption(key = CliStrings.CONFIGURE_PDX__PORTABLE__AUTO__SERIALIZER__CLASSES, - help = CliStrings.CONFIGURE_PDX__PORTABLE__AUTO__SERIALIZER__CLASSES__HELP) String[] portablePatterns) { + help = CliStrings.CONFIGURE_PDX__PORTABLE__AUTO__SERIALIZER__CLASSES__HELP) String[] portableClassesPatterns) { + Result result; try { + ReflectionBasedAutoSerializer autoSerializer; + CacheCreation cache = getCacheCreation(true); InfoResultData ird = ResultBuilder.createInfoResultData(); - CacheCreation cache = new CacheCreation(true); - if ((portablePatterns != null && portablePatterns.length > 0) - && (patterns != null && patterns.length > 0)) { - return ResultBuilder.createUserErrorResult(CliStrings.CONFIGURE_PDX__ERROR__MESSAGE); - } if (!getAllNormalMembers().isEmpty()) { ird.addLine(CliStrings.CONFIGURE_PDX__NORMAL__MEMBERS__WARNING); } + // Set persistent and the disk-store if (diskStore != null) { cache.setPdxPersistent(true); ird.addLine(CliStrings.CONFIGURE_PDX__PERSISTENT + " = " + cache.getPdxPersistent()); + if (!diskStore.equals("")) { cache.setPdxDiskStore(diskStore); ird.addLine(CliStrings.CONFIGURE_PDX__DISKSTORE + " = " + cache.getPdxDiskStore()); @@ -86,52 +122,65 @@ public Result configurePDX( } else { cache.setPdxReadSerialized(CacheConfig.DEFAULT_PDX_READ_SERIALIZED); } + ird.addLine( CliStrings.CONFIGURE_PDX__READ__SERIALIZED + " = " + cache.getPdxReadSerialized()); - // Set ignoreUnreadFields if (ignoreUnreadFields != null) { cache.setPdxIgnoreUnreadFields(ignoreUnreadFields); } else { cache.setPdxIgnoreUnreadFields(CacheConfig.DEFAULT_PDX_IGNORE_UNREAD_FIELDS); } + ird.addLine(CliStrings.CONFIGURE_PDX__IGNORE__UNREAD_FIELDS + " = " + cache.getPdxIgnoreUnreadFields()); - - if (portablePatterns != null) { - ReflectionBasedAutoSerializer autoSerializer = - new ReflectionBasedAutoSerializer(portablePatterns); + // Auto Serializer Configuration + if (portableClassesPatterns != null) { + autoSerializer = createReflectionBasedAutoSerializer(true, portableClassesPatterns); cache.setPdxSerializer(autoSerializer); - ird.addLine("PDX Serializer " + cache.getPdxSerializer().getClass().getName()); - ird.addLine("Portable classes " + Arrays.toString(portablePatterns)); + ird.addLine("PDX Serializer = " + cache.getPdxSerializer().getClass().getName()); + ird.addLine("Portable Classes = " + Arrays.toString(portableClassesPatterns)); } - if (patterns != null) { - ReflectionBasedAutoSerializer nonPortableAutoSerializer = - new ReflectionBasedAutoSerializer(true, patterns); - cache.setPdxSerializer(nonPortableAutoSerializer); - ird.addLine("PDX Serializer : " + cache.getPdxSerializer().getClass().getName()); - ird.addLine("Non portable classes :" + Arrays.toString(patterns)); + if (nonPortableClassesPatterns != null) { + autoSerializer = createReflectionBasedAutoSerializer(false, nonPortableClassesPatterns); + cache.setPdxSerializer(autoSerializer); + ird.addLine("PDX Serializer = " + cache.getPdxSerializer().getClass().getName()); + ird.addLine("Non Portable Classes = " + Arrays.toString(nonPortableClassesPatterns)); } - final StringWriter stringWriter = new StringWriter(); - final PrintWriter printWriter = new PrintWriter(stringWriter); - CacheXmlGenerator.generate(cache, printWriter, true, false, false); - printWriter.close(); - String xmlDefinition = stringWriter.toString(); - // TODO jbarrett - shouldn't this use the same loadXmlDefinition that other constructors use? - XmlEntity xmlEntity = - XmlEntity.builder().withType(CacheXml.PDX).withConfig(xmlDefinition).build(); - + XmlEntity xmlEntity = createXmlEntity(cache); result = ResultBuilder.buildResult(ird); persistClusterConfiguration(result, () -> getSharedConfiguration().addXmlEntity(xmlEntity, null)); - } catch (Exception e) { return ResultBuilder.createGemFireErrorResult(e.getMessage()); } + return result; } + + /** + * Interceptor to validate command parameters. + */ + public static class Interceptor extends AbstractCliAroundInterceptor { + + @Override + public Result preExecution(GfshParseResult parseResult) { + Object portableClassesPatterns = + parseResult.getParamValue(CliStrings.CONFIGURE_PDX__PORTABLE__AUTO__SERIALIZER__CLASSES); + Object nonPortableClassesPatterns = + parseResult.getParamValue(CliStrings.CONFIGURE_PDX__AUTO__SERIALIZER__CLASSES); + + if ((nonPortableClassesPatterns != null && ((String[]) nonPortableClassesPatterns).length > 0) + && (portableClassesPatterns != null && ((String[]) portableClassesPatterns).length > 0)) { + + return ResultBuilder.createUserErrorResult(CliStrings.CONFIGURE_PDX__ERROR__MESSAGE); + } + + return ResultBuilder.createInfoResult(""); + } + } } diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/i18n/CliStrings.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/i18n/CliStrings.java index fe8650c02125..98f4920d07a3 100644 --- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/i18n/CliStrings.java +++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/i18n/CliStrings.java @@ -3114,11 +3114,12 @@ public class CliStrings { public static final String CONFIGURE_PDX__PORTABLE__AUTO__SERIALIZER__CLASSES = "portable-auto-serializable-classes"; public static final String CONFIGURE_PDX__PORTABLE__AUTO__SERIALIZER__CLASSES__HELP = - "the patterns which are matched against domain class names to determine whether they should be serialized"; + "The patterns that are matched against domain class names to determine whether they should be serialized. Serialization done by the auto-serializer will throw an exception if the object of these classes are not portable to non-java languages (check-portability=true)."; public static final String CONFIGURE_PDX__AUTO__SERIALIZER__CLASSES = "auto-serializable-classes"; public static final String CONFIGURE_PDX__AUTO__SERIALIZER__CLASSES__HELP = - "the patterns which are matched against domain class names to determine whether they should be serialized, serialization done by the auto-serializer will throw an exception if the object of these classes are not portable to non-java languages"; + "The patterns that are matched against domain class names to determine whether they should be auto-serialized. Serialization done by the auto-serializer will not throw an exception if the object of these classes are not portable to non-java languages (check-portability=false)."; + public static final String CONFIGURE_PDX__NORMAL__MEMBERS__WARNING = "The command would only take effect on new data members joining the distributed system. It won't affect the existing data members"; public static final String CONFIGURE_PDX__ERROR__MESSAGE = diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/ConfigurePDXCommandIntegrationTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/ConfigurePDXCommandIntegrationTest.java new file mode 100644 index 000000000000..b1288b0c60e2 --- /dev/null +++ b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/ConfigurePDXCommandIntegrationTest.java @@ -0,0 +1,130 @@ +/* + * 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 static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +import org.apache.geode.test.junit.categories.IntegrationTest; +import org.apache.geode.test.junit.rules.GfshCommandRule; +import org.apache.geode.test.junit.rules.LocatorStarterRule; + +@Category(IntegrationTest.class) +public class ConfigurePDXCommandIntegrationTest { + private static final String BASE_COMMAND_STRING = "configure pdx "; + + @Rule + public GfshCommandRule gfsh = new GfshCommandRule().withTimeout(1); + + @Rule + public LocatorStarterRule locator = new LocatorStarterRule().withAutoStart().withJMXManager(); + + @Before + public void before() throws Exception { + gfsh.connectAndVerify(locator); + } + + @Test + @Ignore("See https://issues.apache.org/jira/browse/GEODE-4794") + public void commandShouldSucceedWhenUsingDefaults() { + gfsh.executeAndAssertThat(BASE_COMMAND_STRING).statusIsSuccess().hasNoFailToPersistError(); + } + + @Test + public void commandShouldFailWhenNotConnected() throws Exception { + gfsh.disconnect(); + gfsh.executeAndAssertThat(BASE_COMMAND_STRING).statusIsError().containsOutput("Command", + "was found but is not currently available"); + } + + @Test + public void commandShouldSucceedWhenConfiguringAutoSerializableClassesWithPersistence() { + gfsh.executeAndAssertThat(BASE_COMMAND_STRING + + "--read-serialized=true --disk-store=myDiskStore --ignore-unread-fields=true --auto-serializable-classes=com.company.DomainObject.*#identity=id") + .statusIsSuccess().hasNoFailToPersistError(); + + String sharedConfigXml = locator.getLocator().getSharedConfiguration() + .getConfiguration("cluster").getCacheXmlContent(); + assertThat(sharedConfigXml).contains( + ""); + assertThat(sharedConfigXml).contains("", + "org.apache.geode.pdx.ReflectionBasedAutoSerializer", + "", + "com.company.DomainObject.*, com.company.DomainObject.*#identity=id", + "", ""); + assertThat(sharedConfigXml).contains(""); + } + + @Test + public void commandShouldSucceedWhenConfiguringAutoSerializableClassesWithoutPersistence() { + gfsh.executeAndAssertThat(BASE_COMMAND_STRING + + "--read-serialized=false --ignore-unread-fields=false --auto-serializable-classes=com.company.DomainObject.*#identity=id") + .statusIsSuccess().hasNoFailToPersistError(); + + String sharedConfigXml = locator.getLocator().getSharedConfiguration() + .getConfiguration("cluster").getCacheXmlContent(); + assertThat(sharedConfigXml).contains(""); + assertThat(sharedConfigXml).contains("", + "org.apache.geode.pdx.ReflectionBasedAutoSerializer", + "", + "com.company.DomainObject.*, com.company.DomainObject.*#identity=id", + "", ""); + assertThat(sharedConfigXml).contains(""); + } + + @Test + public void commandShouldSucceedWhenConfiguringPortableAutoSerializableClassesWithPersistence() { + gfsh.executeAndAssertThat(BASE_COMMAND_STRING + + "--read-serialized=true --disk-store=myDiskStore --ignore-unread-fields=true --portable-auto-serializable-classes=com.company.DomainObject.*#identity=id") + .statusIsSuccess().hasNoFailToPersistError(); + + String sharedConfigXml = locator.getLocator().getSharedConfiguration() + .getConfiguration("cluster").getCacheXmlContent(); + assertThat(sharedConfigXml).contains( + ""); + assertThat(sharedConfigXml).contains("") + .contains("true").contains(""); + assertThat(sharedConfigXml).contains("", + "org.apache.geode.pdx.ReflectionBasedAutoSerializer", + "", + "com.company.DomainObject.*, com.company.DomainObject.*#identity=id", + "", ""); + assertThat(sharedConfigXml).contains(""); + } + + @Test + public void commandShouldSucceedWhenConfiguringPortableAutoSerializableClassesWithoutPersistence() { + gfsh.executeAndAssertThat(BASE_COMMAND_STRING + + "--read-serialized=false --ignore-unread-fields=false --portable-auto-serializable-classes=com.company.DomainObject.*#identity=id") + .statusIsSuccess().hasNoFailToPersistError(); + + String sharedConfigXml = locator.getLocator().getSharedConfiguration() + .getConfiguration("cluster").getCacheXmlContent(); + assertThat(sharedConfigXml).contains(""); + assertThat(sharedConfigXml).contains("") + .contains("true").contains(""); + assertThat(sharedConfigXml).contains("", + "org.apache.geode.pdx.ReflectionBasedAutoSerializer", + "", + "com.company.DomainObject.*, com.company.DomainObject.*#identity=id", + "", ""); + assertThat(sharedConfigXml).contains(""); + } +} diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/ConfigurePDXCommandTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/ConfigurePDXCommandTest.java new file mode 100644 index 000000000000..bfd5a61716c0 --- /dev/null +++ b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/ConfigurePDXCommandTest.java @@ -0,0 +1,379 @@ +/* + * 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 static org.apache.geode.internal.cache.CacheConfig.DEFAULT_PDX_IGNORE_UNREAD_FIELDS; +import static org.apache.geode.internal.cache.CacheConfig.DEFAULT_PDX_PERSISTENT; +import static org.apache.geode.internal.cache.CacheConfig.DEFAULT_PDX_READ_SERIALIZED; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import org.apache.commons.lang.StringUtils; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +import org.apache.geode.distributed.DistributedMember; +import org.apache.geode.distributed.internal.ClusterConfigurationService; +import org.apache.geode.internal.cache.CacheConfig; +import org.apache.geode.internal.cache.InternalCache; +import org.apache.geode.internal.cache.xmlcache.CacheCreation; +import org.apache.geode.management.internal.cli.result.CommandResult; +import org.apache.geode.management.internal.configuration.domain.XmlEntity; +import org.apache.geode.test.junit.assertions.CommandResultAssert; +import org.apache.geode.test.junit.categories.UnitTest; +import org.apache.geode.test.junit.rules.GfshParserRule; + +@Category(UnitTest.class) +public class ConfigurePDXCommandTest { + private static final String BASE_COMMAND_STRING = "configure pdx "; + + @ClassRule + public static CustomGfshParserRule gfshParserRule = new CustomGfshParserRule(); + + private InternalCache cache; + private XmlEntity xmlEntity; + private CacheCreation cacheCreation; + private ConfigurePDXCommand command; + private ClusterConfigurationService clusterConfigurationService; + + @Before + public void setUp() throws Exception { + cache = mock(InternalCache.class); + xmlEntity = mock(XmlEntity.class); + command = spy(ConfigurePDXCommand.class); + cacheCreation = spy(CacheCreation.class); + clusterConfigurationService = mock(ClusterConfigurationService.class); + + doReturn(cache).when(command).getCache(); + doReturn(xmlEntity).when(command).createXmlEntity(any()); + doReturn(cacheCreation).when(command).getCacheCreation(anyBoolean()); + doReturn(Collections.emptySet()).when(command).getAllNormalMembers(); + doReturn(clusterConfigurationService).when(command).getSharedConfiguration(); + } + + @Test + public void parsingShouldSucceedWithoutArguments() { + assertThat(gfshParserRule.parse(BASE_COMMAND_STRING)).isNotNull(); + } + + @Test + public void parsingAutoCompleteShouldSucceed() throws Exception { + GfshParserRule.CommandCandidate candidate = gfshParserRule.complete(BASE_COMMAND_STRING); + + assertThat(candidate).isNotNull(); + assertThat(candidate.getCandidates()).isNotNull(); + assertThat(candidate.getCandidates().size()).isEqualTo(5); + } + + @Test + public void executionShouldHandleInternalFailures() { + doThrow(new RuntimeException("Can't create CacheCreation.")).when(command) + .getCacheCreation(anyBoolean()); + gfshParserRule.executeAndAssertThat(command, BASE_COMMAND_STRING).statusIsError() + .containsOutput("Could not process command due to error. Can't create CacheCreation."); + doReturn(cacheCreation).when(command).getCacheCreation(anyBoolean()); + + doThrow(new RuntimeException("Can't find members.")).when(command).getAllNormalMembers(); + gfshParserRule.executeAndAssertThat(command, BASE_COMMAND_STRING).statusIsError() + .containsOutput("Could not process command due to error. Can't find members."); + doReturn(Collections.emptySet()).when(command).getAllNormalMembers(); + + doThrow(new RuntimeException("Can't create XmlEntity.")).when(command).createXmlEntity(any()); + gfshParserRule.executeAndAssertThat(command, BASE_COMMAND_STRING).statusIsError() + .containsOutput("Could not process command due to error. Can't create XmlEntity."); + doReturn(xmlEntity).when(command).createXmlEntity(any()); + + doThrow(new RuntimeException("Can't create ReflectionBasedAutoSerializer.")).when(command) + .createReflectionBasedAutoSerializer(anyBoolean(), any()); + gfshParserRule + .executeAndAssertThat(command, + BASE_COMMAND_STRING + "--auto-serializable-classes=" + new String[] {}) + .statusIsError().containsOutput( + "Could not process command due to error. Can't create ReflectionBasedAutoSerializer."); + gfshParserRule + .executeAndAssertThat(command, + BASE_COMMAND_STRING + "--portable-auto-serializable-classes=" + new String[] {}) + .statusIsError().containsOutput( + "Could not process command due to error. Can't create ReflectionBasedAutoSerializer."); + + verify(command, times(0)).persistClusterConfiguration(any(), any()); + } + + @Test + public void executionShouldFailIfBothPortableAndNonPortableClassesParametersAreSpecifiedAtTheSameTime() { + gfshParserRule + .executeAndAssertThat(command, + BASE_COMMAND_STRING + + "--auto-serializable-classes=org.apache.geode --portable-auto-serializable-classes=org.apache.geode") + .statusIsError().containsOutput( + "The autoserializer cannot support both portable and non-portable classes at the same time."); + + verify(command, times(0)).persistClusterConfiguration(any(), any()); + } + + @Test + public void executionShouldIncludeWarningMessageWhenThereAreMembersAlreadyRunning() { + Set members = new HashSet<>(); + DistributedMember mockMember = mock(DistributedMember.class); + when(mockMember.getId()).thenReturn("member0"); + members.add(mockMember); + doReturn(xmlEntity).when(command).createXmlEntity(any()); + doReturn(members).when(command).getAllNormalMembers(); + + gfshParserRule.executeAndAssertThat(command, BASE_COMMAND_STRING).statusIsSuccess() + .hasDefaultsConfigured(command, cacheCreation).containsOutput( + "The command would only take effect on new data members joining the distributed system. It won't affect the existing data members"); + } + + @Test + public void executionShouldWorkCorrectlyWhenDefaultsAreUsed() { + // Factory Default + gfshParserRule.executeAndAssertThat(command, BASE_COMMAND_STRING).statusIsSuccess() + .hasNoFailToPersistError().hasDefaultsConfigured(command, cacheCreation); + } + + @Test + public void executionShouldCorrectlyConfigurePersistenceWhenDefaultDiskStoreIsUsed() { + // Default Disk Store + gfshParserRule.executeAndAssertThat(command, BASE_COMMAND_STRING + "--disk-store") + .statusIsSuccess().hasNoFailToPersistError() + .hasReadSerializedConfigured(DEFAULT_PDX_READ_SERIALIZED, cacheCreation) + .hasIgnoreUnreadFieldsConfigured(DEFAULT_PDX_IGNORE_UNREAD_FIELDS, cacheCreation) + .hasPersistenseConfigured(true, "DEFAULT", cacheCreation); + + verify(cacheCreation, times(0)).setPdxSerializer(any()); + verify(command, times(1)).persistClusterConfiguration(any(), any()); + verify(command, times(0)).createReflectionBasedAutoSerializer(anyBoolean(), any()); + } + + @Test + public void executionShouldCorrectlyConfigurePersistenceWhenCustomDiskStoreIsUsed() { + // Custom Disk Store + gfshParserRule.executeAndAssertThat(command, BASE_COMMAND_STRING + "--disk-store=myDiskStore") + .statusIsSuccess().hasNoFailToPersistError() + .hasReadSerializedConfigured(DEFAULT_PDX_READ_SERIALIZED, cacheCreation) + .hasIgnoreUnreadFieldsConfigured(DEFAULT_PDX_IGNORE_UNREAD_FIELDS, cacheCreation) + .hasPersistenseConfigured(true, "myDiskStore", cacheCreation); + + verify(cacheCreation, times(0)).setPdxSerializer(any()); + verify(command, times(1)).persistClusterConfiguration(any(), any()); + verify(command, times(0)).createReflectionBasedAutoSerializer(anyBoolean(), any()); + } + + @Test + public void executionShouldCorrectlyConfigureReadSerializedWhenFlagIsSetAsTrue() { + // Custom Configuration as True + gfshParserRule.executeAndAssertThat(command, BASE_COMMAND_STRING + "--read-serialized=true") + .statusIsSuccess().hasNoFailToPersistError() + .hasReadSerializedConfigured(true, cacheCreation) + .hasIgnoreUnreadFieldsConfigured(DEFAULT_PDX_IGNORE_UNREAD_FIELDS, cacheCreation) + .hasPersistenseConfigured(DEFAULT_PDX_PERSISTENT, null, cacheCreation); + + verify(cacheCreation, times(0)).setPdxSerializer(any()); + verify(command, times(1)).persistClusterConfiguration(any(), any()); + verify(command, times(0)).createReflectionBasedAutoSerializer(anyBoolean(), any()); + } + + @Test + public void executionShouldCorrectlyConfigureReadSerializedWhenFlagIsSetAsFalse() { + // Custom Configuration as False + gfshParserRule.executeAndAssertThat(command, BASE_COMMAND_STRING + "--read-serialized=false") + .statusIsSuccess().hasNoFailToPersistError() + .hasReadSerializedConfigured(false, cacheCreation) + .hasIgnoreUnreadFieldsConfigured(DEFAULT_PDX_IGNORE_UNREAD_FIELDS, cacheCreation) + .hasPersistenseConfigured(DEFAULT_PDX_PERSISTENT, null, cacheCreation); + + verify(cacheCreation, times(0)).setPdxSerializer(any()); + verify(command, times(1)).persistClusterConfiguration(any(), any()); + verify(command, times(0)).createReflectionBasedAutoSerializer(anyBoolean(), any()); + } + + @Test + public void executionShouldCorrectlyConfigureIgnoreUnreadFieldsWhenFlagIsSetAsTrue() { + // Custom Configuration as True + gfshParserRule + .executeAndAssertThat(command, BASE_COMMAND_STRING + "--ignore-unread-fields=true") + .statusIsSuccess().hasNoFailToPersistError() + .hasReadSerializedConfigured(DEFAULT_PDX_READ_SERIALIZED, cacheCreation) + .hasIgnoreUnreadFieldsConfigured(true, cacheCreation) + .hasPersistenseConfigured(DEFAULT_PDX_PERSISTENT, null, cacheCreation); + + verify(cacheCreation, times(0)).setPdxSerializer(any()); + verify(command, times(1)).persistClusterConfiguration(any(), any()); + verify(command, times(0)).createReflectionBasedAutoSerializer(anyBoolean(), any()); + } + + @Test + public void executionShouldCorrectlyConfigureIgnoreUnreadFieldsWhenFlagIsSetAsFalse() { + // Custom Configuration as False + gfshParserRule + .executeAndAssertThat(command, BASE_COMMAND_STRING + "--ignore-unread-fields=false") + .statusIsSuccess().hasNoFailToPersistError() + .hasReadSerializedConfigured(DEFAULT_PDX_READ_SERIALIZED, cacheCreation) + .hasIgnoreUnreadFieldsConfigured(false, cacheCreation) + .hasPersistenseConfigured(DEFAULT_PDX_PERSISTENT, null, cacheCreation); + + verify(cacheCreation, times(0)).setPdxSerializer(any()); + verify(command, times(1)).persistClusterConfiguration(any(), any()); + verify(command, times(0)).createReflectionBasedAutoSerializer(anyBoolean(), any()); + } + + @Test + public void executionShouldCorrectlyConfigurePortableAutoSerializableClassesWhenUsingCustomPattern() { + String[] patterns = new String[] {"com.company.DomainObject.*#identity=id"}; + + // Custom Settings + gfshParserRule + .executeAndAssertThat(command, + BASE_COMMAND_STRING + "--portable-auto-serializable-classes=" + patterns[0]) + .statusIsSuccess().hasNoFailToPersistError() + .hasReadSerializedConfigured(DEFAULT_PDX_READ_SERIALIZED, cacheCreation) + .hasIgnoreUnreadFieldsConfigured(DEFAULT_PDX_IGNORE_UNREAD_FIELDS, cacheCreation) + .hasPersistenseConfigured(DEFAULT_PDX_PERSISTENT, null, cacheCreation) + .containsOutput("Portable Classes = [com.company.DomainObject.*#identity=id]") + .containsOutput("PDX Serializer = org.apache.geode.pdx.ReflectionBasedAutoSerializer"); + + verify(cacheCreation, times(1)).setPdxSerializer(any()); + verify(command, times(1)).persistClusterConfiguration(any(), any()); + verify(command, times(1)).createReflectionBasedAutoSerializer(true, patterns); + } + + @Test + public void executionShouldCorrectlyConfigureAutoSerializableClassesWhenUsingCustomPattern() { + String[] patterns = new String[] {"com.company.DomainObject.*#identity=id"}; + + // Custom Settings + gfshParserRule + .executeAndAssertThat(command, + BASE_COMMAND_STRING + "--auto-serializable-classes=" + patterns[0]) + .statusIsSuccess().hasNoFailToPersistError() + .hasReadSerializedConfigured(CacheConfig.DEFAULT_PDX_READ_SERIALIZED, cacheCreation) + .hasIgnoreUnreadFieldsConfigured(CacheConfig.DEFAULT_PDX_IGNORE_UNREAD_FIELDS, + cacheCreation) + .hasPersistenseConfigured(CacheConfig.DEFAULT_PDX_PERSISTENT, null, cacheCreation) + .containsOutput("Non Portable Classes = [com.company.DomainObject.*#identity=id]") + .containsOutput("PDX Serializer = org.apache.geode.pdx.ReflectionBasedAutoSerializer"); + + verify(cacheCreation, times(1)).setPdxSerializer(any()); + verify(command, times(1)).persistClusterConfiguration(any(), any()); + verify(command, times(1)).createReflectionBasedAutoSerializer(false, patterns); + } + + static class CustomGfshParserRule extends GfshParserRule { + @Override + public CustomResultAssert executeAndAssertThat(T instance, String command) { + CommandResultAssert resultAssert = super.executeAndAssertThat(instance, command);; + + return new CustomResultAssert(resultAssert.getCommandResult()); + } + } + + static class CustomResultAssert extends CommandResultAssert { + public CustomResultAssert(CommandResult commandResult) { + super(commandResult); + } + + @Override + public CustomResultAssert statusIsError() { + super.statusIsError(); + + return this; + } + + @Override + public CustomResultAssert statusIsSuccess() { + super.statusIsSuccess(); + + return this; + } + + @Override + public CustomResultAssert containsOutput(String... expectedOutputs) { + super.containsOutput(expectedOutputs); + + return this; + } + + @Override + public CustomResultAssert hasNoFailToPersistError() { + super.hasNoFailToPersistError(); + + return this; + } + + public CustomResultAssert hasPersistenseConfigured(boolean persistenceEnabled, + String diskStoreName, CacheCreation cache) { + assertThat(actual.getOutput()).contains("persistent = " + persistenceEnabled); + + if (StringUtils.isNotEmpty(diskStoreName)) { + assertThat(actual.getOutput()).contains("disk-store = " + diskStoreName); + } + + if (persistenceEnabled) { + verify(cache, times(1)).setPdxPersistent(true); + } else { + verify(cache, times(0)).setPdxPersistent(true); + } + + return this; + } + + + public CustomResultAssert hasReadSerializedConfigured(boolean readSerializedEnabled, + CacheCreation cache) { + assertThat(actual.getOutput()).contains("read-serialized = " + readSerializedEnabled); + verify(cache, times(1)).setPdxReadSerialized(readSerializedEnabled); + + return this; + } + + public CustomResultAssert hasIgnoreUnreadFieldsConfigured(boolean ignoreUnreadFieldsEnabled, + CacheCreation cache) { + assertThat(actual.getOutput()) + .contains("ignore-unread-fields = " + ignoreUnreadFieldsEnabled); + verify(cache, times(1)).setPdxIgnoreUnreadFields(ignoreUnreadFieldsEnabled); + + return this; + } + + public CustomResultAssert hasDefaultsConfigured(ConfigurePDXCommand command, + CacheCreation cacheCreation) { + hasNoFailToPersistError(); + hasReadSerializedConfigured(DEFAULT_PDX_READ_SERIALIZED, cacheCreation); + hasIgnoreUnreadFieldsConfigured(DEFAULT_PDX_IGNORE_UNREAD_FIELDS, cacheCreation); + hasPersistenseConfigured(DEFAULT_PDX_PERSISTENT, null, cacheCreation); + + verify(cacheCreation, times(0)).setPdxSerializer(any()); + verify(command, times(1)).persistClusterConfiguration(any(), any()); + verify(command, times(0)).createReflectionBasedAutoSerializer(anyBoolean(), any()); + + return this; + } + } +}