blob: 9d5678e4fdc897f1ce6aa97ceb67563724ca7713 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License. You may obtain a
* copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package org.apache.geode.management;
import static java.lang.management.ManagementFactory.getPlatformMBeanServer;
import static java.util.Arrays.asList;
import static javax.management.ObjectName.getInstance;
import static org.apache.geode.cache.Region.SEPARATOR;
import static org.apache.geode.distributed.ConfigurationProperties.HTTP_SERVICE_PORT;
import static org.apache.geode.distributed.ConfigurationProperties.JMX_MANAGER;
import static org.apache.geode.distributed.ConfigurationProperties.JMX_MANAGER_PORT;
import static org.apache.geode.distributed.ConfigurationProperties.JMX_MANAGER_START;
import static org.apache.geode.distributed.ConfigurationProperties.LOCATORS;
import static org.apache.geode.distributed.ConfigurationProperties.LOG_FILE;
import static org.apache.geode.distributed.ConfigurationProperties.MAX_WAIT_TIME_RECONNECT;
import static org.apache.geode.distributed.ConfigurationProperties.MEMBER_TIMEOUT;
import static org.apache.geode.distributed.internal.membership.api.MembershipManagerHelper.crashDistributedSystem;
import static org.apache.geode.internal.AvailablePortHelper.getRandomAvailableTCPPorts;
import static org.apache.geode.test.awaitility.GeodeAwaitility.await;
import static org.apache.geode.test.dunit.IgnoredException.addIgnoredException;
import static org.apache.geode.test.dunit.VM.getVM;
import static org.apache.geode.test.dunit.VM.getVMId;
import static org.apache.geode.util.internal.GeodeGlossary.GEMFIRE_PREFIX;
import static org.assertj.core.api.Assertions.assertThat;
import java.io.File;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.Callable;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.QueryExp;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.apache.geode.CancelException;
import org.apache.geode.ForcedDisconnectException;
import org.apache.geode.alerting.internal.spi.AlertingIOException;
import org.apache.geode.cache.CacheClosedException;
import org.apache.geode.distributed.DistributedSystemDisconnectedException;
import org.apache.geode.distributed.LocatorLauncher;
import org.apache.geode.distributed.ServerLauncher;
import org.apache.geode.distributed.internal.InternalLocator;
import org.apache.geode.distributed.internal.membership.api.MemberDisconnectedException;
import org.apache.geode.test.dunit.VM;
import org.apache.geode.test.dunit.rules.DistributedErrorCollector;
import org.apache.geode.test.dunit.rules.DistributedReference;
import org.apache.geode.test.dunit.rules.DistributedRestoreSystemProperties;
import org.apache.geode.test.dunit.rules.DistributedRule;
import org.apache.geode.test.junit.categories.JMXTest;
import org.apache.geode.test.junit.rules.GfshCommandRule;
import org.apache.geode.test.junit.rules.serializable.SerializableTemporaryFolder;
@Category(JMXTest.class)
@SuppressWarnings({"serial", "CodeBlock2Expr", "SameParameterValue"})
public class JmxLocatorReconnectDistributedTest implements Serializable {
private static final String LOCATOR_NAME = "locator";
private static final String SERVER_NAME = "server";
private static final String REGION_NAME = "region";
private static final QueryExp QUERY_ALL = null;
private static final ObjectName GEMFIRE_MXBEANS =
execute(() -> getInstance("GemFire:*"));
private static final Set<ObjectName> EXPECTED_SERVER_MXBEANS =
execute(() -> expectedServerMXBeans(SERVER_NAME, REGION_NAME));
private static final Set<ObjectName> EXPECTED_LOCATOR_MXBEANS =
execute(() -> expectedLocatorMXBeans(LOCATOR_NAME));
private static final Set<ObjectName> EXPECTED_DISTRIBUTED_MXBEANS =
execute(() -> expectedDistributedMXBeans(REGION_NAME));
private VM locatorVM;
private VM serverVM;
private String locators;
private int locatorPort;
private int locatorJmxPort;
private Set<ObjectName> mxbeansOnServer;
private Set<ObjectName> mxbeansOnLocator;
@Rule
public DistributedRule distributedRule = new DistributedRule(2);
@Rule
public DistributedErrorCollector errorCollector = new DistributedErrorCollector();
@Rule
public DistributedReference<LocatorLauncher> locatorLauncher = new DistributedReference<>();
@Rule
public DistributedReference<ServerLauncher> serverLauncher = new DistributedReference<>();
@Rule
public DistributedRestoreSystemProperties restoreProps = new DistributedRestoreSystemProperties();
@Rule
public SerializableTemporaryFolder temporaryFolder = new SerializableTemporaryFolder();
@Rule
public transient GfshCommandRule gfsh = new GfshCommandRule();
@Before
public void setUp() throws Exception {
locatorVM = getVM(0);
serverVM = getVM(1);
File locatorDir = temporaryFolder.newFolder(LOCATOR_NAME);
File serverDir = temporaryFolder.newFolder(SERVER_NAME);
for (VM vm : asList(locatorVM, serverVM)) {
vm.invoke(() -> System.setProperty(GEMFIRE_PREFIX + "standard-output-always-on", "true"));
}
int[] port = getRandomAvailableTCPPorts(2);
locatorPort = port[0];
locatorJmxPort = port[1];
locators = "localhost[" + locatorPort + "]";
locatorVM.invoke(() -> {
locatorLauncher.set(startLocator(locatorDir, locatorPort, locatorJmxPort, locators));
});
serverVM.invoke(() -> serverLauncher.set(startServer(serverDir, locators)));
gfsh.connectAndVerify(locatorJmxPort, GfshCommandRule.PortType.jmxManager);
String createRegionCommand = "create region --type=REPLICATE --name=" + SEPARATOR + REGION_NAME;
gfsh.executeAndAssertThat(createRegionCommand).statusIsSuccess();
addIgnoredException(AlertingIOException.class);
addIgnoredException(CacheClosedException.class);
addIgnoredException(CancelException.class);
addIgnoredException(DistributedSystemDisconnectedException.class);
addIgnoredException(ForcedDisconnectException.class);
addIgnoredException(MemberDisconnectedException.class);
addIgnoredException("Possible loss of quorum");
mxbeansOnServer = serverVM.invoke(() -> {
await().untilAsserted(() -> {
assertThat(getPlatformMBeanServer().queryNames(GEMFIRE_MXBEANS, QUERY_ALL))
.as("GemFire mxbeans on server")
.containsAll(EXPECTED_SERVER_MXBEANS);
});
return getPlatformMBeanServer().queryNames(GEMFIRE_MXBEANS, QUERY_ALL);
});
mxbeansOnLocator = locatorVM.invoke(() -> {
await().untilAsserted(() -> {
assertThat(getPlatformMBeanServer().queryNames(GEMFIRE_MXBEANS, QUERY_ALL))
.as("GemFire mxbeans on locator")
.containsAll(EXPECTED_SERVER_MXBEANS)
.containsAll(EXPECTED_LOCATOR_MXBEANS)
.containsAll(EXPECTED_DISTRIBUTED_MXBEANS);
});
return getPlatformMBeanServer().queryNames(GEMFIRE_MXBEANS, QUERY_ALL);
});
}
@Test
public void serverMXBeanOnServerIsUnaffectedByLocatorCrash() {
locatorVM.invoke(() -> {
crashDistributedSystem(locatorLauncher.get().getCache().getDistributedSystem());
await().untilAsserted(() -> {
assertThat(getPlatformMBeanServer().queryNames(GEMFIRE_MXBEANS, QUERY_ALL))
.as("GemFire mxbeans on locator")
.isEmpty();
});
});
serverVM.invoke(() -> {
assertThat(getPlatformMBeanServer().queryNames(GEMFIRE_MXBEANS, QUERY_ALL))
.as("GemFire mxbeans on server")
.containsExactlyInAnyOrderElementsOf(mxbeansOnServer);
});
locatorVM.invoke(() -> {
InternalLocator locator = (InternalLocator) locatorLauncher.get().getLocator();
await().untilAsserted(() -> {
boolean isReconnected = locator.isReconnected();
boolean isSharedConfigurationRunning = locator.isSharedConfigurationRunning();
Set<ObjectName> mbeanNames =
getPlatformMBeanServer().queryNames(GEMFIRE_MXBEANS, QUERY_ALL);
assertThat(isReconnected)
.as("Locator is reconnected on locator")
.isTrue();
assertThat(isSharedConfigurationRunning)
.as("Locator shared configuration is running on locator")
.isTrue();
assertThat(mbeanNames)
.as("GemFire mxbeans on locator")
.isEqualTo(mxbeansOnLocator);
});
});
serverVM.invoke(() -> {
assertThat(getPlatformMBeanServer().queryNames(GEMFIRE_MXBEANS, QUERY_ALL))
.as("GemFire mxbeans on server")
.containsExactlyInAnyOrderElementsOf(mxbeansOnServer);
});
}
private static LocatorLauncher startLocator(File workingDirectory, int locatorPort, int jmxPort,
String locators) {
LocatorLauncher locatorLauncher = new LocatorLauncher.Builder()
.setMemberName(LOCATOR_NAME)
.setPort(locatorPort)
.setWorkingDirectory(workingDirectory.getAbsolutePath())
.set(HTTP_SERVICE_PORT, "0")
.set(JMX_MANAGER, "true")
.set(JMX_MANAGER_PORT, String.valueOf(jmxPort))
.set(JMX_MANAGER_START, "true")
.set(LOCATORS, locators)
.set(LOG_FILE, new File(workingDirectory, LOCATOR_NAME + ".log").getAbsolutePath())
.set(MAX_WAIT_TIME_RECONNECT, "1000")
.set(MEMBER_TIMEOUT, "2000")
.build();
locatorLauncher.start();
InternalLocator locator = (InternalLocator) locatorLauncher.getLocator();
await().untilAsserted(() -> {
assertThat(locator.isSharedConfigurationRunning())
.as("Locator shared configuration is running on locator" + getVMId())
.isTrue();
});
return locatorLauncher;
}
private static ServerLauncher startServer(File workingDirectory, String locators) {
ServerLauncher serverLauncher = new ServerLauncher.Builder()
.setDisableDefaultServer(true)
.setMemberName(SERVER_NAME)
.setWorkingDirectory(workingDirectory.getAbsolutePath())
.set(HTTP_SERVICE_PORT, "0")
.set(LOCATORS, locators)
.set(LOG_FILE, new File(workingDirectory, SERVER_NAME + ".log").getAbsolutePath())
.set(MAX_WAIT_TIME_RECONNECT, "1000")
.set(MEMBER_TIMEOUT, "2000")
.build();
serverLauncher.start();
return serverLauncher;
}
private static Set<ObjectName> expectedServerMXBeans(String memberName, String regionName)
throws MalformedObjectNameException {
return new HashSet<>(asList(
getInstance("GemFire:type=Member,member=" + memberName),
getInstance("GemFire:service=Region,name=" + SEPARATOR + regionName +
",type=Member,member=" + memberName)));
}
private static Set<ObjectName> expectedLocatorMXBeans(String memberName)
throws MalformedObjectNameException {
return new HashSet<>(asList(
getInstance("GemFire:service=DiskStore,name=cluster_config,type=Member,member=" +
memberName),
getInstance("GemFire:service=Locator,type=Member,member=" + memberName),
getInstance("GemFire:service=LockService,name=__CLUSTER_CONFIG_LS,type=Member,member=" +
memberName),
getInstance("GemFire:type=Member,member=" + memberName),
getInstance("GemFire:service=Manager,type=Member,member=" + memberName)));
}
private static Set<ObjectName> expectedDistributedMXBeans(String regionName)
throws MalformedObjectNameException {
return new HashSet<>(asList(
getInstance("GemFire:service=AccessControl,type=Distributed"),
getInstance("GemFire:service=FileUploader,type=Distributed"),
getInstance("GemFire:service=LockService,name=__CLUSTER_CONFIG_LS,type=Distributed"),
getInstance("GemFire:service=Region,name=" + SEPARATOR + regionName + ",type=Distributed"),
getInstance("GemFire:service=System,type=Distributed")));
}
private static <V> V execute(Callable<V> task) {
try {
return task.call();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}