This is an automated email from the ASF dual-hosted git repository. jensdeppe pushed a commit to branch develop in repository https://gitbox.apache.org/repos/asf/geode.git
The following commit(s) were added to refs/heads/develop by this push: new 092f598 GEODE-6384: Create consistent API to retrieve instances of ClusterManagementService (#3191) 092f598 is described below commit 092f5981c79b10ca5c6da642d1df8348f235f41c Author: Jens Deppe <jde...@pivotal.io> AuthorDate: Fri Feb 22 05:17:36 2019 -0800 GEODE-6384: Create consistent API to retrieve instances of ClusterManagementService (#3191) - The overall intent of this commit is to provide a consistent means for retrieving a ClusterMangementService instance regardless of the context you are developing in. Currently, the relevant contexts are: 1) a Java application that has no access (or need to access) core Geode code. 2) a typical Geode client application and 3) server or locator-side code. - Introduces a service provider interface (ClusterManagementServiceProviderFactory) whose implementations are context dependent. This commit introduces two implementations, namely: BasicClusterManagementProviderFactory and SmartClusterManagementServiceProviderFactory). The latter is incomplete but will be fully supported in the near future. - The entry point to creating a ClusterManagementService is in ClusterManagementServiceProvider - more documentation can be found there. --- geode-assembly/build.gradle | 1 + .../geode/test/junit/rules/HttpResponseAssert.java | 2 +- ...ClusterManagementLocatorReconnectDunitTest.java | 2 +- .../internal/rest/RegionManagementDunitTest.java | 40 ++++- .../internal/DisabledClusterConfigTest.java | 2 +- .../integrationTest/resources/assembly_content.txt | 17 ++ ...ClusterManagementServiceRetrievalDUnitTest.java | 53 ++++++ .../internal/api/RegionAPIDUnitTest.java | 1 + .../commands/DescribeMembersCommandDUnitTest.java | 42 ++++- .../distributed/internal/InternalLocator.java | 2 +- .../internal/cache/ClusterConfigurationLoader.java | 1 - ...odeClusterManagementServiceProviderFactory.java | 124 ++++++++++++++ .../api/LocatorClusterManagementService.java | 2 + .../cli/commands/DescribeMemberCommand.java | 5 - .../internal/cli/domain/MemberInformation.java | 27 ++-- .../functions/GetMemberInformationFunction.java | 21 ++- .../internal/cli/util/MemberInformation.java | 180 --------------------- ...ent.spi.ClusterManagementServiceProviderFactory | 16 ++ .../sanctioned-geode-core-serializables.txt | 5 +- .../internal/api/ClusterManagementResultTest.java | 2 + .../api/LocatorClusterManagementServiceTest.java | 1 + .../geode/test/dunit/rules/ClusterStartupRule.java | 23 ++- geode-management/build.gradle | 1 + .../geode/cache/configuration/RegionConfig.java | 8 +- .../api/ClusterManagementResult.java | 2 +- .../api/ClusterManagementService.java | 38 ++++- .../api/Status.java => api/RestfulEndpoint.java} | 29 +--- .../management/{internal => }/api/Status.java | 2 +- .../client/ClusterManagementServiceProvider.java | 119 ++++++++++++++ .../client/RestTemplateResponseErrorHandler.java | 25 +-- .../internal/ClusterManagementClient.java | 105 ++++++++++++ .../ClusterManagementServiceProviderFactory.java | 60 +++++++ ...JavaClientClusterManagementProviderFactory.java | 54 +++++++ ...ent.spi.ClusterManagementServiceProviderFactory | 17 ++ .../src/test/resources/expected-pom.xml | 5 + geode-web-management/build.gradle | 4 + ...xtLoader.java => BaseLocatorContextLoader.java} | 17 +- .../LocatorWithSecurityManagerContextLoader.java | 31 ++-- .../internal/rest/PlainLocatorContextLoader.java | 36 ++--- .../rest/StandardRequestPostProcessor.java | 2 +- .../client/ClusterManagementClientDUnitTest.java | 87 ++++++++++ .../rest/RegionManagementIntegrationTest.java | 3 +- .../controllers/ManagementControllerAdvice.java | 4 +- .../controllers/RegionManagementController.java | 2 +- .../rest/security/RestSecurityConfiguration.java | 2 +- 45 files changed, 900 insertions(+), 322 deletions(-) diff --git a/geode-assembly/build.gradle b/geode-assembly/build.gradle index 1a1da28..c5d8510 100755 --- a/geode-assembly/build.gradle +++ b/geode-assembly/build.gradle @@ -189,6 +189,7 @@ dependencies { distributedTestCompile(project(':extensions:session-testing-war')) distributedTestCompile(project(':geode-assembly:geode-assembly-test')) distributedTestCompile('org.apache.httpcomponents:httpclient') + distributedTestCompile('org.springframework:spring-web') distributedTestRuntime(project(':extensions:geode-modules-session-internal')) { exclude group: 'org.apache.tomcat' diff --git a/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/test/junit/rules/HttpResponseAssert.java b/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/test/junit/rules/HttpResponseAssert.java index 63e0c61..5ff00de 100644 --- a/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/test/junit/rules/HttpResponseAssert.java +++ b/geode-assembly/geode-assembly-test/src/main/java/org/apache/geode/test/junit/rules/HttpResponseAssert.java @@ -33,7 +33,7 @@ import org.assertj.core.api.AbstractCharSequenceAssert; import org.assertj.core.api.ListAssert; import org.apache.geode.internal.logging.LogService; -import org.apache.geode.management.internal.api.ClusterManagementResult; +import org.apache.geode.management.api.ClusterManagementResult; public class HttpResponseAssert extends AbstractAssert<HttpResponseAssert, HttpResponse> { private static final Logger logger = LogService.getLogger(); diff --git a/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/rest/ClusterManagementLocatorReconnectDunitTest.java b/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/rest/ClusterManagementLocatorReconnectDunitTest.java index 846c162..7d70f08 100644 --- a/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/rest/ClusterManagementLocatorReconnectDunitTest.java +++ b/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/rest/ClusterManagementLocatorReconnectDunitTest.java @@ -28,7 +28,7 @@ import org.apache.geode.cache.Region; import org.apache.geode.cache.configuration.CacheConfig; import org.apache.geode.cache.configuration.CacheElement; import org.apache.geode.cache.configuration.RegionConfig; -import org.apache.geode.management.internal.api.ClusterManagementResult; +import org.apache.geode.management.api.ClusterManagementResult; import org.apache.geode.test.dunit.IgnoredException; import org.apache.geode.test.dunit.rules.ClusterStartupRule; import org.apache.geode.test.dunit.rules.MemberVM; diff --git a/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/rest/RegionManagementDunitTest.java b/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/rest/RegionManagementDunitTest.java index 679eaea..db5263a 100644 --- a/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/rest/RegionManagementDunitTest.java +++ b/geode-assembly/src/distributedTest/java/org/apache/geode/management/internal/rest/RegionManagementDunitTest.java @@ -21,13 +21,15 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Test; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.RestTemplate; import org.apache.geode.cache.Cache; import org.apache.geode.cache.Region; import org.apache.geode.cache.configuration.CacheConfig; import org.apache.geode.cache.configuration.CacheElement; import org.apache.geode.cache.configuration.RegionConfig; -import org.apache.geode.management.internal.api.ClusterManagementResult; +import org.apache.geode.management.api.ClusterManagementResult; import org.apache.geode.test.dunit.IgnoredException; import org.apache.geode.test.dunit.rules.ClusterStartupRule; import org.apache.geode.test.dunit.rules.MemberVM; @@ -78,6 +80,42 @@ public class RegionManagementDunitTest { } @Test + public void createsRegionUsingClusterManagementClient() throws Exception { + RegionConfig regionConfig = new RegionConfig(); + regionConfig.setName("customers2"); + regionConfig.setType("REPLICATE"); + ObjectMapper mapper = new ObjectMapper(); + String json = mapper.writeValueAsString(regionConfig); + + String url = String.format("http://localhost:%d/geode-management/v2/regions", + locator.getHttpPort()); + RestTemplate template = new RestTemplate(); + + ResponseEntity<ClusterManagementResult> result = + template.postForEntity(url, regionConfig, ClusterManagementResult.class); + + result.getBody(); + + // ClusterManagementResult result = + // restClient.doPostAndAssert("/regions", json) + // .hasStatusCode(201) + // .getClusterManagementResult(); + // + // assertThat(result.isSuccessfullyAppliedOnMembers()).isTrue(); + // assertThat(result.isSuccessfullyPersisted()).isTrue(); + // assertThat(result.getMemberStatuses()).containsKeys("server-1").hasSize(1); + // + // // make sure region is created + // server.invoke(() -> verifyRegionCreated("customers", "REPLICATE")); + // + // // make sure region is persisted + // locator.invoke(() -> verifyRegionPersisted("customers", "REPLICATE")); + // + // // verify that additional server can be started with the cluster configuration + // cluster.startServerVM(2, locator.getPort()); + } + + @Test public void createsAPartitionedRegionByDefault() throws Exception { String json = "{\"name\": \"orders\"}"; diff --git a/geode-assembly/src/integrationTest/java/org/apache/geode/management/internal/DisabledClusterConfigTest.java b/geode-assembly/src/integrationTest/java/org/apache/geode/management/internal/DisabledClusterConfigTest.java index f596830..d7af562 100644 --- a/geode-assembly/src/integrationTest/java/org/apache/geode/management/internal/DisabledClusterConfigTest.java +++ b/geode-assembly/src/integrationTest/java/org/apache/geode/management/internal/DisabledClusterConfigTest.java @@ -22,7 +22,7 @@ import org.junit.Rule; import org.junit.Test; import org.apache.geode.distributed.ConfigurationProperties; -import org.apache.geode.management.internal.api.ClusterManagementResult; +import org.apache.geode.management.api.ClusterManagementResult; import org.apache.geode.test.junit.rules.GeodeDevRestClient; import org.apache.geode.test.junit.rules.LocatorStarterRule; import org.apache.geode.test.junit.rules.RequiresGeodeHome; diff --git a/geode-assembly/src/integrationTest/resources/assembly_content.txt b/geode-assembly/src/integrationTest/resources/assembly_content.txt index ab83cdd..c2569de 100644 --- a/geode-assembly/src/integrationTest/resources/assembly_content.txt +++ b/geode-assembly/src/integrationTest/resources/assembly_content.txt @@ -676,6 +676,13 @@ javadoc/org/apache/geode/management/PersistentMemberDetails.html javadoc/org/apache/geode/management/RegionAttributesData.html javadoc/org/apache/geode/management/RegionMXBean.html javadoc/org/apache/geode/management/ServerLoadData.html +javadoc/org/apache/geode/management/api/ClusterManagementResult.html +javadoc/org/apache/geode/management/api/ClusterManagementService.html +javadoc/org/apache/geode/management/api/RestfulEndpoint.html +javadoc/org/apache/geode/management/api/Status.html +javadoc/org/apache/geode/management/api/package-frame.html +javadoc/org/apache/geode/management/api/package-summary.html +javadoc/org/apache/geode/management/api/package-tree.html javadoc/org/apache/geode/management/cli/CliFunction.html javadoc/org/apache/geode/management/cli/CliMetaData.AvailabilityMetadata.html javadoc/org/apache/geode/management/cli/CliMetaData.html @@ -693,6 +700,11 @@ javadoc/org/apache/geode/management/cli/UpdateAllConfigurationGroupsMarker.html javadoc/org/apache/geode/management/cli/package-frame.html javadoc/org/apache/geode/management/cli/package-summary.html javadoc/org/apache/geode/management/cli/package-tree.html +javadoc/org/apache/geode/management/client/ClusterManagementServiceProvider.html +javadoc/org/apache/geode/management/client/RestTemplateResponseErrorHandler.html +javadoc/org/apache/geode/management/client/package-frame.html +javadoc/org/apache/geode/management/client/package-summary.html +javadoc/org/apache/geode/management/client/package-tree.html javadoc/org/apache/geode/management/membership/ClientMembership.html javadoc/org/apache/geode/management/membership/ClientMembershipEvent.html javadoc/org/apache/geode/management/membership/ClientMembershipListener.html @@ -707,6 +719,11 @@ javadoc/org/apache/geode/management/membership/package-tree.html javadoc/org/apache/geode/management/package-frame.html javadoc/org/apache/geode/management/package-summary.html javadoc/org/apache/geode/management/package-tree.html +javadoc/org/apache/geode/management/spi/ClusterManagementServiceProviderFactory.html +javadoc/org/apache/geode/management/spi/JavaClientClusterManagementProviderFactory.html +javadoc/org/apache/geode/management/spi/package-frame.html +javadoc/org/apache/geode/management/spi/package-summary.html +javadoc/org/apache/geode/management/spi/package-tree.html javadoc/org/apache/geode/memcached/GemFireMemcachedServer.Protocol.html javadoc/org/apache/geode/memcached/GemFireMemcachedServer.html javadoc/org/apache/geode/memcached/package-frame.html diff --git a/geode-core/src/distributedTest/java/org/apache/geode/management/api/ClusterManagementServiceRetrievalDUnitTest.java b/geode-core/src/distributedTest/java/org/apache/geode/management/api/ClusterManagementServiceRetrievalDUnitTest.java new file mode 100644 index 0000000..708da0c --- /dev/null +++ b/geode-core/src/distributedTest/java/org/apache/geode/management/api/ClusterManagementServiceRetrievalDUnitTest.java @@ -0,0 +1,53 @@ +/* + * 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.api; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.ClassRule; +import org.junit.Test; +import org.springframework.web.util.AbstractUriTemplateHandler; + +import org.apache.geode.management.client.ClusterManagementServiceProvider; +import org.apache.geode.management.internal.ClusterManagementClient; +import org.apache.geode.test.dunit.rules.ClusterStartupRule; +import org.apache.geode.test.dunit.rules.MemberVM; + +public class ClusterManagementServiceRetrievalDUnitTest { + + private static MemberVM locator; + private static MemberVM server1; + + @ClassRule + public static ClusterStartupRule cluster = new ClusterStartupRule(); + + @Test + public void retrieveClusterManagementServiceFromServer() { + locator = cluster.startLocatorVM(0, + x -> x.withHttpService()); + server1 = cluster.startServerVM(1, locator.getPort()); + + final String url = String.format("http://localhost:%d", locator.getHttpPort()); + server1.invoke(() -> { + ClusterManagementClient cms = + (ClusterManagementClient) ClusterManagementServiceProvider.getService(); + assertThat( + ((AbstractUriTemplateHandler) cms.getRestTemplate().getUriTemplateHandler()).getBaseUrl()) + .isEqualTo(url); + }); + } + +} diff --git a/geode-core/src/distributedTest/java/org/apache/geode/management/internal/api/RegionAPIDUnitTest.java b/geode-core/src/distributedTest/java/org/apache/geode/management/internal/api/RegionAPIDUnitTest.java index 0c2fec2..8633015 100644 --- a/geode-core/src/distributedTest/java/org/apache/geode/management/internal/api/RegionAPIDUnitTest.java +++ b/geode-core/src/distributedTest/java/org/apache/geode/management/internal/api/RegionAPIDUnitTest.java @@ -29,6 +29,7 @@ import org.apache.geode.cache.RegionShortcut; import org.apache.geode.cache.configuration.CacheConfig; import org.apache.geode.cache.configuration.CacheElement; import org.apache.geode.cache.configuration.RegionConfig; +import org.apache.geode.management.api.ClusterManagementResult; import org.apache.geode.test.dunit.rules.ClusterStartupRule; import org.apache.geode.test.dunit.rules.MemberVM; import org.apache.geode.test.junit.categories.RegionsTest; diff --git a/geode-core/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/DescribeMembersCommandDUnitTest.java b/geode-core/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/DescribeMembersCommandDUnitTest.java index 942fd49..b6c61fb 100644 --- a/geode-core/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/DescribeMembersCommandDUnitTest.java +++ b/geode-core/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/DescribeMembersCommandDUnitTest.java @@ -15,6 +15,9 @@ package org.apache.geode.management.internal.cli.commands; import static org.apache.geode.management.internal.cli.i18n.CliStrings.DESCRIBE_MEMBER; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Map; import org.junit.BeforeClass; import org.junit.ClassRule; @@ -22,6 +25,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; +import org.apache.geode.management.internal.cli.result.CommandResult; import org.apache.geode.test.dunit.rules.ClusterStartupRule; import org.apache.geode.test.dunit.rules.MemberVM; import org.apache.geode.test.junit.categories.JMXTest; @@ -58,14 +62,44 @@ public class DescribeMembersCommandDUnitTest { @Test public void describeLocator() throws Exception { gfsh.connectAndVerify(locator); - gfsh.executeAndAssertThat(DESCRIBE_MEMBER + " --name=locator-0").statusIsSuccess() - .containsOutput("locator-0").doesNotContainOutput("server-1"); + CommandResult result = gfsh.executeAndAssertThat(DESCRIBE_MEMBER + " --name=locator-0") + .statusIsSuccess() + .getCommandResult(); + + Map<String, String> memberInfo = result.getMapFromSection("0"); + assertThat(memberInfo.get("Name")).isEqualTo("locator-0"); + assertThat(memberInfo.get("Id")).contains("locator-0"); + assertThat(memberInfo.get("Host")).as("Host").isNotBlank(); + assertThat(memberInfo.get("PID")).as("PID").isNotBlank(); + assertThat(memberInfo.get("Used Heap")).as("Used Heap").isNotBlank(); + assertThat(memberInfo.get("Max Heap")).as("Max Heap").isNotBlank(); + assertThat(memberInfo.get("Working Dir")).as("Working Dir").isNotBlank(); + assertThat(memberInfo.get("Log file")).as("Log File").isNotBlank(); + assertThat(memberInfo.get("Locators")).as("Locators").isNotBlank(); } @Test public void describeServer() throws Exception { gfsh.connectAndVerify(locator); - gfsh.executeAndAssertThat(DESCRIBE_MEMBER + " --name=server-1").statusIsSuccess() - .doesNotContainOutput("locator-0").containsOutput("server-1"); + CommandResult result = gfsh.executeAndAssertThat(DESCRIBE_MEMBER + " --name=server-1") + .statusIsSuccess() + .getCommandResult(); + + Map<String, String> memberInfo = result.getMapFromSection("0"); + assertThat(memberInfo.get("Name")).isEqualTo("server-1"); + assertThat(memberInfo.get("Id")).contains("server-1"); + assertThat(memberInfo.get("Host")).as("Host").isNotBlank(); + assertThat(memberInfo.get("PID")).as("PID").isNotBlank(); + assertThat(memberInfo.get("Used Heap")).as("Used Heap").isNotBlank(); + assertThat(memberInfo.get("Max Heap")).as("Max Heap").isNotBlank(); + assertThat(memberInfo.get("Working Dir")).as("Working Dir").isNotBlank(); + assertThat(memberInfo.get("Log file")).as("Log File").isNotBlank(); + assertThat(memberInfo.get("Locators")).as("Locators").isNotBlank(); + + Map<String, String> cacheServerInfo = result.getMapFromSection("1"); + assertThat(cacheServerInfo.get("Server Bind")).as("Server Bind").isBlank(); + assertThat(cacheServerInfo.get("Server Port")).as("Server Port").isNotBlank(); + assertThat(cacheServerInfo.get("Running")).as("Running").isEqualTo("true"); + assertThat(cacheServerInfo.get("Client Connections")).as("Client Connections").isEqualTo("0"); } } diff --git a/geode-core/src/main/java/org/apache/geode/distributed/internal/InternalLocator.java b/geode-core/src/main/java/org/apache/geode/distributed/internal/InternalLocator.java index 03cf0e6..28bd0bd 100644 --- a/geode-core/src/main/java/org/apache/geode/distributed/internal/InternalLocator.java +++ b/geode-core/src/main/java/org/apache/geode/distributed/internal/InternalLocator.java @@ -83,10 +83,10 @@ import org.apache.geode.internal.logging.NullLoggingSession; import org.apache.geode.internal.net.SocketCreator; import org.apache.geode.internal.net.SocketCreatorFactory; import org.apache.geode.internal.statistics.StatisticsConfig; +import org.apache.geode.management.api.ClusterManagementService; import org.apache.geode.management.internal.AgentUtil; import org.apache.geode.management.internal.JmxManagerLocator; import org.apache.geode.management.internal.JmxManagerLocatorRequest; -import org.apache.geode.management.internal.api.ClusterManagementService; import org.apache.geode.management.internal.api.LocatorClusterManagementService; import org.apache.geode.management.internal.configuration.domain.SharedConfigurationStatus; import org.apache.geode.management.internal.configuration.handlers.SharedConfigurationStatusRequestHandler; diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/ClusterConfigurationLoader.java b/geode-core/src/main/java/org/apache/geode/internal/cache/ClusterConfigurationLoader.java index 7780600..3e15b00 100644 --- a/geode-core/src/main/java/org/apache/geode/internal/cache/ClusterConfigurationLoader.java +++ b/geode-core/src/main/java/org/apache/geode/internal/cache/ClusterConfigurationLoader.java @@ -272,7 +272,6 @@ public class ClusterConfigurationLoader { throws ClusterConfigurationNotAvailableException, UnknownHostException { Set<String> groups = getGroups(groupList); - GetClusterConfigurationFunction function = new GetClusterConfigurationFunction(); ConfigurationResponse response = null; diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/api/GeodeClusterManagementServiceProviderFactory.java b/geode-core/src/main/java/org/apache/geode/management/internal/api/GeodeClusterManagementServiceProviderFactory.java new file mode 100644 index 0000000..ce8d9c7 --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/management/internal/api/GeodeClusterManagementServiceProviderFactory.java @@ -0,0 +1,124 @@ +/* + * 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.api; + +import java.util.List; +import java.util.Set; + +import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.Logger; + +import org.apache.geode.annotations.Immutable; +import org.apache.geode.cache.Cache; +import org.apache.geode.cache.CacheFactory; +import org.apache.geode.cache.client.ClientCache; +import org.apache.geode.cache.client.ClientCacheFactory; +import org.apache.geode.cache.execute.FunctionException; +import org.apache.geode.cache.execute.FunctionService; +import org.apache.geode.cache.execute.ResultCollector; +import org.apache.geode.distributed.internal.InternalLocator; +import org.apache.geode.distributed.internal.membership.InternalDistributedMember; +import org.apache.geode.internal.cache.GemFireCacheImpl; +import org.apache.geode.internal.logging.LogService; +import org.apache.geode.management.api.ClusterManagementService; +import org.apache.geode.management.client.ClusterManagementServiceProvider; +import org.apache.geode.management.internal.ClusterManagementClient; +import org.apache.geode.management.internal.cli.domain.MemberInformation; +import org.apache.geode.management.internal.cli.functions.GetMemberInformationFunction; +import org.apache.geode.management.spi.ClusterManagementServiceProviderFactory; +import org.apache.geode.management.spi.JavaClientClusterManagementProviderFactory; + +/** + * An implementation of {@link ClusterManagementServiceProviderFactory} which can be used in any + * context where Geode is running (client, server or locator). It will attempt to determine the + * address of the {@code ClusterManagementService} when using the {@code create()} call. Otherwise + * an explicit can also be used. + */ +public class GeodeClusterManagementServiceProviderFactory extends + JavaClientClusterManagementProviderFactory { + + @Immutable + private static final GetMemberInformationFunction MEMBER_INFORMATION_FUNCTION = + new GetMemberInformationFunction(); + + private static final Logger logger = LogService.getLogger(); + + @Override + public String getContext() { + return ClusterManagementServiceProvider.GEODE_CONTEXT; + } + + @Override + public ClusterManagementService create() throws IllegalStateException { + if (InternalLocator.getLocator() != null) { + return InternalLocator.getLocator().getClusterManagementService(); + } + + Cache cache = CacheFactory.getAnyInstance(); + if (cache != null && cache.isServer()) { + GemFireCacheImpl cacheImpl = (GemFireCacheImpl) cache; + + Set<InternalDistributedMember> locatorsWithClusterConfig = + cacheImpl.getDistributionManager().getAllHostedLocatorsWithSharedConfiguration() + .keySet(); + String serviceAddress = getHttpServiceAddress(locatorsWithClusterConfig); + + return new ClusterManagementClient(serviceAddress); + } + + ClientCache clientCache = ClientCacheFactory.getAnyInstance(); + if (clientCache != null) { + throw new IllegalStateException( + "Under construction. To retrieve an instance of ClusterManagementService from a Geode client, please use either create(clusterUrl) or create(requestFactory) methods"); + } + // } catch( CacheClosedException e) { + throw new IllegalStateException("ClusterManagementService.create() " + + "must be executed on one of locator, server or client cache VMs"); + } + + private String getHttpServiceAddress(Set<InternalDistributedMember> locators) { + for (InternalDistributedMember locator : locators) { + + try { + ResultCollector resultCollector = + FunctionService.onMember(locator).execute(MEMBER_INFORMATION_FUNCTION); + List<MemberInformation> memberInformation = + (List<MemberInformation>) resultCollector.getResult(); + // Is this even possible? + if (memberInformation.isEmpty()) { + continue; + } + + // What address to use + String host; + if (StringUtils.isNotBlank(memberInformation.get(0).getHttpServiceBindAddress())) { + host = memberInformation.get(0).getHttpServiceBindAddress(); + } else if (StringUtils.isNotBlank(memberInformation.get(0).getServerBindAddress())) { + host = memberInformation.get(0).getServerBindAddress(); + } else { + host = memberInformation.get(0).getHost(); + } + + return String.format("http://%s:%d", host, memberInformation.get(0).getHttpServicePort()); + } catch (FunctionException e) { + logger.warn("Unable to execute GetMemberInformationFunction on " + locator.getId()); + throw new IllegalStateException(e); + } + } + + throw new IllegalStateException("Unable to determine ClusterManagementService endpoint"); + } +} diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/api/LocatorClusterManagementService.java b/geode-core/src/main/java/org/apache/geode/management/internal/api/LocatorClusterManagementService.java index 69aa45e..c439f7c 100644 --- a/geode-core/src/main/java/org/apache/geode/management/internal/api/LocatorClusterManagementService.java +++ b/geode-core/src/main/java/org/apache/geode/management/internal/api/LocatorClusterManagementService.java @@ -35,6 +35,8 @@ import org.apache.geode.distributed.ConfigurationPersistenceService; import org.apache.geode.distributed.DistributedMember; import org.apache.geode.distributed.internal.DistributionManager; import org.apache.geode.internal.logging.LogService; +import org.apache.geode.management.api.ClusterManagementResult; +import org.apache.geode.management.api.ClusterManagementService; import org.apache.geode.management.internal.cli.CliUtil; import org.apache.geode.management.internal.cli.functions.CliFunctionResult; import org.apache.geode.management.internal.cli.functions.UpdateCacheFunction; diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DescribeMemberCommand.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DescribeMemberCommand.java index b874f5a..753e159 100644 --- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DescribeMemberCommand.java +++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DescribeMemberCommand.java @@ -65,11 +65,6 @@ public class DescribeMemberCommand extends InternalGfshCommand { CompositeResultData crd = ResultBuilder.createCompositeResultData(); MemberInformation memberInformation = (MemberInformation) obj; - memberInformation.setName(memberToBeDescribed.getName()); - memberInformation.setId(memberToBeDescribed.getId()); - memberInformation.setHost(memberToBeDescribed.getHost()); - memberInformation.setProcessId("" + memberToBeDescribed.getProcessId()); - CompositeResultData.SectionResultData section = crd.addSection(); section.addData("Name", memberInformation.getName()); section.addData("Id", memberInformation.getId()); diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/domain/MemberInformation.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/domain/MemberInformation.java index a5e1325..357b976 100644 --- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/domain/MemberInformation.java +++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/domain/MemberInformation.java @@ -41,8 +41,9 @@ public class MemberInformation implements Serializable { private String cacheXmlFilePath; private String host; private String processId; - private String locatorBindAddress; private int locatorPort; + private int httpServicePort; + private String httpServiceBindAddress; private boolean isServer; private List<CacheServerInfo> cacheServerList; private int clientCount; @@ -170,14 +171,6 @@ public class MemberInformation implements Serializable { this.processId = processId; } - public String getLocatorBindAddress() { - return locatorBindAddress; - } - - public void setLocatorBindAddress(String locatorBindAddress) { - this.locatorBindAddress = locatorBindAddress; - } - public int getLocatorPort() { return locatorPort; } @@ -186,6 +179,22 @@ public class MemberInformation implements Serializable { this.locatorPort = locatorPort; } + public int getHttpServicePort() { + return httpServicePort; + } + + public void setHttpServicePort(int httpServicePort) { + this.httpServicePort = httpServicePort; + } + + public String getHttpServiceBindAddress() { + return httpServiceBindAddress; + } + + public void setHttpServiceBindAddress(String httpServiceBindAddress) { + this.httpServiceBindAddress = httpServiceBindAddress; + } + public boolean isServer() { return isServer; } diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/GetMemberInformationFunction.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/GetMemberInformationFunction.java index 999f9eb..88c4ee4 100644 --- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/GetMemberInformationFunction.java +++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/GetMemberInformationFunction.java @@ -25,9 +25,11 @@ import org.apache.geode.cache.Cache; import org.apache.geode.cache.CacheClosedException; import org.apache.geode.cache.execute.FunctionContext; import org.apache.geode.cache.server.CacheServer; +import org.apache.geode.distributed.DistributedMember; import org.apache.geode.distributed.internal.DistributionConfig; import org.apache.geode.distributed.internal.InternalDistributedSystem; import org.apache.geode.internal.cache.CacheClientStatus; +import org.apache.geode.internal.cache.InternalCache; import org.apache.geode.internal.cache.execute.InternalFunction; import org.apache.geode.internal.cache.tier.InternalClientMembership; import org.apache.geode.internal.cache.tier.sockets.ClientProxyMembershipID; @@ -41,7 +43,8 @@ import org.apache.geode.management.internal.cli.domain.MemberInformation; */ public class GetMemberInformationFunction implements InternalFunction { - private static final long serialVersionUID = 1L; + + private static final long serialVersionUID = 1404642539058875565L; @Override public String getId() { @@ -73,19 +76,29 @@ public class GetMemberInformationFunction implements InternalFunction { InternalDistributedSystem system = (InternalDistributedSystem) cache.getDistributedSystem(); DistributionConfig config = system.getConfig(); - String serverBindAddress = config.getServerBindAddress(); - MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean(); + DistributedMember member = + CliUtil.getDistributedMemberByNameOrId(functionContext.getMemberName(), + (InternalCache) functionContext.getCache()); MemberInformation memberInfo = new MemberInformation(); + + memberInfo.setName(member.getName()); + memberInfo.setId(member.getId()); + memberInfo.setHost(member.getHost()); + memberInfo.setProcessId("" + member.getProcessId()); + memberInfo.setGroups(config.getGroups()); memberInfo.setLogFilePath(config.getLogFile().getCanonicalPath()); memberInfo.setStatArchiveFilePath(config.getStatisticArchiveFile().getCanonicalPath()); memberInfo.setWorkingDirPath(System.getProperty("user.dir")); memberInfo.setCacheXmlFilePath(config.getCacheXmlFile().getCanonicalPath()); memberInfo.setLocators(config.getLocators()); - memberInfo.setServerBindAddress(serverBindAddress); + memberInfo.setServerBindAddress(config.getServerBindAddress()); memberInfo.setOffHeapMemorySize(config.getOffHeapMemorySize()); + memberInfo.setHttpServicePort(config.getHttpServicePort()); + memberInfo.setHttpServiceBindAddress(config.getHttpServiceBindAddress()); + MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean(); MemoryUsage memUsage = memoryMXBean.getHeapMemoryUsage(); memberInfo.setHeapUsage(Long.toString(bytesToMeg(memUsage.getUsed()))); memberInfo.setMaxHeapSize(Long.toString(bytesToMeg(memUsage.getMax()))); diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/util/MemberInformation.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/util/MemberInformation.java deleted file mode 100644 index a59f23a..0000000 --- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/util/MemberInformation.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * 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.util; - -import java.io.Serializable; - - -/*** - * Data class to hold the information of the member Used in describe member command - * - */ -public class MemberInformation implements Serializable { - private static final long serialVersionUID = 1L; - private String name; - private String id; - private String workingDirPath; - private String groups; - private String logFilePath; - private String statArchiveFilePath; - private String cpuUsage; - private String serverBindAddress; - private String locators; - private String heapUsage; - private String maxHeapSize; - private String initHeapSize; - private String cacheXmlFilePath; - private String host; - private String processId; - private String locatorBindAddress; - private int locatorPort; - - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String getWorkingDirPath() { - return workingDirPath; - } - - public void setWorkingDirPath(String workingDirPath) { - this.workingDirPath = workingDirPath; - } - - public String getGroups() { - return groups; - } - - public void setGroups(String groups) { - this.groups = groups; - } - - public String getLogFilePath() { - return logFilePath; - } - - public void setLogFilePath(String logFilePath) { - this.logFilePath = logFilePath; - } - - public String getStatArchiveFilePath() { - return statArchiveFilePath; - } - - public void setStatArchiveFilePath(String statArchiveFilePath) { - this.statArchiveFilePath = statArchiveFilePath; - } - - public String getCpuUsage() { - return cpuUsage; - } - - public void setCpuUsage(String cpuUsage) { - this.cpuUsage = cpuUsage; - } - - public String getServerBindAddress() { - return serverBindAddress; - } - - public void setServerBindAddress(String serverBindAddress) { - this.serverBindAddress = serverBindAddress; - } - - public String getLocators() { - return locators; - } - - public void setLocators(String locators) { - this.locators = locators; - } - - public String getHeapUsage() { - return heapUsage; - } - - public void setHeapUsage(String heapUsage) { - this.heapUsage = heapUsage; - } - - public String getMaxHeapSize() { - return maxHeapSize; - } - - public void setMaxHeapSize(String maxHeapSize) { - this.maxHeapSize = maxHeapSize; - } - - public String getCacheXmlFilePath() { - return cacheXmlFilePath; - } - - public void setCacheXmlFilePath(String cacheXmlFilePath) { - this.cacheXmlFilePath = cacheXmlFilePath; - } - - public String getInitHeapSize() { - return initHeapSize; - } - - public void setInitHeapSize(String initHeapSize) { - this.initHeapSize = initHeapSize; - } - - public String getHost() { - return host; - } - - public void setHost(String host) { - this.host = host; - } - - public String getProcessId() { - return processId; - } - - public void setProcessId(String processId) { - this.processId = processId; - } - - public String getLocatorBindAddress() { - return locatorBindAddress; - } - - public void setLocatorBindAddress(String locatorBindAddress) { - this.locatorBindAddress = locatorBindAddress; - } - - public int getLocatorPort() { - return locatorPort; - } - - public void setLocatorPort(int locatorPort) { - this.locatorPort = locatorPort; - } -} diff --git a/geode-core/src/main/resources/META-INF/services/org.apache.geode.management.spi.ClusterManagementServiceProviderFactory b/geode-core/src/main/resources/META-INF/services/org.apache.geode.management.spi.ClusterManagementServiceProviderFactory new file mode 100644 index 0000000..ec5fe49 --- /dev/null +++ b/geode-core/src/main/resources/META-INF/services/org.apache.geode.management.spi.ClusterManagementServiceProviderFactory @@ -0,0 +1,16 @@ +# +# 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. +# + +org.apache.geode.management.internal.api.GeodeClusterManagementServiceProviderFactory diff --git a/geode-core/src/main/resources/org/apache/geode/internal/sanctioned-geode-core-serializables.txt b/geode-core/src/main/resources/org/apache/geode/internal/sanctioned-geode-core-serializables.txt index d4105c2..7a57f84 100644 --- a/geode-core/src/main/resources/org/apache/geode/internal/sanctioned-geode-core-serializables.txt +++ b/geode-core/src/main/resources/org/apache/geode/internal/sanctioned-geode-core-serializables.txt @@ -524,7 +524,7 @@ org/apache/geode/management/internal/cli/domain/FixedPartitionAttributesInfo,tru org/apache/geode/management/internal/cli/domain/IndexDetails,true,-2198907141534201288,fromClause:java/lang/String,indexName:java/lang/String,indexStatisticsDetails:org/apache/geode/management/internal/cli/domain/IndexDetails$IndexStatisticsDetails,indexType:org/apache/geode/cache/query/IndexType,indexedExpression:java/lang/String,isValid:boolean,memberId:java/lang/String,memberName:java/lang/String,projectionAttributes:java/lang/String,regionName:java/lang/String,regionPath:java/lang/String org/apache/geode/management/internal/cli/domain/IndexDetails$IndexStatisticsDetails,false,numberOfKeys:java/lang/Long,numberOfUpdates:java/lang/Long,numberOfValues:java/lang/Long,totalUpdateTime:java/lang/Long,totalUses:java/lang/Long org/apache/geode/management/internal/cli/domain/MemberConfigurationInfo,false,cacheAttributes:java/util/Map,cacheServerAttributes:java/util/List,gfePropsRuntime:java/util/Map,gfePropsSetFromFile:java/util/Map,gfePropsSetUsingApi:java/util/Map,gfePropsSetWithDefaults:java/util/Map,jvmInputArguments:java/util/List,pdxAttributes:java/util/Map,systemProperties:java/util/Properties -org/apache/geode/management/internal/cli/domain/MemberInformation,true,1,cacheServerList:java/util/List,cacheXmlFilePath:java/lang/String,clientCount:int,cpuUsage:double,groups:java/lang/String,heapUsage:java/lang/String,host:java/lang/String,hostedRegions:java/util/Set,id:java/lang/String,initHeapSize:java/lang/String,isServer:boolean,locatorBindAddress:java/lang/String,locatorPort:int,locators:java/lang/String,logFilePath:java/lang/String,maxHeapSize:java/lang/String,name:java/lang/Str [...] +org/apache/geode/management/internal/cli/domain/MemberInformation,true,1,cacheServerList:java/util/List,cacheXmlFilePath:java/lang/String,clientCount:int,cpuUsage:double,groups:java/lang/String,heapUsage:java/lang/String,host:java/lang/String,hostedRegions:java/util/Set,httpServiceBindAddress:java/lang/String,httpServicePort:int,id:java/lang/String,initHeapSize:java/lang/String,isServer:boolean,locatorPort:int,locators:java/lang/String,logFilePath:java/lang/String,maxHeapSize:java/lang/S [...] org/apache/geode/management/internal/cli/domain/PartitionAttributesInfo,true,1,colocatedWith:java/lang/String,fpaInfoList:java/util/List,localMaxMemory:int,nonDefaultAttributes:java/util/Map,partitionResolverName:java/lang/String,recoveryDelay:long,redundantCopies:int,startupRecoveryDelay:long,totalNumBuckets:int org/apache/geode/management/internal/cli/domain/RegionAttributesInfo,true,336184564012988487,asyncEventQueueIDs:java/util/Set,cacheListenerClassNames:java/util/List,cacheLoaderClassName:java/lang/String,cacheWriterClassName:java/lang/String,cloningEnabled:boolean,compressorClassName:java/lang/String,concurrencyChecksEnabled:boolean,concurrencyLevel:int,customExpiryIdleTimeoutClass:java/lang/String,customExpiryTTLClass:java/lang/String,dataPolicy:org/apache/geode/cache/DataPolicy,diskStor [...] org/apache/geode/management/internal/cli/domain/RegionDescription,true,6461449275798378332,cndEvictionAttributes:java/util/Map,cndPartitionAttributes:java/util/Map,cndRegionAttributes:java/util/Map,dataPolicy:org/apache/geode/cache/DataPolicy,isAccessor:boolean,isLocal:boolean,isPartition:boolean,isPersistent:boolean,isReplicate:boolean,name:java/lang/String,regionDescPerMemberMap:java/util/Map,scope:org/apache/geode/cache/Scope @@ -569,7 +569,7 @@ org/apache/geode/management/internal/cli/functions/GatewaySenderDestroyFunction, org/apache/geode/management/internal/cli/functions/GatewaySenderDestroyFunctionArgs,true,3848480256348119530,id:java/lang/String,ifExists:boolean org/apache/geode/management/internal/cli/functions/GatewaySenderFunctionArgs,true,4636678328980816780,alertThreshold:java/lang/Integer,batchSize:java/lang/Integer,batchTimeInterval:java/lang/Integer,diskStoreName:java/lang/String,diskSynchronous:java/lang/Boolean,dispatcherThreads:java/lang/Integer,enableBatchConflation:java/lang/Boolean,enablePersistence:java/lang/Boolean,gatewayEventFilters:java/util/List,gatewayTransportFilters:java/util/List,id:java/lang/String,manualStart:java/lang/ [...] org/apache/geode/management/internal/cli/functions/GetMemberConfigInformationFunction,true,1 -org/apache/geode/management/internal/cli/functions/GetMemberInformationFunction,true,1 +org/apache/geode/management/internal/cli/functions/GetMemberInformationFunction,true,1404642539058875565 org/apache/geode/management/internal/cli/functions/GetRegionDescriptionFunction,true,1 org/apache/geode/management/internal/cli/functions/GetRegionsFunction,true,1 org/apache/geode/management/internal/cli/functions/GetStackTracesFunction,true,1 @@ -615,7 +615,6 @@ org/apache/geode/management/internal/cli/util/ExportLogsCacheWriter,false,curren org/apache/geode/management/internal/cli/util/FixedPartitionAttributesInfo,false,isPrimary:boolean,numBuckets:int,partitionName:java/lang/String org/apache/geode/management/internal/cli/util/JConsoleNotFoundException,true,-1485615321440327206 org/apache/geode/management/internal/cli/util/LogFilter$LineFilterResult,false -org/apache/geode/management/internal/cli/util/MemberInformation,true,1,cacheXmlFilePath:java/lang/String,cpuUsage:java/lang/String,groups:java/lang/String,heapUsage:java/lang/String,host:java/lang/String,id:java/lang/String,initHeapSize:java/lang/String,locatorBindAddress:java/lang/String,locatorPort:int,locators:java/lang/String,logFilePath:java/lang/String,maxHeapSize:java/lang/String,name:java/lang/String,processId:java/lang/String,serverBindAddress:java/lang/String,statArchiveFilePat [...] org/apache/geode/management/internal/cli/util/VisualVmNotFoundException,true,-8491645604829510102 org/apache/geode/management/internal/configuration/domain/SharedConfigurationStatus,false org/apache/geode/management/internal/configuration/functions/DownloadJarFunction,true,1 diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/api/ClusterManagementResultTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/api/ClusterManagementResultTest.java index d58c919..3473d1e 100644 --- a/geode-core/src/test/java/org/apache/geode/management/internal/api/ClusterManagementResultTest.java +++ b/geode-core/src/test/java/org/apache/geode/management/internal/api/ClusterManagementResultTest.java @@ -22,6 +22,8 @@ import static org.assertj.core.api.Assertions.assertThat; import org.junit.Before; import org.junit.Test; +import org.apache.geode.management.api.ClusterManagementResult; + public class ClusterManagementResultTest { private ClusterManagementResult result; diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/api/LocatorClusterManagementServiceTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/api/LocatorClusterManagementServiceTest.java index f6aa96d..e3dbd39 100644 --- a/geode-core/src/test/java/org/apache/geode/management/internal/api/LocatorClusterManagementServiceTest.java +++ b/geode-core/src/test/java/org/apache/geode/management/internal/api/LocatorClusterManagementServiceTest.java @@ -35,6 +35,7 @@ import org.apache.geode.cache.configuration.RegionConfig; import org.apache.geode.distributed.ConfigurationPersistenceService; import org.apache.geode.distributed.DistributedMember; import org.apache.geode.distributed.internal.DistributionManager; +import org.apache.geode.management.api.ClusterManagementResult; import org.apache.geode.management.internal.cli.functions.CliFunctionResult; import org.apache.geode.management.internal.exceptions.EntityExistsException; diff --git a/geode-dunit/src/main/java/org/apache/geode/test/dunit/rules/ClusterStartupRule.java b/geode-dunit/src/main/java/org/apache/geode/test/dunit/rules/ClusterStartupRule.java index cd87286..c842715 100644 --- a/geode-dunit/src/main/java/org/apache/geode/test/dunit/rules/ClusterStartupRule.java +++ b/geode-dunit/src/main/java/org/apache/geode/test/dunit/rules/ClusterStartupRule.java @@ -74,6 +74,8 @@ public class ClusterStartupRule implements SerializableTestRule { public static MemberStarterRule<?> memberStarter; public static ClientCacheRule clientCacheRule; + private boolean skipLocalDistributedSystemCleanup; + public static InternalCache getCache() { if (memberStarter == null) { return null; @@ -164,7 +166,9 @@ public class ClusterStartupRule implements SerializableTestRule { private void after(Description description) throws Throwable { - MemberStarterRule.disconnectDSIfAny(); + if (!skipLocalDistributedSystemCleanup) { + MemberStarterRule.disconnectDSIfAny(); + } // stop all the members in the order of clients, servers and locators List<VMProvider> vms = new ArrayList<>(); @@ -188,6 +192,19 @@ public class ClusterStartupRule implements SerializableTestRule { DUnitLauncher.closeAndCheckForSuspects(); } + public boolean isSkipLocalDistributedSystemCleanup() { + return skipLocalDistributedSystemCleanup; + } + + /** + * In some weird situations you may not want to do local DS cleanup as that lifecyle is deferred + * elsewhere - see {@code LocatorCleanupEventListener} and any test that uses {@code + * PlainLocatorContextLoader} or {@code LocatorWithSecurityManagerContextLoader} + */ + public void setSkipLocalDistributedSystemCleanup(boolean skipLocalDistributedSystemCleanup) { + this.skipLocalDistributedSystemCleanup = skipLocalDistributedSystemCleanup; + } + public MemberVM startLocatorVM(int index, int... locatorPort) { return startLocatorVM(index, x -> x.withConnectionToLocator(locatorPort)); } @@ -326,8 +343,8 @@ public class ClusterStartupRule implements SerializableTestRule { /** * gracefully stop the member/client inside this vm * - * if this vm is a server/locator, it stops them and cleans the working dir - * if this vm is a client, it closes the client cache. + * if this vm is a server/locator, it stops them and cleans the working dir if this vm is a + * client, it closes the client cache. * * @param index vm index */ diff --git a/geode-management/build.gradle b/geode-management/build.gradle index d89f506..1c915dc 100755 --- a/geode-management/build.gradle +++ b/geode-management/build.gradle @@ -24,6 +24,7 @@ dependencies { compile('com.fasterxml.jackson.core:jackson-databind') compile('com.fasterxml.jackson.core:jackson-core') compile('com.fasterxml.jackson.core:jackson-annotations') + compile('org.springframework:spring-web') compileOnly(project(':geode-common')) { exclude module: 'junit' diff --git a/geode-management/src/main/java/org/apache/geode/cache/configuration/RegionConfig.java b/geode-management/src/main/java/org/apache/geode/cache/configuration/RegionConfig.java index 7522af8..27f2e73 100644 --- a/geode-management/src/main/java/org/apache/geode/cache/configuration/RegionConfig.java +++ b/geode-management/src/main/java/org/apache/geode/cache/configuration/RegionConfig.java @@ -30,6 +30,7 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlType; import org.apache.geode.annotations.Experimental; +import org.apache.geode.management.api.RestfulEndpoint; /** @@ -149,7 +150,7 @@ import org.apache.geode.annotations.Experimental; @XmlType(name = "region-type", namespace = "http://geode.apache.org/schema/cache", propOrder = {"regionAttributes", "indexes", "entries", "regionElements", "regions"}) @Experimental -public class RegionConfig implements CacheElement { +public class RegionConfig implements CacheElement, RestfulEndpoint { @XmlElement(name = "region-attributes", namespace = "http://geode.apache.org/schema/cache") protected RegionAttributesType regionAttributes; @XmlElement(name = "index", namespace = "http://geode.apache.org/schema/cache") @@ -172,6 +173,11 @@ public class RegionConfig implements CacheElement { this.type = refid; } + @Override + public String getEndpoint() { + return "/v2/regions"; + } + public RegionAttributesType getRegionAttributes() { return regionAttributes; } diff --git a/geode-management/src/main/java/org/apache/geode/management/internal/api/ClusterManagementResult.java b/geode-management/src/main/java/org/apache/geode/management/api/ClusterManagementResult.java similarity index 97% rename from geode-management/src/main/java/org/apache/geode/management/internal/api/ClusterManagementResult.java rename to geode-management/src/main/java/org/apache/geode/management/api/ClusterManagementResult.java index a4b03fb..67bdd2c 100644 --- a/geode-management/src/main/java/org/apache/geode/management/internal/api/ClusterManagementResult.java +++ b/geode-management/src/main/java/org/apache/geode/management/api/ClusterManagementResult.java @@ -12,7 +12,7 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package org.apache.geode.management.internal.api; +package org.apache.geode.management.api; import java.util.HashMap; import java.util.Map; diff --git a/geode-management/src/main/java/org/apache/geode/management/internal/api/ClusterManagementService.java b/geode-management/src/main/java/org/apache/geode/management/api/ClusterManagementService.java similarity index 51% rename from geode-management/src/main/java/org/apache/geode/management/internal/api/ClusterManagementService.java rename to geode-management/src/main/java/org/apache/geode/management/api/ClusterManagementService.java index 1a88592..c87b641 100644 --- a/geode-management/src/main/java/org/apache/geode/management/internal/api/ClusterManagementService.java +++ b/geode-management/src/main/java/org/apache/geode/management/api/ClusterManagementService.java @@ -13,28 +13,52 @@ * the License. */ -package org.apache.geode.management.internal.api; +package org.apache.geode.management.api; import org.apache.geode.annotations.Experimental; import org.apache.geode.cache.configuration.CacheElement; /** - * this is responsible for applying and persisting cache configuration changes on locators - * and/or servers. + * this is responsible for applying and persisting cache configuration changes on locators and/or + * servers. */ @Experimental public interface ClusterManagementService { + /** - * This method will try to create the element on all the applicable members in the cluster and - * persist the configuration in the cluster configuration if persistence is enabled. + * This method will create the element on all the applicable members in the cluster and persist + * the configuration in the cluster configuration if persistence is enabled. * - * @param config this holds the configuration attributes of the element you are trying to create - * on the cluster + * @param config this holds the configuration attributes of the element to be created on the + * cluster + * @param group the server group to which this config applies * @throws IllegalArgumentException, NoMemberException, EntityExistsException + * @see CacheElement */ ClusterManagementResult create(CacheElement config, String group); + /** + * This method will delete the element on all the applicable members in the cluster and update the + * configuration in the cluster configuration if persistence is enabled. + * + * @param config this holds the configuration attributes of the element to be deleted on the + * cluster + * @param group the server group to which this config applies + * @throws IllegalArgumentException, NoMemberException, EntityExistsException + * @see CacheElement + */ ClusterManagementResult delete(CacheElement config, String group); + /** + * This method will update the element on all the applicable members in the cluster and persist + * the updated configuration in the cluster configuration if persistence is enabled. + * + * @param config this holds the configuration attributes of the element to be updated on the + * cluster + * @param group the server group to which this config applies + * @throws IllegalArgumentException, NoMemberException, EntityExistsException + * @see CacheElement + */ ClusterManagementResult update(CacheElement config, String group); + } diff --git a/geode-management/src/main/java/org/apache/geode/management/internal/api/Status.java b/geode-management/src/main/java/org/apache/geode/management/api/RestfulEndpoint.java similarity index 59% copy from geode-management/src/main/java/org/apache/geode/management/internal/api/Status.java copy to geode-management/src/main/java/org/apache/geode/management/api/RestfulEndpoint.java index 9a8a5cb..81736df 100644 --- a/geode-management/src/main/java/org/apache/geode/management/internal/api/Status.java +++ b/geode-management/src/main/java/org/apache/geode/management/api/RestfulEndpoint.java @@ -12,33 +12,10 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package org.apache.geode.management.internal.api; -public class Status { - boolean success; - String message; +package org.apache.geode.management.api; - // needed for json deserialization - public Status() {} +public interface RestfulEndpoint { - public Status(boolean success, String message) { - this.success = success; - this.message = message; - } - - public boolean isSuccess() { - return success; - } - - public void setSuccess(boolean success) { - this.success = success; - } - - public String getMessage() { - return message; - } - - public void setMessage(String message) { - this.message = message; - } + String getEndpoint(); } diff --git a/geode-management/src/main/java/org/apache/geode/management/internal/api/Status.java b/geode-management/src/main/java/org/apache/geode/management/api/Status.java similarity index 96% rename from geode-management/src/main/java/org/apache/geode/management/internal/api/Status.java rename to geode-management/src/main/java/org/apache/geode/management/api/Status.java index 9a8a5cb..e50c85a 100644 --- a/geode-management/src/main/java/org/apache/geode/management/internal/api/Status.java +++ b/geode-management/src/main/java/org/apache/geode/management/api/Status.java @@ -12,7 +12,7 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package org.apache.geode.management.internal.api; +package org.apache.geode.management.api; public class Status { boolean success; diff --git a/geode-management/src/main/java/org/apache/geode/management/client/ClusterManagementServiceProvider.java b/geode-management/src/main/java/org/apache/geode/management/client/ClusterManagementServiceProvider.java new file mode 100644 index 0000000..c378d46 --- /dev/null +++ b/geode-management/src/main/java/org/apache/geode/management/client/ClusterManagementServiceProvider.java @@ -0,0 +1,119 @@ +/* + * 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.client; + +import java.util.ArrayList; +import java.util.List; +import java.util.ServiceLoader; + +import org.springframework.http.client.ClientHttpRequestFactory; + +import org.apache.geode.annotations.Experimental; +import org.apache.geode.management.api.ClusterManagementService; +import org.apache.geode.management.spi.ClusterManagementServiceProviderFactory; + +/** + * Top-level entry point for client interaction with the {@link ClusterManagementService}. A user + * can create an instance of the {@code ClusterManagementService} (CMS for short) in several ways, + * each providing various level of control or expediency. + * <p/> + * Calling {@code getFactory(context)} will return an explicit instance of {@link + * ClusterManagementServiceProviderFactory}. Methods on this factory can then be called to create or + * retrieve instances of a CMS. New {@link ClusterManagementServiceProvider}s can be written if + * specific customization or parameterization is required. + * <p/> + * A note about contexts. A context is simply a unique string which identifies a specific instance + * of CMS that will be returned. Contexts map to the different uses of a CMS. Currently, the + * following contexts are provided: + * <ul> + * <li> + * {@code JAVA_CLIENT_CONTEXT} ("java-client") - would be used to retrieve a CMS instance on a pure + * Java client - i.e. an app that is not running in the context of a Geode client or server. This + * context is available when using the <i>geode-management</i> module. + * </li> + * <li>{@code GEODE_CONTEXT} ("geode") - would be used to retrieve a CMS instance + * from a JVM where either a {@code Cache} or {@code ClientCache} exists. This context is available + * when using the <i>geode-core</i> module. + * </li> + * </ul> + * If the URL of the Cluster Management Service is known, the {@code getService(url)} method can be + * called. This implicitly uses the {@code JAVA_CLIENT_CONTEXT}. For further control the CMS can be + * configured with a {@link ClientHttpRequestFactory} by calling {@code + * getService(requestFactory)}. + * <p/> + * Finally, the simplest way to create a CMS instance is simply to call {@code getService()}. This + * method will attempt to infer the context and use an appropriate service provider to create a CMS + * instance. + */ +@Experimental +public class ClusterManagementServiceProvider { + + public static final String JAVA_CLIENT_CONTEXT = "java-client"; + public static final String GEODE_CONTEXT = "geode"; + + private static List<ClusterManagementServiceProviderFactory> providerFactories = null; + + public static ClusterManagementService getService() { + ClusterManagementServiceProviderFactory factory; + try { + factory = getFactory(GEODE_CONTEXT); + try { + ClusterManagementService cms = factory.create(); + return cms; + } catch (IllegalStateException ex) { + // Ignored + } + } catch (IllegalArgumentException iex) { + // Ig + } catch (Exception iex) { + iex.printStackTrace(); + } + + throw new IllegalStateException( + "Unable to get ClusterManagementService using any of the default contexts"); + } + + public static ClusterManagementService getService(String clusterUrl) { + return getFactory(JAVA_CLIENT_CONTEXT).create(clusterUrl); + } + + public static ClusterManagementService getService(ClientHttpRequestFactory requestFactory) { + return getFactory(JAVA_CLIENT_CONTEXT).create(requestFactory); + } + + public static synchronized ClusterManagementServiceProviderFactory getFactory(String context) { + if (providerFactories == null) { + loadClusterManagementServiceProviderFactories(); + } + + ClusterManagementServiceProviderFactory factory = providerFactories.stream() + .filter(x -> x.getContext().equalsIgnoreCase(context)) + .findFirst() + .orElseThrow( + () -> new IllegalArgumentException("Did not find provider for context: " + context)); + + return factory; + } + + private static void loadClusterManagementServiceProviderFactories() { + providerFactories = new ArrayList<>(); + + for (ClusterManagementServiceProviderFactory factory : ServiceLoader + .load(ClusterManagementServiceProviderFactory.class)) { + providerFactories.add(factory); + } + } +} diff --git a/geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/StandardRequestPostProcessor.java b/geode-management/src/main/java/org/apache/geode/management/client/RestTemplateResponseErrorHandler.java similarity index 56% copy from geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/StandardRequestPostProcessor.java copy to geode-management/src/main/java/org/apache/geode/management/client/RestTemplateResponseErrorHandler.java index 92a3d5f..6ae39c0 100644 --- a/geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/StandardRequestPostProcessor.java +++ b/geode-management/src/main/java/org/apache/geode/management/client/RestTemplateResponseErrorHandler.java @@ -13,18 +13,23 @@ * the License. */ -package org.apache.geode.management.internal.rest; +package org.apache.geode.management.client; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.test.web.servlet.request.RequestPostProcessor; +import java.io.IOException; + +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.web.client.ResponseErrorHandler; + +public class RestTemplateResponseErrorHandler implements ResponseErrorHandler { + @Override + public boolean hasError(ClientHttpResponse response) throws IOException { + // All errors should be handled by the server and be returned as part of the + // ResponseEntity<ClusterManagementResult>. + return false; + } -class StandardRequestPostProcessor implements RequestPostProcessor { @Override - public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) { - request.addHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON); - request.addHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON); - return request; + public void handleError(ClientHttpResponse response) throws IOException { + // this should not be necessary } } diff --git a/geode-management/src/main/java/org/apache/geode/management/internal/ClusterManagementClient.java b/geode-management/src/main/java/org/apache/geode/management/internal/ClusterManagementClient.java new file mode 100644 index 0000000..5eeb647 --- /dev/null +++ b/geode-management/src/main/java/org/apache/geode/management/internal/ClusterManagementClient.java @@ -0,0 +1,105 @@ +/* + * 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; + +import org.springframework.http.ResponseEntity; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.web.client.ResponseErrorHandler; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.DefaultUriTemplateHandler; +import sun.reflect.generics.reflectiveObjects.NotImplementedException; + +import org.apache.geode.annotations.Experimental; +import org.apache.geode.cache.configuration.CacheElement; +import org.apache.geode.management.api.ClusterManagementResult; +import org.apache.geode.management.api.ClusterManagementService; +import org.apache.geode.management.api.RestfulEndpoint; +import org.apache.geode.management.client.RestTemplateResponseErrorHandler; + +/** + * Implementation of {@link ClusterManagementService} interface which represents the cluster + * management service as used by a Java client. + * <p/> + * In order to manipulate Geode components (Regions, etc.) clients can construct instances of {@link + * CacheElement}s and call the corresponding {@link ClusterManagementClient#create(CacheElement, + * String)}, {@link ClusterManagementClient#delete(CacheElement, String)} or {@link + * ClusterManagementClient#update(CacheElement, String)} method. The returned {@link + * ClusterManagementResult} will contain all necessary information about the outcome of the call. + * This will include the result of persisting the config as part of the cluster configuration as + * well as creating the actual component in the cluster. + * <p/> + * All create calls are idempotent and will not return an error if the desired component already + * exists. + */ +@Experimental +public class ClusterManagementClient implements ClusterManagementService { + + private static final ResponseErrorHandler DEFAULT_ERROR_HANDLER = + new RestTemplateResponseErrorHandler(); + + private final RestTemplate restTemplate; + + private ClusterManagementClient() { + restTemplate = new RestTemplate(); + restTemplate.setErrorHandler(DEFAULT_ERROR_HANDLER); + } + + public ClusterManagementClient(ClientHttpRequestFactory requestFactory) { + this(); + this.restTemplate.setRequestFactory(requestFactory); + } + + public ClusterManagementClient(String clusterUrl) { + this(); + DefaultUriTemplateHandler templateHandler = new DefaultUriTemplateHandler(); + templateHandler.setBaseUrl(clusterUrl); + restTemplate.setUriTemplateHandler(templateHandler); + } + + @Override + public ClusterManagementResult create(CacheElement config, String group) { + String endPoint = getEndpoint(config); + + ResponseEntity<ClusterManagementResult> result = restTemplate.postForEntity(endPoint, config, + ClusterManagementResult.class); + + return result.getBody(); + } + + @Override + public ClusterManagementResult delete(CacheElement config, String group) { + throw new NotImplementedException(); + } + + @Override + public ClusterManagementResult update(CacheElement config, String group) { + throw new NotImplementedException(); + } + + public RestTemplate getRestTemplate() { + return restTemplate; + } + + private String getEndpoint(CacheElement config) { + if (!(config instanceof RestfulEndpoint)) { + throw new IllegalArgumentException( + String.format("The config type %s does not have a RESTful endpoint defined", + config.getClass().getName())); + } + + return ((RestfulEndpoint) config).getEndpoint(); + } +} diff --git a/geode-management/src/main/java/org/apache/geode/management/spi/ClusterManagementServiceProviderFactory.java b/geode-management/src/main/java/org/apache/geode/management/spi/ClusterManagementServiceProviderFactory.java new file mode 100644 index 0000000..44136f2 --- /dev/null +++ b/geode-management/src/main/java/org/apache/geode/management/spi/ClusterManagementServiceProviderFactory.java @@ -0,0 +1,60 @@ +/* + * 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.spi; + +import org.springframework.http.client.ClientHttpRequestFactory; + +import org.apache.geode.management.api.ClusterManagementService; + +/** + * A Server Provider interface which should be implemented to create instances of {@link + * ClusterManagementService} for a given context. + */ +public interface ClusterManagementServiceProviderFactory { + + /** + * Returns a list of the contexts which this provider supports. + * + * @return a list of names of supported contexts of this factory + */ + String getContext(); + + /** + * Create a {@code ClusterManagementService} instance. Throws an {@code IllegalStateException} + * if the instance cannot be created. This method would typically be used by an implementation + * that is able to infer the correct Cluster Management endpoint URL to use. + * + * @return an instance of the {@code ClusterManagementService} + */ + ClusterManagementService create() throws IllegalStateException; + + /** + * Create a {@code ClusterManagementService} that will use the given URL. + * + * @param clusterUrl the URL to conect to + * @return an instance of {@code ClusterManagementService} + */ + ClusterManagementService create(String clusterUrl); + + /** + * Create a {@code ClusterManagementService} that will use the given {@link + * ClientHttpRequestFactory} to establish connections with the Cluster Management endpoint. + * + * @param requestFactory the {@code ClientHttpRequestFactory} to use for new connections. + * @return an instance of {@code ClusterManagementService} + */ + ClusterManagementService create(ClientHttpRequestFactory requestFactory); +} diff --git a/geode-management/src/main/java/org/apache/geode/management/spi/JavaClientClusterManagementProviderFactory.java b/geode-management/src/main/java/org/apache/geode/management/spi/JavaClientClusterManagementProviderFactory.java new file mode 100644 index 0000000..666ce51 --- /dev/null +++ b/geode-management/src/main/java/org/apache/geode/management/spi/JavaClientClusterManagementProviderFactory.java @@ -0,0 +1,54 @@ +/* + * 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.spi; + +import org.springframework.http.client.ClientHttpRequestFactory; + +import org.apache.geode.management.api.ClusterManagementService; +import org.apache.geode.management.client.ClusterManagementServiceProvider; +import org.apache.geode.management.internal.ClusterManagementClient; + +/** + * A Service Provider factory which will be used to create {@link ClusterManagementService} + * instances from pure Java code - i.e. the Cluster Management endpoint URL <b>cannot</b> be + * inferred from the implied runtime context but needs to be specifically configured using a given + * URL or {@code ClientHttpRequestFactory}. + */ +public class JavaClientClusterManagementProviderFactory + implements ClusterManagementServiceProviderFactory { + + @Override + public String getContext() { + return ClusterManagementServiceProvider.JAVA_CLIENT_CONTEXT; + } + + @Override + public ClusterManagementService create() throws IllegalArgumentException { + throw new IllegalArgumentException(String.format( + "When using a %s context, you must call create(String) or create(ClientHttpRequestFactory)", + ClusterManagementServiceProvider.JAVA_CLIENT_CONTEXT)); + } + + @Override + public ClusterManagementService create(String clusterUrl) { + return new ClusterManagementClient(clusterUrl); + } + + @Override + public ClusterManagementService create(ClientHttpRequestFactory requestFactory) { + return new ClusterManagementClient(requestFactory); + } +} diff --git a/geode-management/src/main/resources/META-INF/services/org.apache.geode.management.spi.ClusterManagementServiceProviderFactory b/geode-management/src/main/resources/META-INF/services/org.apache.geode.management.spi.ClusterManagementServiceProviderFactory new file mode 100644 index 0000000..5863340 --- /dev/null +++ b/geode-management/src/main/resources/META-INF/services/org.apache.geode.management.spi.ClusterManagementServiceProviderFactory @@ -0,0 +1,17 @@ +# +# 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. +# + + +org.apache.geode.management.spi.JavaClientClusterManagementProviderFactory diff --git a/geode-management/src/test/resources/expected-pom.xml b/geode-management/src/test/resources/expected-pom.xml index 14ab573..66e0853 100644 --- a/geode-management/src/test/resources/expected-pom.xml +++ b/geode-management/src/test/resources/expected-pom.xml @@ -55,6 +55,11 @@ <artifactId>jackson-annotations</artifactId> <scope>compile</scope> </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-web</artifactId> + <scope>compile</scope> + </dependency> </dependencies> <dependencyManagement> <dependencies> diff --git a/geode-web-management/build.gradle b/geode-web-management/build.gradle index f3868c8..46ab5f6 100644 --- a/geode-web-management/build.gradle +++ b/geode-web-management/build.gradle @@ -124,6 +124,10 @@ dependencies { distributedTestCompile(project(':geode-dunit')) { exclude module: 'geode-core' } + + distributedTestRuntimeOnly('org.apache.logging.log4j:log4j-slf4j-impl') { + exclude module: 'slf4j-api' + } } war { diff --git a/geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/PlainLocatorContextLoader.java b/geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/BaseLocatorContextLoader.java similarity index 76% copy from geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/PlainLocatorContextLoader.java copy to geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/BaseLocatorContextLoader.java index e2a28b7..fc27eec 100644 --- a/geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/PlainLocatorContextLoader.java +++ b/geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/BaseLocatorContextLoader.java @@ -17,6 +17,7 @@ package org.apache.geode.management.internal.rest; import org.springframework.test.context.web.GenericXmlWebContextLoader; import org.springframework.test.context.web.WebMergedContextConfiguration; +import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.GenericWebApplicationContext; import org.apache.geode.internal.cache.HttpService; @@ -28,24 +29,26 @@ import org.apache.geode.test.junit.rules.LocatorStarterRule; * ServletContext so that it can be retrieved and cleaned up in the LocatorCleanupEventListener. * There has to be a better way... */ -class PlainLocatorContextLoader extends GenericXmlWebContextLoader { +public abstract class BaseLocatorContextLoader extends GenericXmlWebContextLoader { - private LocatorStarterRule locator = new LocatorStarterRule() - .withAutoStart(); + public abstract LocatorStarterRule getLocator(); @Override protected void loadBeanDefinitions(GenericWebApplicationContext context, WebMergedContextConfiguration webMergedConfig) { - locator.before(); + getLocator().before(); super.loadBeanDefinitions(context, webMergedConfig); context.getServletContext().setAttribute(HttpService.SECURITY_SERVICE_SERVLET_CONTEXT_PARAM, - locator.getCache().getSecurityService()); + getLocator().getCache().getSecurityService()); context.getServletContext().setAttribute(HttpService.CLUSTER_MANAGEMENT_SERVICE_CONTEXT_PARAM, - locator.getLocator().getClusterManagementService()); + getLocator().getLocator().getClusterManagementService()); - context.getServletContext().setAttribute("locator", locator); + context.getServletContext().setAttribute("locator", getLocator()); } + public static LocatorStarterRule getLocatorFromContext(WebApplicationContext context) { + return (LocatorStarterRule) context.getServletContext().getAttribute("locator"); + } } diff --git a/geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/LocatorWithSecurityManagerContextLoader.java b/geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/LocatorWithSecurityManagerContextLoader.java index 4f03a2a..696e16a 100644 --- a/geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/LocatorWithSecurityManagerContextLoader.java +++ b/geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/LocatorWithSecurityManagerContextLoader.java @@ -15,33 +15,22 @@ package org.apache.geode.management.internal.rest; -import org.springframework.test.context.web.GenericXmlWebContextLoader; -import org.springframework.test.context.web.WebMergedContextConfiguration; -import org.springframework.web.context.support.GenericWebApplicationContext; - -import org.apache.geode.internal.cache.HttpService; import org.apache.geode.security.SimpleTestSecurityManager; import org.apache.geode.test.junit.rules.LocatorStarterRule; -class LocatorWithSecurityManagerContextLoader extends GenericXmlWebContextLoader { - - private LocatorStarterRule locator = new LocatorStarterRule() - .withSecurityManager(SimpleTestSecurityManager.class) - .withAutoStart(); - - @Override - protected void loadBeanDefinitions(GenericWebApplicationContext context, - WebMergedContextConfiguration webMergedConfig) { +public class LocatorWithSecurityManagerContextLoader extends BaseLocatorContextLoader { - locator.before(); + private final LocatorStarterRule locator; - super.loadBeanDefinitions(context, webMergedConfig); - context.getServletContext().setAttribute(HttpService.SECURITY_SERVICE_SERVLET_CONTEXT_PARAM, - locator.getCache().getSecurityService()); - context.getServletContext().setAttribute(HttpService.CLUSTER_MANAGEMENT_SERVICE_CONTEXT_PARAM, - locator.getLocator().getClusterManagementService()); + public LocatorWithSecurityManagerContextLoader() { + locator = new LocatorStarterRule() + .withSecurityManager(SimpleTestSecurityManager.class) + .withAutoStart(); + } - context.getServletContext().setAttribute("locator", locator); + @Override + public LocatorStarterRule getLocator() { + return locator; } } diff --git a/geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/PlainLocatorContextLoader.java b/geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/PlainLocatorContextLoader.java index e2a28b7..adedec8 100644 --- a/geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/PlainLocatorContextLoader.java +++ b/geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/PlainLocatorContextLoader.java @@ -15,37 +15,19 @@ package org.apache.geode.management.internal.rest; -import org.springframework.test.context.web.GenericXmlWebContextLoader; -import org.springframework.test.context.web.WebMergedContextConfiguration; -import org.springframework.web.context.support.GenericWebApplicationContext; - -import org.apache.geode.internal.cache.HttpService; import org.apache.geode.test.junit.rules.LocatorStarterRule; -/** - * This is quite horrible. In particular we're trying to link the lifecycle of the - * LocatorStarterRule and Spring's application context. The locator is injected into the - * ServletContext so that it can be retrieved and cleaned up in the LocatorCleanupEventListener. - * There has to be a better way... - */ -class PlainLocatorContextLoader extends GenericXmlWebContextLoader { - - private LocatorStarterRule locator = new LocatorStarterRule() - .withAutoStart(); - - @Override - protected void loadBeanDefinitions(GenericWebApplicationContext context, - WebMergedContextConfiguration webMergedConfig) { - - locator.before(); +public class PlainLocatorContextLoader extends BaseLocatorContextLoader { - super.loadBeanDefinitions(context, webMergedConfig); - context.getServletContext().setAttribute(HttpService.SECURITY_SERVICE_SERVLET_CONTEXT_PARAM, - locator.getCache().getSecurityService()); - context.getServletContext().setAttribute(HttpService.CLUSTER_MANAGEMENT_SERVICE_CONTEXT_PARAM, - locator.getLocator().getClusterManagementService()); + private final LocatorStarterRule locator; - context.getServletContext().setAttribute("locator", locator); + public PlainLocatorContextLoader() { + locator = new LocatorStarterRule() + .withAutoStart(); } + @Override + public LocatorStarterRule getLocator() { + return locator; + } } diff --git a/geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/StandardRequestPostProcessor.java b/geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/StandardRequestPostProcessor.java index 92a3d5f..b1282f0 100644 --- a/geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/StandardRequestPostProcessor.java +++ b/geode-web-management/src/commonTest/java/org/apache/geode/management/internal/rest/StandardRequestPostProcessor.java @@ -20,7 +20,7 @@ import org.springframework.http.MediaType; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.test.web.servlet.request.RequestPostProcessor; -class StandardRequestPostProcessor implements RequestPostProcessor { +public class StandardRequestPostProcessor implements RequestPostProcessor { @Override public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) { request.addHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON); diff --git a/geode-web-management/src/distributedTest/java/org/apache/geode/management/client/ClusterManagementClientDUnitTest.java b/geode-web-management/src/distributedTest/java/org/apache/geode/management/client/ClusterManagementClientDUnitTest.java new file mode 100644 index 0000000..06a6f4f --- /dev/null +++ b/geode-web-management/src/distributedTest/java/org/apache/geode/management/client/ClusterManagementClientDUnitTest.java @@ -0,0 +1,87 @@ +/* + * 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.client; + + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.client.MockMvcClientHttpRequestFactory; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.client.ResponseErrorHandler; +import org.springframework.web.context.WebApplicationContext; + +import org.apache.geode.cache.configuration.RegionConfig; +import org.apache.geode.management.api.ClusterManagementResult; +import org.apache.geode.management.api.ClusterManagementService; +import org.apache.geode.management.internal.rest.BaseLocatorContextLoader; +import org.apache.geode.management.internal.rest.PlainLocatorContextLoader; +import org.apache.geode.test.dunit.rules.ClusterStartupRule; +import org.apache.geode.test.dunit.rules.MemberVM; + +@RunWith(SpringRunner.class) +@ContextConfiguration(locations = {"classpath*:WEB-INF/geode-management-servlet.xml"}, + loader = PlainLocatorContextLoader.class) +@WebAppConfiguration +public class ClusterManagementClientDUnitTest { + private static final ResponseErrorHandler ERROR_HANDLER = new RestTemplateResponseErrorHandler(); + + @Autowired + private WebApplicationContext webApplicationContext; + + @Rule + public ClusterStartupRule cluster = new ClusterStartupRule(1); + + private MemberVM server1; + private ClusterManagementService client; + + @Before + public void before() { + cluster.setSkipLocalDistributedSystemCleanup(true); + MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext) + .build(); + MockMvcClientHttpRequestFactory requestFactory = new MockMvcClientHttpRequestFactory(mockMvc); + client = ClusterManagementServiceProvider.getService(requestFactory); + + server1 = cluster.startServerVM(0, + BaseLocatorContextLoader.getLocatorFromContext(webApplicationContext).getPort()); + } + + @Test + @WithMockUser + public void createRegion() { + RegionConfig region = new RegionConfig(); + region.setName("customer"); + region.setType("REPLICATE"); + + ClusterManagementResult result = client.create(region, ""); + + // This all fails in light of running this test repeatedly as a stress test. Until we introduce + // idempotency and/or the ability to call client.delete we can't do this. But it will get fixed + // assertThat(result.isSuccessful()).isTrue(); + + // Not implemented yet + // result = client.delete(region, ""); + // assertThat(result.isSuccessful()).isTrue(); + } +} diff --git a/geode-web-management/src/integrationTest/java/org/apache/geode/management/internal/rest/RegionManagementIntegrationTest.java b/geode-web-management/src/integrationTest/java/org/apache/geode/management/internal/rest/RegionManagementIntegrationTest.java index 5df6a11..14ef4a2 100644 --- a/geode-web-management/src/integrationTest/java/org/apache/geode/management/internal/rest/RegionManagementIntegrationTest.java +++ b/geode-web-management/src/integrationTest/java/org/apache/geode/management/internal/rest/RegionManagementIntegrationTest.java @@ -44,8 +44,7 @@ import org.apache.geode.cache.configuration.RegionConfig; @WebAppConfiguration public class RegionManagementIntegrationTest { - static RequestPostProcessor POST_PROCESSOR = - new StandardRequestPostProcessor(); + static RequestPostProcessor POST_PROCESSOR = new StandardRequestPostProcessor(); @Autowired private WebApplicationContext webApplicationContext; diff --git a/geode-web-management/src/main/java/org/apache/geode/management/internal/rest/controllers/ManagementControllerAdvice.java b/geode-web-management/src/main/java/org/apache/geode/management/internal/rest/controllers/ManagementControllerAdvice.java index dba5499..9e8265d 100644 --- a/geode-web-management/src/main/java/org/apache/geode/management/internal/rest/controllers/ManagementControllerAdvice.java +++ b/geode-web-management/src/main/java/org/apache/geode/management/internal/rest/controllers/ManagementControllerAdvice.java @@ -26,7 +26,7 @@ import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.apache.geode.internal.logging.LogService; -import org.apache.geode.management.internal.api.ClusterManagementResult; +import org.apache.geode.management.api.ClusterManagementResult; import org.apache.geode.management.internal.exceptions.EntityExistsException; import org.apache.geode.security.AuthenticationFailedException; import org.apache.geode.security.NotAuthorizedException; @@ -50,7 +50,7 @@ public class ManagementControllerAdvice { } @ExceptionHandler({AuthenticationFailedException.class, AuthenticationException.class}) - public ResponseEntity<ClusterManagementResult> unauthorized(AuthenticationFailedException e) { + public ResponseEntity<ClusterManagementResult> unauthorized(Exception e) { return new ResponseEntity<>(new ClusterManagementResult(false, e.getMessage()), HttpStatus.UNAUTHORIZED); } diff --git a/geode-web-management/src/main/java/org/apache/geode/management/internal/rest/controllers/RegionManagementController.java b/geode-web-management/src/main/java/org/apache/geode/management/internal/rest/controllers/RegionManagementController.java index f1ebae2..e59c0a1 100644 --- a/geode-web-management/src/main/java/org/apache/geode/management/internal/rest/controllers/RegionManagementController.java +++ b/geode-web-management/src/main/java/org/apache/geode/management/internal/rest/controllers/RegionManagementController.java @@ -26,7 +26,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.apache.geode.cache.configuration.RegionConfig; -import org.apache.geode.management.internal.api.ClusterManagementResult; +import org.apache.geode.management.api.ClusterManagementResult; @Controller("regionManagement") @RequestMapping(MANAGEMENT_API_VERSION) diff --git a/geode-web-management/src/main/java/org/apache/geode/management/internal/rest/security/RestSecurityConfiguration.java b/geode-web-management/src/main/java/org/apache/geode/management/internal/rest/security/RestSecurityConfiguration.java index 2bc6bc6..1edbb79 100644 --- a/geode-web-management/src/main/java/org/apache/geode/management/internal/rest/security/RestSecurityConfiguration.java +++ b/geode-web-management/src/main/java/org/apache/geode/management/internal/rest/security/RestSecurityConfiguration.java @@ -37,7 +37,7 @@ import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.AuthenticationEntryPoint; -import org.apache.geode.management.internal.api.ClusterManagementResult; +import org.apache.geode.management.api.ClusterManagementResult; @Configuration @EnableWebSecurity