Repository: geode Updated Branches: refs/heads/feature/GEODE-2267 450516464 -> 112f26699
GEODE-2418: enable gfsh to download file from http connection Project: http://git-wip-us.apache.org/repos/asf/geode/repo Commit: http://git-wip-us.apache.org/repos/asf/geode/commit/4a700206 Tree: http://git-wip-us.apache.org/repos/asf/geode/tree/4a700206 Diff: http://git-wip-us.apache.org/repos/asf/geode/diff/4a700206 Branch: refs/heads/feature/GEODE-2267 Commit: 4a700206973e72099550aefbf89e3f29f52daf54 Parents: 4505164 Author: Jinmei Liao <[email protected]> Authored: Tue Feb 21 15:54:12 2017 -0800 Committer: Jinmei Liao <[email protected]> Committed: Wed Feb 22 09:14:50 2017 -0800 ---------------------------------------------------------------------- .../geode/management/cli/CliMetaData.java | 8 ++ .../cli/AbstractCliAroundInterceptor.java | 4 +- .../internal/cli/CliAroundInterceptor.java | 29 ++++- .../management/internal/cli/CommandRequest.java | 15 ++- .../cli/commands/MiscellaneousCommands.java | 126 ++++++++++++------- .../cli/functions/ExportLogsFunction.java | 7 +- .../internal/cli/i18n/CliStrings.java | 30 ++++- .../cli/shell/GfshExecutionStrategy.java | 62 +++++---- .../cli/util/ExportLogsCacheWriter.java | 10 +- .../MiscellaneousCommandsController.java | 41 ++++-- .../web/shell/AbstractHttpOperationInvoker.java | 42 ++++--- .../web/shell/RestHttpOperationInvoker.java | 35 +++--- .../web/shell/SimpleHttpOperationInvoker.java | 12 +- .../dunit/rules/GfshShellConnectionRule.java | 26 ++-- .../cli/commands/ExportLogsOverHttpDunit.java | 113 +++++++++++++++++ .../RestHttpOperationInvokerJUnitTest.java | 46 +++---- .../SimpleHttpOperationInvokerJUnitTest.java | 18 +-- 17 files changed, 437 insertions(+), 187 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/geode/blob/4a700206/geode-core/src/main/java/org/apache/geode/management/cli/CliMetaData.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/management/cli/CliMetaData.java b/geode-core/src/main/java/org/apache/geode/management/cli/CliMetaData.java index a20fba5..dc7d741 100644 --- a/geode-core/src/main/java/org/apache/geode/management/cli/CliMetaData.java +++ b/geode-core/src/main/java/org/apache/geode/management/cli/CliMetaData.java @@ -44,6 +44,14 @@ public @interface CliMetaData { boolean shellOnly() default false; /** + * Indicates when executed over http, is this command downloading files from the member. + * When this is set to true, the RestHttpOperationInvoker will use an extractor to extract + * the inputstream in the response to a temporary file and it's up to your command's interceptor's + * postExecution to use that temp file to fit your need. + **/ + boolean isFileDownloadOverHttp() default false; + + /** * Indicates that the effect of the command is persisted or the commands affects the persistent * configuration */ http://git-wip-us.apache.org/repos/asf/geode/blob/4a700206/geode-core/src/main/java/org/apache/geode/management/internal/cli/AbstractCliAroundInterceptor.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/AbstractCliAroundInterceptor.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/AbstractCliAroundInterceptor.java index de24727..3e1357d 100644 --- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/AbstractCliAroundInterceptor.java +++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/AbstractCliAroundInterceptor.java @@ -14,10 +14,10 @@ */ package org.apache.geode.management.internal.cli; -import java.io.IOException; - import org.apache.geode.management.internal.cli.shell.Gfsh; +import java.io.IOException; + /** * Semi-complete implementation of {@link CliAroundInterceptor} for convenience for implementors. * http://git-wip-us.apache.org/repos/asf/geode/blob/4a700206/geode-core/src/main/java/org/apache/geode/management/internal/cli/CliAroundInterceptor.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/CliAroundInterceptor.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/CliAroundInterceptor.java index 11d74c1..2eeebb5 100644 --- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/CliAroundInterceptor.java +++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/CliAroundInterceptor.java @@ -15,8 +15,11 @@ package org.apache.geode.management.internal.cli; import org.apache.geode.management.cli.Result; +import org.apache.geode.management.internal.cli.result.ResultBuilder; import org.apache.geode.management.internal.cli.shell.GfshExecutionStrategy; +import java.nio.file.Path; + /** * Interceptor interface which {@link GfshExecutionStrategy} can use to intercept before & after * actual command execution. @@ -26,8 +29,30 @@ import org.apache.geode.management.internal.cli.shell.GfshExecutionStrategy; */ public interface CliAroundInterceptor { - public Result preExecution(GfshParseResult parseResult); + /** + * called by the OperationInvoker before the command is executed + * @param parseResult + * @return + */ + default public Result preExecution(GfshParseResult parseResult) { + return ResultBuilder.createInfoResult(""); + } + + @Deprecated + default public Result postExecution(GfshParseResult parseResult, Result commandResult){ + return commandResult; + } - public Result postExecution(GfshParseResult parseResult, Result commandResult); + /** + * called by the OperationInvoker after the command is executed + * @param parseResult + * @param commandResult + * @param tempFile: if the command's isFileDownloadOverHttp is true, the is the File downloaded + * after the http response is processed. + * @return + */ + default public Result postExecution(GfshParseResult parseResult, Result commandResult, Path tempFile) { + return postExecution(parseResult, commandResult); + } } http://git-wip-us.apache.org/repos/asf/geode/blob/4a700206/geode-core/src/main/java/org/apache/geode/management/internal/cli/CommandRequest.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/CommandRequest.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/CommandRequest.java index b0242c9..a135340 100644 --- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/CommandRequest.java +++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/CommandRequest.java @@ -14,12 +14,13 @@ */ package org.apache.geode.management.internal.cli; +import org.apache.geode.internal.lang.StringUtils; +import org.apache.geode.management.cli.CliMetaData; + import java.util.Collections; import java.util.HashMap; import java.util.Map; -import org.apache.geode.internal.lang.StringUtils; - /** * The CommandRequest class encapsulates information pertaining to the command the user entered in * Gfsh. @@ -39,6 +40,7 @@ public class CommandRequest { private final Map<String, String> customParameters = new HashMap<String, String>(); private final Map<String, String> env; + private boolean downloadFile = false; private String customInput; @@ -65,6 +67,11 @@ public class CommandRequest { this.env = env; this.fileData = fileData; this.parseResult = parseResult; + + CliMetaData metaData = parseResult.getMethod().getDeclaredAnnotation(CliMetaData.class); + if(metaData.isFileDownloadOverHttp()){ + downloadFile = true; + } } public String getName() { @@ -88,6 +95,10 @@ public class CommandRequest { return customInput; } + public boolean isDownloadFile(){ + return downloadFile; + } + public void setCustomInput(final String input) { this.customInput = input; } http://git-wip-us.apache.org/repos/asf/geode/blob/4a700206/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/MiscellaneousCommands.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/MiscellaneousCommands.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/MiscellaneousCommands.java index e720d09..1da3d68 100644 --- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/MiscellaneousCommands.java +++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/MiscellaneousCommands.java @@ -14,42 +14,8 @@ */ package org.apache.geode.management.internal.cli.commands; -import java.io.BufferedInputStream; -import java.io.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.PrintWriter; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.zip.DataFormatException; -import java.util.zip.GZIPInputStream; -import javax.management.ObjectName; - import org.apache.commons.io.FileUtils; +import org.apache.commons.lang.StringUtils; import org.apache.geode.LogWriter; import org.apache.geode.cache.Cache; import org.apache.geode.cache.CacheFactory; @@ -108,19 +74,49 @@ import org.apache.geode.management.internal.cli.result.ResultData; import org.apache.geode.management.internal.cli.result.ResultDataException; import org.apache.geode.management.internal.cli.result.TabularResultData; import org.apache.geode.management.internal.cli.shell.Gfsh; -import org.apache.geode.management.internal.cli.util.MergeLogs; import org.apache.geode.management.internal.cli.util.ExportLogsCacheWriter; import org.apache.geode.management.internal.configuration.utils.ZipUtils; import org.apache.geode.management.internal.security.ResourceOperation; import org.apache.geode.security.ResourcePermission.Operation; import org.apache.geode.security.ResourcePermission.Resource; - import org.apache.logging.log4j.Logger; import org.springframework.shell.core.CommandMarker; import org.springframework.shell.core.annotation.CliAvailabilityIndicator; import org.springframework.shell.core.annotation.CliCommand; import org.springframework.shell.core.annotation.CliOption; +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.zip.DataFormatException; +import java.util.zip.GZIPInputStream; +import javax.management.ObjectName; + /** * @since GemFire 7.0 */ @@ -129,6 +125,7 @@ public class MiscellaneousCommands implements CommandMarker { public final static String FORMAT = "yyyy/MM/dd/HH/mm/ss/SSS/z"; public final static String ONLY_DATE_FORMAT = "yyyy/MM/dd"; public final static String DEFAULT_TIME_OUT = "10"; + private static Logger logger = LogService.getLogger(); private final GetStackTracesFunction getStackTracesFunction = new GetStackTracesFunction(); @@ -139,8 +136,6 @@ public class MiscellaneousCommands implements CommandMarker { public void shutdownNode(final long timeout, final Set<DistributedMember> includeMembers) throws TimeoutException, InterruptedException, ExecutionException { - Cache cache = CacheFactory.getAnyInstance(); - LogWriter logger = cache.getLogger(); ExecutorService exec = Executors.newSingleThreadExecutor(); try { final Function shutDownFunction = new ShutDownFunction(); @@ -691,11 +686,13 @@ public class MiscellaneousCommands implements CommandMarker { @CliCommand(value = CliStrings.EXPORT_LOGS, help = CliStrings.EXPORT_LOGS__HELP) @CliMetaData(shellOnly = false, + isFileDownloadOverHttp = true, + interceptor = "org.apache.geode.management.internal.cli.commands.MiscellaneousCommands$ExportLogsInterceptor", relatedTopic = {CliStrings.TOPIC_GEODE_SERVER, CliStrings.TOPIC_GEODE_DEBUG_UTIL}) @ResourceOperation(resource = Resource.CLUSTER, operation = Operation.READ) public Result exportLogs( @CliOption(key = CliStrings.EXPORT_LOGS__DIR, help = CliStrings.EXPORT_LOGS__DIR__HELP, - mandatory = true) String dirName, + mandatory = false) String dirName, @CliOption(key = CliStrings.EXPORT_LOGS__GROUP, unspecifiedDefaultValue = CliMetaData.ANNOTATION_NULL_VALUE, optionContext = ConverterHint.MEMBERGROUP, @@ -719,12 +716,11 @@ public class MiscellaneousCommands implements CommandMarker { unspecifiedDefaultValue = CliMetaData.ANNOTATION_NULL_VALUE, help = CliStrings.EXPORT_LOGS__ENDTIME__HELP) String end) { Result result = null; - Logger logger = LogService.getLogger(); - try { GemFireCacheImpl cache = GemFireCacheImpl.getInstance(); - Set<DistributedMember> targetMembers = CliUtil.findMembersIncludingLocators(groups, memberIds); + Set<DistributedMember> targetMembers = + CliUtil.findMembersIncludingLocators(groups, memberIds); Map<String, Path> zipFilesFromMembers = new HashMap<>(); @@ -736,7 +732,8 @@ public class MiscellaneousCommands implements CommandMarker { cacheWriter.startFile(server.getName()); - CliUtil.executeFunction(new ExportLogsFunction(), + CliUtil + .executeFunction(new ExportLogsFunction(), new ExportLogsFunction.Args(start, end, logLevel, onlyLogLevel), server) .getResult(); Path zipFile = cacheWriter.endFile(); @@ -756,12 +753,13 @@ public class MiscellaneousCommands implements CommandMarker { } Path workingDir = Paths.get(System.getProperty("user.dir")); - Path exportedLogsZipFile = workingDir.resolve("exportedLogs[" + System.currentTimeMillis() + "].zip").toAbsolutePath(); + Path exportedLogsZipFile = workingDir + .resolve("exportedLogs_" + System.currentTimeMillis() + ".zip").toAbsolutePath(); logger.info("Zipping into: " + exportedLogsZipFile.toString()); ZipUtils.zipDirectory(exportedLogsDir, exportedLogsZipFile); FileUtils.deleteDirectory(tempDir.toFile()); - result = ResultBuilder.createInfoResult("File exported to: " + exportedLogsZipFile.toString()); + result = ResultBuilder.createInfoResult(exportedLogsZipFile.toString()); } catch (Exception ex) { ex.printStackTrace(); logger.error(ex, ex); @@ -774,7 +772,40 @@ public class MiscellaneousCommands implements CommandMarker { return result; } - + /** + * after the export logs, will need to copy the tempFile to the desired location + * and delete the temp file. + */ + public static class ExportLogsInterceptor extends AbstractCliAroundInterceptor { + @Override + public Result postExecution(GfshParseResult parseResult, Result commandResult, Path tempFile) { + // in the command over http case, the command result is in the downloaded temp file + if(tempFile!=null){ + Path dirPath; + String dirName = parseResult.getParamValueStrings().get("dir"); + if(StringUtils.isBlank(dirName)){ + dirPath = Paths.get(System.getProperty("user.dir")); + } + else{ + dirPath = Paths.get(dirName); + } + String fileName = "exportedLogs_"+System.currentTimeMillis()+".zip"; + File exportedLogFile = dirPath.resolve(fileName).toFile(); + try { + FileUtils.copyFile(tempFile.toFile(), exportedLogFile); + FileUtils.deleteQuietly(tempFile.toFile()); + commandResult = ResultBuilder.createInfoResult("Logs exported to: "+exportedLogFile.getAbsolutePath()); + } catch (IOException e) { + logger.error(e.getMessage(), e); + commandResult = ResultBuilder.createGemFireErrorResult(e.getMessage()); + } + } + else{ + commandResult = ResultBuilder.createInfoResult("Logs exported to the connected locator's file system: " + commandResult.nextLine()); + } + return commandResult; + } + } /**** * Current implementation supports writing it to a file and returning the location of the file @@ -1005,6 +1036,7 @@ public class MiscellaneousCommands implements CommandMarker { result = ResultBuilder.buildResult(getSystemWideMetrics(export_to_report_to, categories)); } } catch (Exception e) { + logger.error(e.getMessage(), e); return ResultBuilder.createGemFireErrorResult("#SB" + CliUtil.stackTraceAsString(e)); } return result; http://git-wip-us.apache.org/repos/asf/geode/blob/4a700206/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/ExportLogsFunction.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/ExportLogsFunction.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/ExportLogsFunction.java index 1dc89e9..560e8aa 100644 --- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/ExportLogsFunction.java +++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/ExportLogsFunction.java @@ -17,7 +17,6 @@ package org.apache.geode.management.internal.cli.functions; import static java.util.stream.Collectors.toSet; -import static org.apache.geode.distributed.internal.DistributionManager.LOCATOR_DM_TYPE; import org.apache.geode.cache.AttributesFactory; import org.apache.geode.cache.DataPolicy; @@ -99,10 +98,6 @@ public class ExportLogsFunction implements Function, InternalEntity { } } - protected static boolean isLocator(GemFireCacheImpl cache) { - return cache.getMyId().getVmKind() == LOCATOR_DM_TYPE; - } - public static Region createOrGetExistingExportLogsRegion(boolean isInitiatingMember) throws IOException, ClassNotFoundException { GemFireCacheImpl cache = GemFireCacheImpl.getInstance(); @@ -134,7 +129,7 @@ public class ExportLogsFunction implements Function, InternalEntity { return; } - exportLogsRegion.destroyRegion(); + exportLogsRegion.destroyRegion(); } http://git-wip-us.apache.org/repos/asf/geode/blob/4a700206/geode-core/src/main/java/org/apache/geode/management/internal/cli/i18n/CliStrings.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/i18n/CliStrings.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/i18n/CliStrings.java index c536e8e..cfba6d3 100644 --- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/i18n/CliStrings.java +++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/i18n/CliStrings.java @@ -14,18 +14,38 @@ */ package org.apache.geode.management.internal.cli.i18n; -import static org.apache.geode.distributed.ConfigurationProperties.*; - -import java.text.MessageFormat; +import static org.apache.geode.distributed.ConfigurationProperties.ARCHIVE_DISK_SPACE_LIMIT; +import static org.apache.geode.distributed.ConfigurationProperties.ARCHIVE_FILE_SIZE_LIMIT; +import static org.apache.geode.distributed.ConfigurationProperties.CACHE_XML_FILE; +import static org.apache.geode.distributed.ConfigurationProperties.DURABLE_CLIENT_ID; +import static org.apache.geode.distributed.ConfigurationProperties.ENABLE_CLUSTER_CONFIGURATION; +import static org.apache.geode.distributed.ConfigurationProperties.ENABLE_TIME_STATISTICS; +import static org.apache.geode.distributed.ConfigurationProperties.JMX_MANAGER; +import static org.apache.geode.distributed.ConfigurationProperties.LOCATORS; +import static org.apache.geode.distributed.ConfigurationProperties.LOG_DISK_SPACE_LIMIT; +import static org.apache.geode.distributed.ConfigurationProperties.LOG_FILE_SIZE_LIMIT; +import static org.apache.geode.distributed.ConfigurationProperties.LOG_LEVEL; +import static org.apache.geode.distributed.ConfigurationProperties.MCAST_ADDRESS; +import static org.apache.geode.distributed.ConfigurationProperties.MCAST_PORT; +import static org.apache.geode.distributed.ConfigurationProperties.MEMCACHED_BIND_ADDRESS; +import static org.apache.geode.distributed.ConfigurationProperties.MEMCACHED_PORT; +import static org.apache.geode.distributed.ConfigurationProperties.MEMCACHED_PROTOCOL; +import static org.apache.geode.distributed.ConfigurationProperties.SERVER_BIND_ADDRESS; +import static org.apache.geode.distributed.ConfigurationProperties.SOCKET_BUFFER_SIZE; +import static org.apache.geode.distributed.ConfigurationProperties.STATISTIC_ARCHIVE_FILE; +import static org.apache.geode.distributed.ConfigurationProperties.STATISTIC_SAMPLE_RATE; +import static org.apache.geode.distributed.ConfigurationProperties.USE_CLUSTER_CONFIGURATION; import org.apache.geode.cache.PartitionAttributesFactory; import org.apache.geode.cache.server.CacheServer; import org.apache.geode.distributed.ConfigurationProperties; -import org.apache.geode.distributed.internal.DistributionConfig; import org.apache.geode.distributed.internal.ClusterConfigurationService; +import org.apache.geode.distributed.internal.DistributionConfig; import org.apache.geode.internal.cache.xmlcache.CacheXml; import org.apache.geode.management.internal.cli.shell.Gfsh; +import java.text.MessageFormat; + /** * - * Contains 'String' constants used as key to the Localized strings to be used in classes under * <code>org.apache.geode.management.internal.cli</code> for Command Line Interface (CLI). NOTES: 1. @@ -1371,7 +1391,7 @@ public class CliStrings { public static final String EXPORT_LOGS__HELP = "Export the log files for a member or members."; public static final String EXPORT_LOGS__DIR = "dir"; public static final String EXPORT_LOGS__DIR__HELP = - "Directory to which log files will be written."; + "Local directory to which log files will be written. This is only used when you are exporting logs using http connection. If no specified, user.dir will be used."; public static final String EXPORT_LOGS__MEMBER = "member"; public static final String EXPORT_LOGS__MEMBER__HELP = "Name/Id of the member whose log files will be exported."; http://git-wip-us.apache.org/repos/asf/geode/blob/4a700206/geode-core/src/main/java/org/apache/geode/management/internal/cli/shell/GfshExecutionStrategy.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/shell/GfshExecutionStrategy.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/shell/GfshExecutionStrategy.java index b53c53f..02744e5 100755 --- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/shell/GfshExecutionStrategy.java +++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/shell/GfshExecutionStrategy.java @@ -14,10 +14,7 @@ */ package org.apache.geode.management.internal.cli.shell; -import static org.apache.geode.management.internal.cli.multistep.CLIMultiStepHelper.*; - -import java.lang.reflect.Method; -import java.util.Map; +import static org.apache.geode.management.internal.cli.multistep.CLIMultiStepHelper.execCLISteps; import org.apache.geode.internal.ClassPathLoader; import org.apache.geode.management.cli.CliMetaData; @@ -35,13 +32,16 @@ import org.apache.geode.management.internal.cli.multistep.MultiStepCommand; import org.apache.geode.management.internal.cli.result.FileResult; import org.apache.geode.management.internal.cli.result.ResultBuilder; import org.apache.geode.security.NotAuthorizedException; - import org.springframework.shell.core.ExecutionStrategy; import org.springframework.shell.core.Shell; import org.springframework.shell.event.ParseResult; import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; +import java.lang.reflect.Method; +import java.nio.file.Path; +import java.util.Map; + /** * Defines the {@link ExecutionStrategy} for commands that are executed in GemFire SHell (gfsh). * @@ -235,10 +235,14 @@ public class GfshExecutionStrategy implements ExecutionStrategy { try { response = shell.getOperationInvoker() .processCommand(new CommandRequest(parseResult, env, fileData)); + } catch (NotAuthorizedException e) { return ResultBuilder .createGemFireUnAuthorizedErrorResult("Unauthorized. Reason : " + e.getMessage()); - } finally { + } catch (Exception e){ + e.printStackTrace(); + } + finally { env.clear(); } @@ -250,33 +254,39 @@ public class GfshExecutionStrategy implements ExecutionStrategy { + "Please check manager logs for error."); } - if (logWrapper.fineEnabled()) { - logWrapper.fine("Received response :: " + response); - } - CommandResponse commandResponse = - CommandResponseBuilder.prepareCommandResponseFromJson((String) response); + // the response could be a string which is a json respresentation of the CommandResult object + // it can also be a Path to a temp file downloaded from the rest http request + if(response instanceof String) { + CommandResponse commandResponse = + CommandResponseBuilder.prepareCommandResponseFromJson((String) response); - if (commandResponse.isFailedToPersist()) { - shell.printAsSevere(CliStrings.SHARED_CONFIGURATION_FAILED_TO_PERSIST_COMMAND_CHANGES); - logWrapper.severe(CliStrings.SHARED_CONFIGURATION_FAILED_TO_PERSIST_COMMAND_CHANGES); + if (commandResponse.isFailedToPersist()) { + shell.printAsSevere(CliStrings.SHARED_CONFIGURATION_FAILED_TO_PERSIST_COMMAND_CHANGES); + logWrapper.severe(CliStrings.SHARED_CONFIGURATION_FAILED_TO_PERSIST_COMMAND_CHANGES); + } + + String debugInfo = commandResponse.getDebugInfo(); + if (debugInfo != null && !debugInfo.trim().isEmpty()) { + // TODO - Abhishek When debug is ON, log response in gfsh logs + // TODO - Abhishek handle \n better. Is it coming from GemFire formatter + debugInfo = debugInfo.replaceAll("\n\n\n", "\n"); + debugInfo = debugInfo.replaceAll("\n\n", "\n"); + debugInfo = + debugInfo.replaceAll("\n", "\n[From Manager : " + commandResponse.getSender() + "]"); + debugInfo = "[From Manager : " + commandResponse.getSender() + "]" + debugInfo; + LogWrapper.getInstance().info(debugInfo); + } + commandResult = ResultBuilder.fromJson((String) response); } - String debugInfo = commandResponse.getDebugInfo(); - if (debugInfo != null && !debugInfo.trim().isEmpty()) { - // TODO - Abhishek When debug is ON, log response in gfsh logs - // TODO - Abhishek handle \n better. Is it coming from GemFire formatter - debugInfo = debugInfo.replaceAll("\n\n\n", "\n"); - debugInfo = debugInfo.replaceAll("\n\n", "\n"); - debugInfo = - debugInfo.replaceAll("\n", "\n[From Manager : " + commandResponse.getSender() + "]"); - debugInfo = "[From Manager : " + commandResponse.getSender() + "]" + debugInfo; - LogWrapper.getInstance().info(debugInfo); + Path tempFile = null; + if(response instanceof Path){ + tempFile = (Path)response; } - commandResult = ResultBuilder.fromJson((String) response); // 3. Post Remote Execution if (interceptor != null) { - Result postExecResult = interceptor.postExecution(parseResult, commandResult); + Result postExecResult = interceptor.postExecution(parseResult, commandResult, tempFile); if (postExecResult != null) { if (Status.ERROR.equals(postExecResult.getStatus())) { if (logWrapper.infoEnabled()) { http://git-wip-us.apache.org/repos/asf/geode/blob/4a700206/geode-core/src/main/java/org/apache/geode/management/internal/cli/util/ExportLogsCacheWriter.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/util/ExportLogsCacheWriter.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/util/ExportLogsCacheWriter.java index a8b7225..178bb4a 100644 --- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/util/ExportLogsCacheWriter.java +++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/util/ExportLogsCacheWriter.java @@ -22,15 +22,11 @@ import org.apache.geode.cache.EntryEvent; import org.apache.geode.cache.util.CacheWriterAdapter; import java.io.BufferedOutputStream; -import java.io.File; import java.io.FileOutputStream; import java.io.IOException; -import java.io.OutputStream; import java.io.Serializable; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Map; public class ExportLogsCacheWriter extends CacheWriterAdapter implements Serializable { private Path currentFile; @@ -38,11 +34,9 @@ public class ExportLogsCacheWriter extends CacheWriterAdapter implements Seriali @Override public void beforeCreate(EntryEvent event) throws CacheWriterException { - if (currentFile.getFileName().endsWith("server-2.zip")) { - System.out.println("We got data from server 2"); - } if (currentOutputStream == null) { - throw new IllegalStateException("No outputStream is open. You must call startFile before sending data."); + throw new IllegalStateException( + "No outputStream is open. You must call startFile before sending data."); } try { http://git-wip-us.apache.org/repos/asf/geode/blob/4a700206/geode-core/src/main/java/org/apache/geode/management/internal/web/controllers/MiscellaneousCommandsController.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/web/controllers/MiscellaneousCommandsController.java b/geode-core/src/main/java/org/apache/geode/management/internal/web/controllers/MiscellaneousCommandsController.java index ac912c8..28cc658 100644 --- a/geode-core/src/main/java/org/apache/geode/management/internal/web/controllers/MiscellaneousCommandsController.java +++ b/geode-core/src/main/java/org/apache/geode/management/internal/web/controllers/MiscellaneousCommandsController.java @@ -14,11 +14,13 @@ */ package org.apache.geode.management.internal.web.controllers; -import java.util.concurrent.Callable; - import org.apache.geode.internal.lang.StringUtils; import org.apache.geode.management.internal.cli.i18n.CliStrings; +import org.apache.geode.management.internal.cli.result.ResultBuilder; import org.apache.geode.management.internal.cli.util.CommandStringBuilder; +import org.springframework.core.io.InputStreamResource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; @@ -27,6 +29,9 @@ import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; +import java.io.File; +import java.io.FileInputStream; + /** * The MiscellaneousCommandsController class implements GemFire Management REST API web service * endpoints for the Gfsh Miscellaneous Commands. @@ -48,21 +53,19 @@ import org.springframework.web.bind.annotation.ResponseBody; public class MiscellaneousCommandsController extends AbstractCommandsController { @RequestMapping(method = RequestMethod.GET, value = "/logs") - public Callable<ResponseEntity<String>> exportLogs( - @RequestParam(CliStrings.EXPORT_LOGS__DIR) final String directory, - @RequestParam(value = CliStrings.EXPORT_LOGS__GROUP, required = false) final String[] groups, - @RequestParam(value = CliStrings.EXPORT_LOGS__MEMBER, + public ResponseEntity<InputStreamResource> exportLogs(@RequestParam(value = CliStrings.EXPORT_LOGS__DIR, required = false) final String directory, + @RequestParam(value = CliStrings.EXPORT_LOGS__GROUP, required = false) final String[] groups, + @RequestParam(value = CliStrings.EXPORT_LOGS__MEMBER, required = false) final String memberNameId, - @RequestParam(value = CliStrings.EXPORT_LOGS__LOGLEVEL, + @RequestParam(value = CliStrings.EXPORT_LOGS__LOGLEVEL, required = false) final String logLevel, - @RequestParam(value = CliStrings.EXPORT_LOGS__UPTO_LOGLEVEL, + @RequestParam(value = CliStrings.EXPORT_LOGS__UPTO_LOGLEVEL, defaultValue = "false") final Boolean onlyLogLevel, - @RequestParam(value = CliStrings.EXPORT_LOGS__MERGELOG, + @RequestParam(value = CliStrings.EXPORT_LOGS__MERGELOG, defaultValue = "false") final Boolean mergeLog, - @RequestParam(value = CliStrings.EXPORT_LOGS__STARTTIME, + @RequestParam(value = CliStrings.EXPORT_LOGS__STARTTIME, required = false) final String startTime, - @RequestParam(value = CliStrings.EXPORT_LOGS__ENDTIME, - required = false) final String endTime) { + @RequestParam(value = CliStrings.EXPORT_LOGS__ENDTIME, required = false) final String endTime) { final CommandStringBuilder command = new CommandStringBuilder(CliStrings.EXPORT_LOGS); command.addOption(CliStrings.EXPORT_LOGS__DIR, decode(directory)); @@ -93,7 +96,19 @@ public class MiscellaneousCommandsController extends AbstractCommandsController command.addOption(CliStrings.EXPORT_LOGS__ENDTIME, endTime); } - return getProcessCommandCallable(command.toString()); + // the result is json string from CommandResult + String result = processCommand(command.toString()); + + // parse the result to get the file path + String filePath = ResultBuilder.fromJson(result).nextLine().trim(); + + HttpHeaders respHeaders = new HttpHeaders(); + try { + InputStreamResource isr = new InputStreamResource(new FileInputStream(new File(filePath))); + return new ResponseEntity<InputStreamResource>(isr, respHeaders, HttpStatus.OK); + } catch (Exception ex) { + throw new RuntimeException("IOError writing file to output stream", ex); + } } // TODO determine whether Async functionality is required http://git-wip-us.apache.org/repos/asf/geode/blob/4a700206/geode-core/src/main/java/org/apache/geode/management/internal/web/shell/AbstractHttpOperationInvoker.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/web/shell/AbstractHttpOperationInvoker.java b/geode-core/src/main/java/org/apache/geode/management/internal/web/shell/AbstractHttpOperationInvoker.java index fa05248..2a409cc 100644 --- a/geode-core/src/main/java/org/apache/geode/management/internal/web/shell/AbstractHttpOperationInvoker.java +++ b/geode-core/src/main/java/org/apache/geode/management/internal/web/shell/AbstractHttpOperationInvoker.java @@ -15,6 +15,7 @@ package org.apache.geode.management.internal.web.shell; +import org.apache.commons.io.FileUtils; import org.apache.geode.internal.GemFireVersion; import org.apache.geode.internal.lang.StringUtils; import org.apache.geode.internal.logging.LogService; @@ -41,14 +42,18 @@ import org.springframework.http.client.ClientHttpResponse; import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.web.client.RequestCallback; import org.springframework.web.client.ResourceAccessException; import org.springframework.web.client.ResponseErrorHandler; +import org.springframework.web.client.ResponseExtractor; import org.springframework.web.client.RestTemplate; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; @@ -583,7 +588,7 @@ public abstract class AbstractHttpOperationInvoker implements HttpOperationInvok * @see org.apache.geode.management.internal.web.http.ClientHttpRequest * @see org.springframework.http.ResponseEntity */ - protected <T> ResponseEntity<T> send(final ClientHttpRequest request, + protected <T> T send(final ClientHttpRequest request, final Class<T> responseType) { return send(request, responseType, Collections.<String, Object>emptyMap()); } @@ -603,10 +608,9 @@ public abstract class AbstractHttpOperationInvoker implements HttpOperationInvok * @see org.springframework.web.client.RestTemplate#exchange(java.net.URI, * org.springframework.http.HttpMethod, org.springframework.http.HttpEntity, Class) */ - protected <T> ResponseEntity<T> send(final ClientHttpRequest request, final Class<T> responseType, + protected <T> T send(final ClientHttpRequest request, final Class<T> responseType, final Map<String, ?> uriVariables) { final URI url = request.getURL(uriVariables); - if (isDebugEnabled()) { printInfo("Link: %1$s", request.getLink().toHttpRequestLine()); printInfo("HTTP URL: %1$s", url); @@ -626,7 +630,24 @@ public abstract class AbstractHttpOperationInvoker implements HttpOperationInvok printInfo("HTTP response body: ", response.getBody()); } - return response; + return response.getBody(); + } + + protected Path downloadResponseToTempFile(ClientHttpRequest request, Map<String, ?> uriVariables){ + final URI url = request.getURL(uriVariables); + // Optional Accept header + RequestCallback requestCallback = r -> r.getHeaders().setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM)); + + // Streams the response instead of loading it all in memory + ResponseExtractor<Path> responseExtractor = resp -> { + Path tempFile = Files.createTempFile("fileDownload", ""); + if(tempFile.toFile().exists()){ + FileUtils.deleteQuietly(tempFile.toFile()); + } + Files.copy(resp.getBody(), tempFile); + return tempFile; + }; + return getRestTemplate().execute(url, org.springframework.http.HttpMethod.GET, requestCallback, responseExtractor); } /** @@ -680,10 +701,8 @@ public abstract class AbstractHttpOperationInvoker implements HttpOperationInvok request.addParameterValues("resourceName", resourceName); request.addParameterValues("attributeName", attributeName); - final ResponseEntity<byte[]> response = send(request, byte[].class); - try { - return IOUtils.deserializeObject(response.getBody()); + return IOUtils.deserializeObject(send(request, byte[].class)); } catch (IOException e) { throw new MBeanAccessException(String.format( "De-serializing the result of accessing attribute (%1$s) on MBean (%2$s) failed!", @@ -785,10 +804,8 @@ public abstract class AbstractHttpOperationInvoker implements HttpOperationInvok request.addParameterValues("parameters", params); // TODO may need to convert method parameter // arguments - final ResponseEntity<byte[]> response = send(request, byte[].class); - try { - return IOUtils.deserializeObject(response.getBody()); + return IOUtils.deserializeObject(send(request, byte[].class)); } catch (IOException e) { throw new MBeanAccessException(String.format( "De-serializing the result from invoking operation (%1$s) on MBean (%2$s) failed!", @@ -831,11 +848,8 @@ public abstract class AbstractHttpOperationInvoker implements HttpOperationInvok final ClientHttpRequest request = createHttpRequest(link); request.setContent(new QueryParameterSource(objectName, queryExpression)); - - final ResponseEntity<byte[]> response = send(request, byte[].class); - try { - return (Set<ObjectName>) IOUtils.deserializeObject(response.getBody()); + return (Set<ObjectName>) IOUtils.deserializeObject(send(request, byte[].class)); } catch (Exception e) { throw new MBeanAccessException(String.format( "An error occurred while querying for MBean names using ObjectName pattern (%1$s) and Query expression (%2$s)!", http://git-wip-us.apache.org/repos/asf/geode/blob/4a700206/geode-core/src/main/java/org/apache/geode/management/internal/web/shell/RestHttpOperationInvoker.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/web/shell/RestHttpOperationInvoker.java b/geode-core/src/main/java/org/apache/geode/management/internal/web/shell/RestHttpOperationInvoker.java index 82b2b1f..cb0f082 100644 --- a/geode-core/src/main/java/org/apache/geode/management/internal/web/shell/RestHttpOperationInvoker.java +++ b/geode-core/src/main/java/org/apache/geode/management/internal/web/shell/RestHttpOperationInvoker.java @@ -15,14 +15,6 @@ package org.apache.geode.management.internal.web.shell; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - import org.apache.geode.internal.lang.Filter; import org.apache.geode.internal.lang.Initable; import org.apache.geode.internal.lang.StringUtils; @@ -36,15 +28,21 @@ import org.apache.geode.management.internal.web.domain.LinkIndex; import org.apache.geode.management.internal.web.http.ClientHttpRequest; import org.apache.geode.management.internal.web.http.HttpHeader; import org.apache.geode.management.internal.web.util.ConvertUtils; - import org.apache.logging.log4j.Logger; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; import org.springframework.http.client.ClientHttpResponse; import org.springframework.web.client.ResourceAccessException; import org.springframework.web.util.UriTemplate; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + /** * The RestHttpOperationInvoker class is an implementation of the OperationInvoker interface that * translates (adapts) GemFire shell command invocations into HTTP requests to a corresponding REST @@ -376,7 +374,8 @@ public class RestHttpOperationInvoker extends AbstractHttpOperationInvoker imple * (execution). * * @param command the command requested/entered by the user to be processed. - * @return the result of the command execution. + * @return either a json string of the CommandResult or a Path to a temp file if the response is a + * InputStream * @see #createHttpRequest(org.apache.geode.management.internal.cli.CommandRequest) * @see #handleResourceAccessException(org.springframework.web.client.ResourceAccessException) * @see #isConnected() @@ -388,15 +387,19 @@ public class RestHttpOperationInvoker extends AbstractHttpOperationInvoker imple * @see org.springframework.http.ResponseEntity */ @Override - public String processCommand(final CommandRequest command) { + public Object processCommand(final CommandRequest command) { assertState(isConnected(), "Gfsh must be connected to the GemFire Manager in order to process commands remotely!"); + Object result = null; try { - ResponseEntity<String> response = - send(createHttpRequest(command), String.class, command.getParameters()); - - return response.getBody(); + if(command.isDownloadFile()){ + result = downloadResponseToTempFile(createHttpRequest(command), command.getParameters()); + } + else{ + result = send(createHttpRequest(command), String.class, command.getParameters()); + } + return result; } catch (RestApiCallForCommandNotFoundException e) { return simpleProcessCommand(command, e); } catch (ResourceAccessException e) { http://git-wip-us.apache.org/repos/asf/geode/blob/4a700206/geode-core/src/main/java/org/apache/geode/management/internal/web/shell/SimpleHttpOperationInvoker.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/web/shell/SimpleHttpOperationInvoker.java b/geode-core/src/main/java/org/apache/geode/management/internal/web/shell/SimpleHttpOperationInvoker.java index dcda27b..d11d824 100644 --- a/geode-core/src/main/java/org/apache/geode/management/internal/web/shell/SimpleHttpOperationInvoker.java +++ b/geode-core/src/main/java/org/apache/geode/management/internal/web/shell/SimpleHttpOperationInvoker.java @@ -15,19 +15,17 @@ package org.apache.geode.management.internal.web.shell; -import java.net.URI; -import java.util.Map; - import org.apache.geode.management.internal.cli.CommandRequest; import org.apache.geode.management.internal.cli.shell.Gfsh; import org.apache.geode.management.internal.web.domain.Link; import org.apache.geode.management.internal.web.http.ClientHttpRequest; import org.apache.geode.management.internal.web.http.HttpMethod; - -import org.springframework.http.ResponseEntity; import org.springframework.web.client.ResourceAccessException; import org.springframework.web.util.UriComponentsBuilder; +import java.net.URI; +import java.util.Map; + /** * The SimpleHttpOperationInvoker class is an implementation of the OperationInvoker interface that * issues commands to the GemFire Manager via HTTP. The SimpleHttpOperationInvoker uses a single URL @@ -156,9 +154,7 @@ public class SimpleHttpOperationInvoker extends AbstractHttpOperationInvoker { "Gfsh must be connected to the GemFire Manager in order to process commands remotely!"); try { - final ResponseEntity<String> response = send(createHttpRequest(command), String.class); - - return response.getBody(); + return send(createHttpRequest(command), String.class); } catch (ResourceAccessException e) { return handleResourceAccessException(e); } http://git-wip-us.apache.org/repos/asf/geode/blob/4a700206/geode-core/src/test/java/org/apache/geode/test/dunit/rules/GfshShellConnectionRule.java ---------------------------------------------------------------------- diff --git a/geode-core/src/test/java/org/apache/geode/test/dunit/rules/GfshShellConnectionRule.java b/geode-core/src/test/java/org/apache/geode/test/dunit/rules/GfshShellConnectionRule.java index 93572fe..c3310d5 100644 --- a/geode-core/src/test/java/org/apache/geode/test/dunit/rules/GfshShellConnectionRule.java +++ b/geode-core/src/test/java/org/apache/geode/test/dunit/rules/GfshShellConnectionRule.java @@ -16,6 +16,7 @@ package org.apache.geode.test.dunit.rules; import static org.assertj.core.api.Assertions.assertThat; +import org.apache.geode.internal.lang.StringUtils; import org.apache.geode.management.cli.Result; import org.apache.geode.management.internal.cli.CliUtil; import org.apache.geode.management.internal.cli.HeadlessGfsh; @@ -23,6 +24,7 @@ import org.apache.geode.management.internal.cli.i18n.CliStrings; import org.apache.geode.management.internal.cli.result.CommandResult; import org.apache.geode.management.internal.cli.util.CommandStringBuilder; import org.apache.geode.test.junit.rules.DescribedExternalResource; +import org.json.JSONArray; import org.junit.runner.Description; /** @@ -89,6 +91,11 @@ public class GfshShellConnectionRule extends DescribedExternalResource { assertThat(this.connected).isTrue(); } + public void connectAndVerify(int port, PortType type, String... options) throws Exception { + connect(port, type, options); + assertThat(this.connected).isTrue(); + } + public void connect(int port, PortType type, String... options) throws Exception { CliUtil.isGfshVM = true; final CommandStringBuilder connectCommand = new CommandStringBuilder(CliStrings.CONNECT); @@ -157,19 +164,24 @@ public class GfshShellConnectionRule extends DescribedExternalResource { public CommandResult executeCommand(String command) throws Exception { gfsh.executeCommand(command); CommandResult result = (CommandResult) gfsh.getResult(); - System.out.println("Command Result: \n" + gfsh.outputString); + if(StringUtils.isBlank(gfsh.outputString)){ + JSONArray messages = ((JSONArray)result.getContent().get("message")); + for(int i=0; i< messages.length(); i++){ + gfsh.outputString += messages.getString(i)+"\n"; + } + } + System.out.println("Command result: \n" + gfsh.outputString); return result; } + public String getGfshOutput(){ + return gfsh.outputString; + } + public CommandResult executeAndVerifyCommand(String command) throws Exception { CommandResult result = executeCommand(command); - - if (result.getStatus() != Result.Status.OK) { - System.out.println("broken"); - } - assertThat(result.getStatus()).describedAs(result.getContent().toString()) - .isEqualTo(Result.Status.OK); + assertThat(result.getStatus()).isEqualTo(Result.Status.OK); return result; } http://git-wip-us.apache.org/repos/asf/geode/blob/4a700206/geode-web/src/test/java/org/apache/geode/management/internal/cli/commands/ExportLogsOverHttpDunit.java ---------------------------------------------------------------------- diff --git a/geode-web/src/test/java/org/apache/geode/management/internal/cli/commands/ExportLogsOverHttpDunit.java b/geode-web/src/test/java/org/apache/geode/management/internal/cli/commands/ExportLogsOverHttpDunit.java new file mode 100644 index 0000000..a45d9ef --- /dev/null +++ b/geode-web/src/test/java/org/apache/geode/management/internal/cli/commands/ExportLogsOverHttpDunit.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package org.apache.geode.management.internal.cli.commands; + +import static org.apache.geode.distributed.ConfigurationProperties.HTTP_SERVICE_BIND_ADDRESS; +import static org.apache.geode.distributed.ConfigurationProperties.HTTP_SERVICE_PORT; +import static org.apache.geode.distributed.ConfigurationProperties.JMX_MANAGER_PORT; +import static org.assertj.core.api.Assertions.assertThat; + +import org.apache.geode.internal.AvailablePortHelper; +import org.apache.geode.test.dunit.rules.GfshShellConnectionRule; +import org.apache.geode.test.dunit.rules.Locator; +import org.apache.geode.test.dunit.rules.LocatorServerStartupRule; +import org.apache.geode.test.junit.categories.DistributedTest; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.rules.TemporaryFolder; + +import java.io.File; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Properties; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +@Category(DistributedTest.class) +public class ExportLogsOverHttpDunit { + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @ClassRule + public static LocatorServerStartupRule lsRule = new LocatorServerStartupRule(); + + @ClassRule + public static GfshShellConnectionRule gfshConnector = new GfshShellConnectionRule(); + + private static int jmxPort, httpPort; + private static Properties locatorProperties; + private static Set<String> expectedZipEntries = new HashSet<>(); + + @BeforeClass + public static void beforeClass() throws Exception { + int[] ports = AvailablePortHelper.getRandomAvailableTCPPorts(2); + httpPort = ports[0]; + jmxPort = ports[1]; + locatorProperties = new Properties(); + locatorProperties.setProperty(HTTP_SERVICE_BIND_ADDRESS, "localhost"); + locatorProperties.setProperty(HTTP_SERVICE_PORT, httpPort + ""); + locatorProperties.setProperty(JMX_MANAGER_PORT, jmxPort + ""); + + // start the locator in vm0 and then connect to it over http + Locator locator = lsRule.startLocatorVM(0, locatorProperties); + lsRule.startServerVM(1, locator.getPort()); + gfshConnector.connectAndVerify(httpPort, GfshShellConnectionRule.PortType.http); + + String[] expectedLogs = {"locator-0/locator-0.log", "server-1/server-1.log"}; + expectedZipEntries = Arrays.stream(expectedLogs).collect(Collectors.toSet()); + } + + @Test + public void testExportWithNoDir() throws Exception { + // export the logs + gfshConnector.executeCommand("export logs"); + // verify that the message contains a path to the user.dir + String message = gfshConnector.getGfshOutput(); + assertThat(message).contains("Logs exported to: "); + assertThat(message).contains(System.getProperty("user.dir")); + + String zipPath = message.replaceAll("Logs exported to: ", "").trim(); + verifyZipContent(zipPath); + } + + @Test + public void testExportWithDir() throws Exception { + File dir = temporaryFolder.newFolder(); + // export the logs + gfshConnector.executeCommand("export logs --dir="+dir.getAbsolutePath()); + // verify that the message contains a path to the user.dir + String message = gfshConnector.getGfshOutput(); + assertThat(message).contains("Logs exported to: "); + assertThat(message).contains(dir.getAbsolutePath()); + + String zipPath = message.replaceAll("Logs exported to: ", "").trim(); + verifyZipContent(zipPath); + } + + private void verifyZipContent(String zipPath) throws Exception{ + Set<String> actualZipEnries = + new ZipFile(zipPath).stream().map(ZipEntry::getName).collect(Collectors.toSet()); + + assertThat(actualZipEnries).isEqualTo(expectedZipEntries); + + } +} http://git-wip-us.apache.org/repos/asf/geode/blob/4a700206/geode-web/src/test/java/org/apache/geode/management/internal/web/shell/RestHttpOperationInvokerJUnitTest.java ---------------------------------------------------------------------- diff --git a/geode-web/src/test/java/org/apache/geode/management/internal/web/shell/RestHttpOperationInvokerJUnitTest.java b/geode-web/src/test/java/org/apache/geode/management/internal/web/shell/RestHttpOperationInvokerJUnitTest.java index 808c78f..912e16a 100644 --- a/geode-web/src/test/java/org/apache/geode/management/internal/web/shell/RestHttpOperationInvokerJUnitTest.java +++ b/geode-web/src/test/java/org/apache/geode/management/internal/web/shell/RestHttpOperationInvokerJUnitTest.java @@ -14,22 +14,12 @@ */ package org.apache.geode.management.internal.web.shell; -import static org.junit.Assert.*; - -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.springframework.core.io.Resource; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.client.ResourceAccessException; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; import org.apache.geode.internal.lang.StringUtils; import org.apache.geode.management.internal.cli.CommandRequest; @@ -39,6 +29,18 @@ import org.apache.geode.management.internal.web.domain.LinkIndex; import org.apache.geode.management.internal.web.http.ClientHttpRequest; import org.apache.geode.management.internal.web.http.HttpMethod; import org.apache.geode.test.junit.categories.UnitTest; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.springframework.core.io.Resource; +import org.springframework.web.client.ResourceAccessException; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; /** * The RestHttpOperationInvokerJUnitTest class is a test suite of test cases testing the contract @@ -315,13 +317,13 @@ public class RestHttpOperationInvokerJUnitTest extends AbstractWebTestCase { @Override @SuppressWarnings("unchecked") - protected <T> ResponseEntity<T> send(final ClientHttpRequest request, + protected <T> T send(final ClientHttpRequest request, final Class<T> responseType, final Map<String, ?> uriVariables) { - return new ResponseEntity(expectedResult, HttpStatus.OK); + return (T) expectedResult; } }; - final String actualResult = operationInvoker.processCommand( + final Object actualResult = operationInvoker.processCommand( createCommandRequest("list-libraries", Collections.<String, String>emptyMap())); assertEquals(expectedResult, actualResult); @@ -351,7 +353,7 @@ public class RestHttpOperationInvokerJUnitTest extends AbstractWebTestCase { protected void printWarning(final String message, final Object... args) {} }; - final String actualResult = operationInvoker.processCommand( + final Object actualResult = operationInvoker.processCommand( createCommandRequest("get resource", Collections.<String, String>emptyMap())); assertEquals(expectedResult, actualResult); @@ -371,7 +373,7 @@ public class RestHttpOperationInvokerJUnitTest extends AbstractWebTestCase { protected void printWarning(final String message, final Object... args) {} @Override - protected <T> ResponseEntity<T> send(final ClientHttpRequest request, + protected <T> T send(final ClientHttpRequest request, final Class<T> responseType, final Map<String, ?> uriVariables) { throw new ResourceAccessException("test"); } @@ -389,7 +391,7 @@ public class RestHttpOperationInvokerJUnitTest extends AbstractWebTestCase { + "Please try reconnecting or see the GemFire Manager's log file for further details.", operationInvoker.getBaseUrl(), "test"); - final String actualResult = operationInvoker.processCommand( + final Object actualResult = operationInvoker.processCommand( createCommandRequest("list-libraries", Collections.<String, String>emptyMap())); assertFalse(operationInvoker.isConnected()); http://git-wip-us.apache.org/repos/asf/geode/blob/4a700206/geode-web/src/test/java/org/apache/geode/management/internal/web/shell/SimpleHttpOperationInvokerJUnitTest.java ---------------------------------------------------------------------- diff --git a/geode-web/src/test/java/org/apache/geode/management/internal/web/shell/SimpleHttpOperationInvokerJUnitTest.java b/geode-web/src/test/java/org/apache/geode/management/internal/web/shell/SimpleHttpOperationInvokerJUnitTest.java index 8afbcf6..ae19afa 100644 --- a/geode-web/src/test/java/org/apache/geode/management/internal/web/shell/SimpleHttpOperationInvokerJUnitTest.java +++ b/geode-web/src/test/java/org/apache/geode/management/internal/web/shell/SimpleHttpOperationInvokerJUnitTest.java @@ -14,9 +14,10 @@ */ package org.apache.geode.management.internal.web.shell; -import static org.junit.Assert.*; - -import java.util.Collections; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import org.apache.geode.management.internal.cli.CommandRequest; import org.apache.geode.management.internal.web.AbstractWebTestCase; @@ -25,15 +26,14 @@ import org.apache.geode.management.internal.web.http.ClientHttpRequest; import org.apache.geode.management.internal.web.http.HttpHeader; import org.apache.geode.management.internal.web.http.HttpMethod; import org.apache.geode.test.junit.categories.UnitTest; - import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.experimental.categories.Category; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; import org.springframework.web.client.ResourceAccessException; +import java.util.Collections; + /** * The SimpleHttpOperationInvokerJUnitTest class is a test suite of test cases testing the contract * and functionality of the SimpleHttpOperationInvoker class. @@ -128,9 +128,9 @@ public class SimpleHttpOperationInvokerJUnitTest extends AbstractWebTestCase { @Override @SuppressWarnings("unchecked") - protected <T> ResponseEntity<T> send(final ClientHttpRequest request, + protected <T> T send(final ClientHttpRequest request, final Class<T> responseType) { - return new ResponseEntity(expectedResult, HttpStatus.OK); + return (T) expectedResult; } }; @@ -151,7 +151,7 @@ public class SimpleHttpOperationInvokerJUnitTest extends AbstractWebTestCase { } @Override - protected <T> ResponseEntity<T> send(final ClientHttpRequest request, + protected <T> T send(final ClientHttpRequest request, final Class<T> responseType) { throw new ResourceAccessException("test"); }
