Java tutorial
/* * 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.test.junit.rules.gfsh; import static org.apache.commons.lang.SystemUtils.PATH_SEPARATOR; import static org.assertj.core.api.Assertions.assertThat; import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.function.Predicate; import java.util.stream.Collectors; import org.junit.rules.ExternalResource; import org.junit.rules.TemporaryFolder; import org.apache.geode.test.junit.rules.RequiresGeodeHome; /** * The {@code GfshRule} allows a test to execute Gfsh commands via the actual (fully-assembled) gfsh * binaries. Each call to {@link GfshRule#execute(GfshScript)} will invoke the given gfsh script in * a forked JVM. The {@link GfshRule#after()} method will attempt to clean up all forked JVMs. */ public class GfshRule extends ExternalResource { private static final String DOUBLE_QUOTE = "\""; private TemporaryFolder temporaryFolder = new TemporaryFolder(); private List<GfshExecution> gfshExecutions; private Path gfsh; @Override protected void before() throws IOException { gfsh = new RequiresGeodeHome().getGeodeHome().toPath().resolve("bin/gfsh"); assertThat(gfsh).exists(); gfshExecutions = new ArrayList<>(); temporaryFolder.create(); } /** * Attempts to stop any started servers/locators via pid file and tears down any remaining gfsh * JVMs. */ @Override protected void after() { gfshExecutions.stream().map(GfshExecution::getWorkingDir).collect(Collectors.toList()) .forEach(this::stopMembersQuietly); gfshExecutions.stream().map(GfshExecution::getProcess).map(Process::destroyForcibly) .forEach((Process process) -> { try { // Process.destroyForcibly() may not terminate immediately process.waitFor(1, TimeUnit.MINUTES); } catch (InterruptedException ignore) { // We ignore this exception so that we still attempt the rest of the cleanup. } }); temporaryFolder.delete(); } public TemporaryFolder getTemporaryFolder() { return temporaryFolder; } public GfshExecution execute(String... commands) { return execute(GfshScript.of(commands)); } public GfshExecution execute(GfshScript gfshScript) { GfshExecution gfshExecution; try { File workingDir = new File(temporaryFolder.getRoot(), gfshScript.getName()); workingDir.mkdirs(); Process process = toProcessBuilder(gfshScript, gfsh, workingDir).start(); gfshExecution = new GfshExecution(process, workingDir); gfshExecutions.add(gfshExecution); gfshScript.awaitIfNecessary(process); } catch (IOException e) { throw new UncheckedIOException(e); } return gfshExecution; } protected ProcessBuilder toProcessBuilder(GfshScript gfshScript, Path gfshPath, File workingDir) { List<String> commandsToExecute = new ArrayList<>(); commandsToExecute.add(gfshPath.toAbsolutePath().toString()); for (String command : gfshScript.getCommands()) { commandsToExecute.add("-e " + command); } ProcessBuilder processBuilder = new ProcessBuilder(commandsToExecute); processBuilder.directory(workingDir); List<String> extendedClasspath = gfshScript.getExtendedClasspath(); if (!extendedClasspath.isEmpty()) { Map<String, String> environmentMap = processBuilder.environment(); String classpathKey = "CLASSPATH"; String existingJavaArgs = environmentMap.get(classpathKey); String specified = String.join(PATH_SEPARATOR, extendedClasspath); String newValue = String.format("%s%s", existingJavaArgs == null ? "" : existingJavaArgs + ":", specified); environmentMap.put(classpathKey, newValue); } return processBuilder; } private void stopMembersQuietly(File parentDirectory) { File[] potentialMemberDirectories = parentDirectory.listFiles(File::isDirectory); Predicate<File> isServerDir = (File directory) -> Arrays.stream(directory.list()) .anyMatch(filename -> filename.endsWith("server.pid")); Predicate<File> isLocatorDir = (File directory) -> Arrays.stream(directory.list()) .anyMatch(filename -> filename.endsWith("locator.pid")); Arrays.stream(potentialMemberDirectories).filter(isServerDir).forEach(this::stopServerInDir); Arrays.stream(potentialMemberDirectories).filter(isLocatorDir).forEach(this::stopLocatorInDir); } private void stopServerInDir(File dir) { String stopServerCommand = "stop server --dir=" + quoteArgument(dir.toString()); GfshScript stopServerScript = new GfshScript(stopServerCommand).withName("teardown-stop-server") .awaitQuietly(); execute(stopServerScript); } private void stopLocatorInDir(File dir) { String stopLocatorCommand = "stop locator --dir=" + quoteArgument(dir.toString()); GfshScript stopServerScript = new GfshScript(stopLocatorCommand).withName("teardown-stop-locator") .awaitQuietly(); execute(stopServerScript); } private String quoteArgument(String argument) { if (!argument.startsWith(DOUBLE_QUOTE)) { argument = DOUBLE_QUOTE + argument; } if (!argument.endsWith(DOUBLE_QUOTE)) { argument = argument + DOUBLE_QUOTE; } return argument; } }