You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by je...@apache.org on 2015/12/30 01:28:26 UTC
[26/33] incubator-geode git commit: GEODE-473: Capture status file
lines in finally block
GEODE-473: Capture status file lines in finally block
Change FileProcessController status handler to capture lines read from
status file in finally block.
* Add new UnitTest for LocatorState
* Add new IntegrationTest for FileProcessController
Project: http://git-wip-us.apache.org/repos/asf/incubator-geode/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-geode/commit/121710cd
Tree: http://git-wip-us.apache.org/repos/asf/incubator-geode/tree/121710cd
Diff: http://git-wip-us.apache.org/repos/asf/incubator-geode/diff/121710cd
Branch: refs/heads/feature/GEODE-14
Commit: 121710cdd9558442ba6ee4ecdaea2fc2172a241e
Parents: 05b7ed1
Author: Kirk Lund <kl...@pivotal.io>
Authored: Mon Dec 21 13:02:11 2015 -0800
Committer: Kirk Lund <kl...@pivotal.io>
Committed: Tue Dec 22 14:26:28 2015 -0800
----------------------------------------------------------------------
.../internal/process/FileProcessController.java | 39 +++-
.../distributed/LocatorStateJUnitTest.java | 208 +++++++++++++++++++
...leProcessControllerIntegrationJUnitTest.java | 155 ++++++++++++++
3 files changed, 393 insertions(+), 9 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/121710cd/gemfire-core/src/main/java/com/gemstone/gemfire/internal/process/FileProcessController.java
----------------------------------------------------------------------
diff --git a/gemfire-core/src/main/java/com/gemstone/gemfire/internal/process/FileProcessController.java b/gemfire-core/src/main/java/com/gemstone/gemfire/internal/process/FileProcessController.java
index 471877f..f635bfb 100755
--- a/gemfire-core/src/main/java/com/gemstone/gemfire/internal/process/FileProcessController.java
+++ b/gemfire-core/src/main/java/com/gemstone/gemfire/internal/process/FileProcessController.java
@@ -20,6 +20,7 @@ import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
@@ -37,28 +38,44 @@ import com.gemstone.gemfire.lang.AttachAPINotFoundException;
* @author Kirk Lund
* @since 8.0
*/
-public final class FileProcessController implements ProcessController {
+public class FileProcessController implements ProcessController {
private static final Logger logger = LogService.getLogger();
public static final String STATUS_TIMEOUT_PROPERTY = "gemfire.FileProcessController.STATUS_TIMEOUT";
- private final long statusTimeout = Long.getLong(STATUS_TIMEOUT_PROPERTY, 60*1000);
+ private final long statusTimeoutMillis;
private final FileControllerParameters arguments;
private final int pid;
/**
* Constructs an instance for controlling a local process.
*
- * @param pid process id identifying the process to attach to
+ * @param arguments details about the controllable process
+ * @param pid process id identifying the process to control
*
* @throws IllegalArgumentException if pid is not a positive integer
*/
public FileProcessController(final FileControllerParameters arguments, final int pid) {
+ this(arguments, pid, Long.getLong(STATUS_TIMEOUT_PROPERTY, 60*1000), TimeUnit.MILLISECONDS);
+ }
+
+ /**
+ * Constructs an instance for controlling a local process.
+ *
+ * @param arguments details about the controllable process
+ * @param pid process id identifying the process to control
+ * @param timeout the timeout that operations must complete within
+ * @param units the units of the timeout
+ *
+ * @throws IllegalArgumentException if pid is not a positive integer
+ */
+ public FileProcessController(final FileControllerParameters arguments, final int pid, final long timeout, final TimeUnit units) {
if (pid < 1) {
throw new IllegalArgumentException("Invalid pid '" + pid + "' specified");
}
this.pid = pid;
this.arguments = arguments;
+ this.statusTimeoutMillis = units.toMillis(timeout);
}
@Override
@@ -98,14 +115,14 @@ public final class FileProcessController implements ProcessController {
public void handleRequest() throws IOException {
// read the statusFile
final BufferedReader reader = new BufferedReader(new FileReader(statusFile));
+ final StringBuilder lines = new StringBuilder();
try {
- final StringBuilder lines = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
lines.append(line);
}
- statusRef.set(lines.toString());
} finally {
+ statusRef.set(lines.toString());
reader.close();
}
}
@@ -122,8 +139,8 @@ public final class FileProcessController implements ProcessController {
// if timeout invoke stop and then throw TimeoutException
final long start = System.currentTimeMillis();
while (statusFileWatchdog.isAlive()) {
- Thread.sleep(100);
- if (System.currentTimeMillis() >= start + this.statusTimeout) {
+ Thread.sleep(10);
+ if (System.currentTimeMillis() >= start + this.statusTimeoutMillis) {
final TimeoutException te = new TimeoutException("Timed out waiting for process to create " + statusFile);
try {
statusFileWatchdog.stop();
@@ -135,7 +152,11 @@ public final class FileProcessController implements ProcessController {
throw te;
}
}
- assert statusRef.get() != null;
- return statusRef.get();
+
+ final String lines = statusRef.get();
+ if (null == lines || lines.trim().isEmpty()) {
+ throw new IllegalStateException("Failed to read status file");
+ }
+ return lines;
}
}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/121710cd/gemfire-core/src/test/java/com/gemstone/gemfire/distributed/LocatorStateJUnitTest.java
----------------------------------------------------------------------
diff --git a/gemfire-core/src/test/java/com/gemstone/gemfire/distributed/LocatorStateJUnitTest.java b/gemfire-core/src/test/java/com/gemstone/gemfire/distributed/LocatorStateJUnitTest.java
new file mode 100755
index 0000000..248c39f
--- /dev/null
+++ b/gemfire-core/src/test/java/com/gemstone/gemfire/distributed/LocatorStateJUnitTest.java
@@ -0,0 +1,208 @@
+/*
+ * 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 com.gemstone.gemfire.distributed;
+
+import static com.googlecode.catchexception.CatchException.*;
+import static org.assertj.core.api.Assertions.*;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import com.gemstone.gemfire.distributed.AbstractLauncher.ServiceState;
+import com.gemstone.gemfire.distributed.AbstractLauncher.Status;
+import com.gemstone.gemfire.distributed.LocatorLauncher.LocatorState;
+import com.gemstone.gemfire.management.internal.cli.json.GfJsonException;
+import com.gemstone.gemfire.management.internal.cli.json.GfJsonObject;
+import com.gemstone.gemfire.test.junit.categories.UnitTest;
+
+/**
+ * Unit tests for LocatorLauncher.LocatorState
+ */
+@Category(UnitTest.class)
+public class LocatorStateJUnitTest {
+
+ @Test
+ public void fromJsonWithEmptyStringThrowsIllegalArgumentException() throws Exception {
+ // given: empty string
+ String emptyString = "";
+
+ // when: passed to fromJson
+ verifyException(this).fromJson(emptyString);
+
+ // then: throws IllegalArgumentException with cause of GfJsonException
+ assertThat((Exception)caughtException())
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasCauseInstanceOf(GfJsonException.class);
+
+ assertThat(caughtException().getCause())
+ .isInstanceOf(GfJsonException.class)
+ .hasNoCause();
+ }
+
+ @Test
+ public void fromJsonWithWhiteSpaceStringThrowsIllegalArgumentException() throws Exception {
+ // given: white space string
+ String whiteSpaceString = " ";
+
+ // when: passed to fromJson
+ verifyException(this).fromJson(whiteSpaceString);
+
+ // then: throws IllegalArgumentException with cause of GfJsonException
+ assertThat((Exception)caughtException())
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasCauseInstanceOf(GfJsonException.class);
+
+ assertThat(caughtException().getCause())
+ .isInstanceOf(GfJsonException.class)
+ .hasNoCause();
+ }
+
+ @Test
+ public void fromJsonWithNullStringThrowsNullPointerException() throws Exception {
+ // given: null string
+ String nullString = null;
+
+ // when: passed to fromJson
+ verifyException(this).fromJson(nullString);
+
+ // then: throws NullPointerException
+ assertThat((Exception)caughtException())
+ .isInstanceOf(NullPointerException.class)
+ .hasNoCause();
+ }
+
+ @Test
+ public void fromJsonWithValidJsonStringReturnsLocatorState() throws Exception {
+ // given: valid json string
+ String jsonString = createStatusJson();
+
+ // when: passed to fromJson
+ LocatorState value = fromJson(jsonString);
+
+ // then: return valid instance of LocatorState
+ assertThat(value).isInstanceOf(LocatorState.class);
+
+ assertThat(value.getClasspath()).isEqualTo(getClasspath());
+ assertThat(value.getGemFireVersion()).isEqualTo(getGemFireVersion());
+ assertThat(value.getHost()).isEqualTo(getHost());
+ assertThat(value.getJavaVersion()).isEqualTo(getJavaVersion());
+ assertThat(value.getJvmArguments()).isEqualTo(getJvmArguments());
+ assertThat(value.getServiceLocation()).isEqualTo(getServiceLocation());
+ assertThat(value.getLogFile()).isEqualTo(getLogFile());
+ assertThat(value.getMemberName()).isEqualTo(getMemberName());
+ assertThat(value.getPid()).isEqualTo(getPid());
+ assertThat(value.getPort()).isEqualTo(getPort());
+ assertThat(value.getStatus().getDescription()).isEqualTo(getStatusDescription());
+ assertThat(value.getStatusMessage()).isEqualTo(getStatusMessage());
+ assertThat(value.getTimestamp().getTime()).isEqualTo(getTimestampTime());
+ assertThat(value.getUptime()).isEqualTo(getUptime());
+ assertThat(value.getWorkingDirectory()).isEqualTo(getWorkingDirectory());
+ }
+
+ protected LocatorState fromJson(final String value) {
+ return LocatorState.fromJson(value);
+ }
+
+ private String classpath = "test_classpath";
+ private String gemFireVersion = "test_gemfireversion";
+ private String host = "test_host";
+ private String javaVersion = "test_javaversion";
+ private String jvmArguments = "test_jvmarguments";
+ private String serviceLocation = "test_location";
+ private String logFile = "test_logfile";
+ private String memberName = "test_membername";
+ private Integer pid = 6396;
+ private String port = "test_port";
+ private String statusDescription = Status.NOT_RESPONDING.getDescription();
+ private String statusMessage = "test_statusmessage";
+ private Long timestampTime = 1450728233024L;
+ private Long uptime = 1629L;
+ private String workingDirectory = "test_workingdirectory";
+
+ private String getClasspath() {
+ return this.classpath;
+ }
+ private String getGemFireVersion() {
+ return this.gemFireVersion;
+ }
+ private String getHost() {
+ return this.host;
+ }
+ private String getJavaVersion() {
+ return this.javaVersion;
+ }
+ private List<String> getJvmArguments() {
+ List<String> list = new ArrayList<String>();
+ list.add(this.jvmArguments);
+ return list;
+ }
+ private String getServiceLocation() {
+ return this.serviceLocation;
+ }
+ private String getLogFile() {
+ return this.logFile;
+ }
+ private String getMemberName() {
+ return this.memberName;
+ }
+ private Integer getPid() {
+ return this.pid;
+ }
+ private String getPort() {
+ return this.port;
+ }
+ private String getStatusDescription() {
+ return this.statusDescription;
+ }
+ private String getStatusMessage() {
+ return this.statusMessage;
+ }
+ private Long getTimestampTime() {
+ return this.timestampTime;
+ }
+ private Long getUptime() {
+ return this.uptime;
+ }
+ private String getWorkingDirectory() {
+ return this.workingDirectory;
+ }
+
+ private String createStatusJson() {
+ final Map<String, Object> map = new HashMap<String, Object>();
+ map.put(ServiceState.JSON_CLASSPATH, getClasspath());
+ map.put(ServiceState.JSON_GEMFIREVERSION, getGemFireVersion());
+ map.put(ServiceState.JSON_HOST, getHost());
+ map.put(ServiceState.JSON_JAVAVERSION, getJavaVersion());
+ map.put(ServiceState.JSON_JVMARGUMENTS, getJvmArguments());
+ map.put(ServiceState.JSON_LOCATION, getServiceLocation());
+ map.put(ServiceState.JSON_LOGFILE, getLogFile());
+ map.put(ServiceState.JSON_MEMBERNAME, getMemberName());
+ map.put(ServiceState.JSON_PID, getPid());
+ map.put(ServiceState.JSON_PORT, getPort());
+ map.put(ServiceState.JSON_STATUS, getStatusDescription());
+ map.put(ServiceState.JSON_STATUSMESSAGE, getStatusMessage());
+ map.put(ServiceState.JSON_TIMESTAMP, getTimestampTime());
+ map.put(ServiceState.JSON_UPTIME, getUptime());
+ map.put(ServiceState.JSON_WORKINGDIRECTORY, getWorkingDirectory());
+ return new GfJsonObject(map).toString();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/121710cd/gemfire-core/src/test/java/com/gemstone/gemfire/internal/process/FileProcessControllerIntegrationJUnitTest.java
----------------------------------------------------------------------
diff --git a/gemfire-core/src/test/java/com/gemstone/gemfire/internal/process/FileProcessControllerIntegrationJUnitTest.java b/gemfire-core/src/test/java/com/gemstone/gemfire/internal/process/FileProcessControllerIntegrationJUnitTest.java
new file mode 100755
index 0000000..6255af1
--- /dev/null
+++ b/gemfire-core/src/test/java/com/gemstone/gemfire/internal/process/FileProcessControllerIntegrationJUnitTest.java
@@ -0,0 +1,155 @@
+/*
+ * 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 com.gemstone.gemfire.internal.process;
+
+import static com.googlecode.catchexception.CatchException.*;
+import static com.jayway.awaitility.Awaitility.*;
+import static java.util.concurrent.TimeUnit.*;
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+import static org.hamcrest.Matchers.*;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.rules.TemporaryFolder;
+import org.junit.rules.TestName;
+
+import com.gemstone.gemfire.distributed.LocatorLauncher;
+import com.gemstone.gemfire.distributed.LocatorStateJUnitTest;
+import com.gemstone.gemfire.distributed.AbstractLauncher.Status;
+import com.gemstone.gemfire.distributed.LocatorLauncher.Builder;
+import com.gemstone.gemfire.distributed.LocatorLauncher.LocatorState;
+import com.gemstone.gemfire.test.junit.categories.IntegrationTest;
+
+/**
+ * Integration tests for FileProcessController.
+ */
+@Category(IntegrationTest.class)
+public class FileProcessControllerIntegrationJUnitTest {
+
+ @Rule
+ public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ @Rule
+ public TestName testName = new TestName();
+
+ private ProcessType processType;
+ private ExecutorService executor;
+
+ @Before
+ public void setUp() throws Exception {
+ this.processType = ProcessType.LOCATOR;
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (this.executor != null) {
+ this.executor.shutdownNow();
+ }
+ }
+
+ @Test
+ public void statusShouldAwaitTimeoutWhileFileIsEmpty() throws Exception {
+ // given: FileProcessController with empty pidFile
+ int pid = ProcessUtils.identifyPid();
+ File emptyPidFile = this.temporaryFolder.newFile(this.processType.getPidFileName());
+ FileControllerParameters params = mock(FileControllerParameters.class);
+ when(params.getPidFile()).thenReturn(emptyPidFile);
+ when(params.getProcessId()).thenReturn(pid);
+ when(params.getProcessType()).thenReturn(this.processType);
+ when(params.getWorkingDirectory()).thenReturn(this.temporaryFolder.getRoot());
+
+ FileProcessController controller = new FileProcessController(params, 1, 10, MILLISECONDS);
+
+ // when
+ verifyException(controller).status();
+
+ // then: we expect TimeoutException to be thrown
+ assertThat((Exception)caughtException())
+ .isInstanceOf(TimeoutException.class)
+ .hasMessageContaining("Timed out waiting for process to create")
+ .hasNoCause();
+ }
+
+ @Test
+ public void statusShouldReturnJsonFromStatusFile() throws Exception {
+ // given: FileProcessController with pidFile containing real pid
+ int pid = ProcessUtils.identifyPid();
+ File pidFile = this.temporaryFolder.newFile(this.processType.getPidFileName());
+ writeToFile(pidFile, String.valueOf(pid));
+
+ FileControllerParameters params = mock(FileControllerParameters.class);
+ when(params.getPidFile()).thenReturn(pidFile);
+ when(params.getProcessId()).thenReturn(pid);
+ when(params.getProcessType()).thenReturn(this.processType);
+ when(params.getWorkingDirectory()).thenReturn(this.temporaryFolder.getRoot());
+
+ FileProcessController controller = new FileProcessController(params, pid, 1, MINUTES);
+
+ // when: status is called in one thread and json is written to the file
+ AtomicReference<String> status = new AtomicReference<String>();
+ AtomicReference<Exception> exception = new AtomicReference<Exception>();
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+ executor.execute(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ status.set(controller.status());
+ } catch (Exception e) {
+ exception.set(e);
+ }
+ }
+ });
+
+ // write status
+ String statusJson = generateStatusJson();
+ File statusFile = this.temporaryFolder.newFile(this.processType.getStatusFileName());
+ writeToFile(statusFile, statusJson);
+
+ // then: returned status should be the json in the file
+ assertThat(exception.get()).isNull();
+ with().pollInterval(10, MILLISECONDS).await().atMost(2, MINUTES).untilAtomic(status, equalTo(statusJson));
+ assertThat(status.get()).isEqualTo(statusJson);
+ System.out.println(statusJson);
+ }
+
+ private static void writeToFile(final File file, final String value) throws IOException {
+ final FileWriter writer = new FileWriter(file);
+ writer.write(value);
+ writer.flush();
+ writer.close();
+ }
+
+ private static String generateStatusJson() {
+ Builder builder = new Builder();
+ LocatorLauncher defaultLauncher = builder.build();
+ Status status = Status.ONLINE;
+ LocatorState locatorState = new LocatorState(defaultLauncher, status);
+ return locatorState.toJson();
+ }
+}