You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by js...@apache.org on 2017/08/21 19:56:15 UTC
geode git commit: GEODE-3217: Reimplement gfsh query as a single-step
command
Repository: geode
Updated Branches:
refs/heads/release/1.2.1 828b24697 -> 070ea49d2
GEODE-3217: Reimplement gfsh query as a single-step command
(cherry picked from commit 564a94b)
Project: http://git-wip-us.apache.org/repos/asf/geode/repo
Commit: http://git-wip-us.apache.org/repos/asf/geode/commit/070ea49d
Tree: http://git-wip-us.apache.org/repos/asf/geode/tree/070ea49d
Diff: http://git-wip-us.apache.org/repos/asf/geode/diff/070ea49d
Branch: refs/heads/release/1.2.1
Commit: 070ea49d2633edff9992cf1d2558b98f73553144
Parents: 828b246
Author: Jared Stewart <js...@pivotal.io>
Authored: Mon Jul 17 11:14:40 2017 -0700
Committer: Jared Stewart <js...@pivotal.io>
Committed: Mon Aug 21 12:55:47 2017 -0700
----------------------------------------------------------------------
.../internal/cli/commands/DataCommands.java | 78 ++----
.../internal/cli/commands/QueryCommand.java | 133 +++++++++
.../internal/cli/commands/QueryInterceptor.java | 107 ++++++++
.../internal/cli/domain/DataCommandResult.java | 97 ++-----
.../cli/functions/DataCommandFunction.java | 273 +------------------
.../management/internal/cli/shell/Gfsh.java | 55 ++--
.../web/controllers/DataCommandsController.java | 12 +-
.../internal/cli/commands/QueryCommandTest.java | 267 ++++++++++++++++++
.../cli/commands/QueryCommandUnitTest.java | 30 ++
.../internal/cli/result/ResultBuilderTest.java | 131 +++++++++
10 files changed, 749 insertions(+), 434 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/geode/blob/070ea49d/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DataCommands.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DataCommands.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DataCommands.java
index cb9c4fe..ae65399 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DataCommands.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DataCommands.java
@@ -14,12 +14,30 @@
*/
package org.apache.geode.management.internal.cli.commands;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.concurrent.Callable;
+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 org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
+import org.apache.shiro.subject.Subject;
+import org.springframework.shell.core.annotation.CliCommand;
+import org.springframework.shell.core.annotation.CliOption;
+
import org.apache.geode.LogWriter;
import org.apache.geode.cache.CacheClosedException;
-import org.apache.geode.cache.CacheFactory;
import org.apache.geode.cache.DataPolicy;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.control.RebalanceFactory;
@@ -50,36 +68,13 @@ import org.apache.geode.management.internal.cli.functions.ExportDataFunction;
import org.apache.geode.management.internal.cli.functions.ImportDataFunction;
import org.apache.geode.management.internal.cli.functions.RebalanceFunction;
import org.apache.geode.management.internal.cli.i18n.CliStrings;
-import org.apache.geode.management.internal.cli.multistep.CLIMultiStepHelper;
-import org.apache.geode.management.internal.cli.multistep.CLIStep;
-import org.apache.geode.management.internal.cli.multistep.MultiStepCommand;
import org.apache.geode.management.internal.cli.result.CompositeResultData;
import org.apache.geode.management.internal.cli.result.ErrorResultData;
import org.apache.geode.management.internal.cli.result.ResultBuilder;
import org.apache.geode.management.internal.cli.result.TabularResultData;
-import org.apache.geode.management.internal.cli.shell.Gfsh;
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.shiro.subject.Subject;
-import org.springframework.shell.core.annotation.CliAvailabilityIndicator;
-import org.springframework.shell.core.annotation.CliCommand;
-import org.springframework.shell.core.annotation.CliOption;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.StringTokenizer;
-import java.util.concurrent.Callable;
-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;
/**
* @since GemFire 7.0
@@ -1091,41 +1086,6 @@ public class DataCommands implements GfshCommand {
return makePresentationResult(dataResult);
}
- @CliMetaData(relatedTopic = {CliStrings.TOPIC_GEODE_DATA, CliStrings.TOPIC_GEODE_REGION})
- @MultiStepCommand
- @CliCommand(value = {CliStrings.QUERY}, help = CliStrings.QUERY__HELP)
- public Object query(
- @CliOption(key = CliStrings.QUERY__QUERY, help = CliStrings.QUERY__QUERY__HELP,
- mandatory = true) final String query,
- @CliOption(key = CliStrings.QUERY__STEPNAME, help = "Step name",
- unspecifiedDefaultValue = CliStrings.QUERY__STEPNAME__DEFAULTVALUE) String stepName,
- @CliOption(key = CliStrings.QUERY__INTERACTIVE, help = CliStrings.QUERY__INTERACTIVE__HELP,
- unspecifiedDefaultValue = "true") final boolean interactive) {
-
- if (!CliUtil.isGfshVM() && stepName.equals(CliStrings.QUERY__STEPNAME__DEFAULTVALUE)) {
- return ResultBuilder.createInfoResult(CliStrings.QUERY__MSG__NOT_SUPPORTED_ON_MEMBERS);
- }
-
- Object[] arguments = new Object[] {query, stepName, interactive};
- CLIStep exec = new DataCommandFunction.SelectExecStep(arguments);
- CLIStep display = new DataCommandFunction.SelectDisplayStep(arguments);
- CLIStep move = new DataCommandFunction.SelectMoveStep(arguments);
- CLIStep quit = new DataCommandFunction.SelectQuitStep(arguments);
- CLIStep[] steps = {exec, display, move, quit};
- return CLIMultiStepHelper.chooseStep(steps, stepName);
- }
-
- @CliAvailabilityIndicator({CliStrings.REBALANCE, CliStrings.GET, CliStrings.PUT,
- CliStrings.REMOVE, CliStrings.LOCATE_ENTRY, CliStrings.QUERY, CliStrings.IMPORT_DATA,
- CliStrings.EXPORT_DATA})
- public boolean dataCommandsAvailable() {
- boolean isAvailable = true; // always available on server
- if (CliUtil.isGfshVM()) { // in gfsh check if connected
- isAvailable = getGfsh() != null && getGfsh().isConnectedAndReady();
- }
- return isAvailable;
- }
-
private static class MemberPRInfo {
public ArrayList<DistributedMember> dsMemberList;
public String region;
http://git-wip-us.apache.org/repos/asf/geode/blob/070ea49d/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/QueryCommand.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/QueryCommand.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/QueryCommand.java
new file mode 100644
index 0000000..07d8f9d
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/QueryCommand.java
@@ -0,0 +1,133 @@
+/*
+ * 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 java.io.File;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.logging.log4j.Logger;
+import org.apache.shiro.subject.Subject;
+import org.springframework.shell.core.annotation.CliCommand;
+import org.springframework.shell.core.annotation.CliOption;
+
+import org.apache.geode.cache.CacheFactory;
+import org.apache.geode.cache.query.QueryInvalidException;
+import org.apache.geode.cache.query.internal.CompiledValue;
+import org.apache.geode.cache.query.internal.QCompiler;
+import org.apache.geode.distributed.DistributedMember;
+import org.apache.geode.internal.cache.InternalCache;
+import org.apache.geode.internal.logging.LogService;
+import org.apache.geode.internal.security.IntegratedSecurityService;
+import org.apache.geode.management.cli.CliMetaData;
+import org.apache.geode.management.cli.ConverterHint;
+import org.apache.geode.management.cli.Result;
+import org.apache.geode.management.internal.cli.domain.DataCommandRequest;
+import org.apache.geode.management.internal.cli.domain.DataCommandResult;
+import org.apache.geode.management.internal.cli.functions.DataCommandFunction;
+import org.apache.geode.management.internal.cli.i18n.CliStrings;
+import org.apache.geode.management.internal.cli.remote.CommandExecutionContext;
+import org.apache.geode.management.internal.cli.result.CompositeResultData;
+import org.apache.geode.management.internal.cli.result.ResultBuilder;
+
+public class QueryCommand implements GfshCommand {
+ private static final Logger logger = LogService.getLogger();
+
+ @CliCommand(value = "query", help = CliStrings.QUERY__HELP)
+ @CliMetaData(interceptor = "org.apache.geode.management.internal.cli.commands.QueryInterceptor")
+ public Result query(
+ @CliOption(key = CliStrings.QUERY__QUERY, help = CliStrings.QUERY__QUERY__HELP,
+ mandatory = true) final String query,
+ @CliOption(key = "file", help = "File in which to output the results.",
+ optionContext = ConverterHint.FILE) final File outputFile,
+ @CliOption(key = CliStrings.QUERY__INTERACTIVE, unspecifiedDefaultValue = "false",
+ help = CliStrings.QUERY__INTERACTIVE__HELP) final boolean interactive) {
+ DataCommandResult dataResult = select(query);
+ CompositeResultData rd = dataResult.toSelectCommandResult();
+ return ResultBuilder.buildResult(rd);
+ }
+
+ private DataCommandResult select(String query) {
+ InternalCache cache = (InternalCache) CacheFactory.getAnyInstance();
+ DataCommandResult dataResult;
+
+ if (StringUtils.isEmpty(query)) {
+ dataResult = DataCommandResult.createSelectInfoResult(null, null, -1, null,
+ CliStrings.QUERY__MSG__QUERY_EMPTY, false);
+ return dataResult;
+ }
+
+ Object array[] = DataCommands.replaceGfshEnvVar(query, CommandExecutionContext.getShellEnv());
+ query = (String) array[1];
+ boolean limitAdded = false;
+
+ if (!StringUtils.containsIgnoreCase(query, " limit")
+ && !StringUtils.containsIgnoreCase(query, " count(")) {
+ query = query + " limit " + CommandExecutionContext.getShellFetchSize();
+ limitAdded = true;
+ }
+
+ @SuppressWarnings("deprecation")
+ QCompiler compiler = new QCompiler();
+ Set<String> regionsInQuery;
+ try {
+ CompiledValue compiledQuery = compiler.compileQuery(query);
+ Set<String> regions = new HashSet<>();
+ compiledQuery.getRegionsInQuery(regions, null);
+
+ // authorize data read on these regions
+ for (String region : regions) {
+ IntegratedSecurityService.getSecurityService().authorizeRegionRead(region);
+ }
+
+ regionsInQuery = Collections.unmodifiableSet(regions);
+ if (regionsInQuery.size() > 0) {
+ Set<DistributedMember> members =
+ DataCommands.getQueryRegionsAssociatedMembers(regionsInQuery, cache, false);
+ if (members != null && members.size() > 0) {
+ DataCommandFunction function = new DataCommandFunction();
+ DataCommandRequest request = new DataCommandRequest();
+ request.setCommand(CliStrings.QUERY);
+ request.setQuery(query);
+ Subject subject = IntegratedSecurityService.getSecurityService().getSubject();
+ if (subject != null) {
+ request.setPrincipal(subject.getPrincipal());
+ }
+ dataResult = DataCommands.callFunctionForRegion(request, function, members);
+ dataResult.setInputQuery(query);
+ if (limitAdded) {
+ dataResult.setLimit(CommandExecutionContext.getShellFetchSize());
+ }
+ return dataResult;
+ } else {
+ return DataCommandResult.createSelectInfoResult(null, null, -1, null, CliStrings
+ .format(CliStrings.QUERY__MSG__REGIONS_NOT_FOUND, regionsInQuery.toString()), false);
+ }
+ } else {
+ return DataCommandResult.createSelectInfoResult(null, null, -1, null,
+ CliStrings.format(CliStrings.QUERY__MSG__INVALID_QUERY,
+ "Region mentioned in query probably missing /"),
+ false);
+ }
+ } catch (QueryInvalidException qe) {
+ logger.error("{} Failed Error {}", query, qe.getMessage(), qe);
+ return DataCommandResult.createSelectInfoResult(null, null, -1, null,
+ CliStrings.format(CliStrings.QUERY__MSG__INVALID_QUERY, qe.getMessage()), false);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/geode/blob/070ea49d/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/QueryInterceptor.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/QueryInterceptor.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/QueryInterceptor.java
new file mode 100644
index 0000000..559965f
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/QueryInterceptor.java
@@ -0,0 +1,107 @@
+/*
+ * 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 java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.file.Path;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.SystemUtils;
+import org.springframework.shell.event.ParseResult;
+
+import org.apache.geode.management.cli.Result;
+import org.apache.geode.management.internal.cli.AbstractCliAroundInterceptor;
+import org.apache.geode.management.internal.cli.GfshParseResult;
+import org.apache.geode.management.internal.cli.result.CommandResult;
+import org.apache.geode.management.internal.cli.result.CompositeResultData;
+import org.apache.geode.management.internal.cli.result.InfoResultData;
+import org.apache.geode.management.internal.cli.result.ResultBuilder;
+import org.apache.geode.management.internal.cli.result.TabularResultData;
+
+public class QueryInterceptor extends AbstractCliAroundInterceptor {
+ public static final String FILE_ALREADY_EXISTS_MESSAGE =
+ "The specified output file already exists.";
+
+ @Override
+ public Result preExecution(GfshParseResult parseResult) {
+ File outputFile = getOutputFile(parseResult);
+
+ if (outputFile != null && outputFile.exists()) {
+ return ResultBuilder.createUserErrorResult(FILE_ALREADY_EXISTS_MESSAGE);
+ }
+
+ return ResultBuilder.createInfoResult("");
+ }
+
+ @Override
+ public Result postExecution(GfshParseResult parseResult, Result result, Path tempFile) {
+ File outputFile = getOutputFile(parseResult);
+
+ if (outputFile == null) {
+ return result;
+ }
+
+ CommandResult commandResult = (CommandResult) result;
+ CompositeResultData resultData = (CompositeResultData) commandResult.getResultData();
+ CompositeResultData.SectionResultData sectionResultData = resultData.retrieveSectionByIndex(0);
+
+ String limit = sectionResultData.retrieveString("Limit");
+ String resultString = sectionResultData.retrieveString("Result");
+ String rows = sectionResultData.retrieveString("Rows");
+
+ if ("false".equalsIgnoreCase(resultString)) {
+ return result;
+ }
+
+ TabularResultData tabularResultData = sectionResultData.retrieveTableByIndex(0);
+ CommandResult resultTable = new CommandResult(tabularResultData);
+ try {
+ writeResultTableToFile(outputFile, resultTable);
+ // return a result w/ message explaining limit
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ InfoResultData infoResultData = ResultBuilder.createInfoResultData();
+ infoResultData.addLine("Result : " + resultString);
+ if (StringUtils.isNotBlank(limit)) {
+ infoResultData.addLine("Limit : " + limit);
+ }
+ infoResultData.addLine("Rows : " + rows);
+ infoResultData.addLine(SystemUtils.LINE_SEPARATOR);
+ infoResultData.addLine("Query results output to " + outputFile.getAbsolutePath());
+
+ return new CommandResult(infoResultData);
+ }
+
+ private File getOutputFile(ParseResult parseResult) {
+ return (File) parseResult.getArguments()[1];
+ }
+
+ private void writeResultTableToFile(File file, CommandResult commandResult) throws IOException {
+ try (FileWriter fileWriter = new FileWriter(file)) {
+ while (commandResult.hasNextLine()) {
+ fileWriter.write(commandResult.nextLine());
+
+ if (commandResult.hasNextLine()) {
+ fileWriter.write(SystemUtils.LINE_SEPARATOR);
+ }
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/geode/blob/070ea49d/geode-core/src/main/java/org/apache/geode/management/internal/cli/domain/DataCommandResult.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/domain/DataCommandResult.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/domain/DataCommandResult.java
index fe88fc9..b682767 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/domain/DataCommandResult.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/domain/DataCommandResult.java
@@ -14,16 +14,26 @@
*/
package org.apache.geode.management.internal.cli.domain;
-import static org.apache.geode.management.internal.cli.multistep.CLIMultiStepHelper.createBannerResult;
-import static org.apache.geode.management.internal.cli.multistep.CLIMultiStepHelper.createPageResult;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
import org.apache.commons.lang.StringUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.json.JSONObject;
+
import org.apache.geode.DataSerializer;
import org.apache.geode.internal.ClassPathLoader;
import org.apache.geode.management.cli.Result;
import org.apache.geode.management.internal.cli.GfshParser;
import org.apache.geode.management.internal.cli.i18n.CliStrings;
-import org.apache.geode.management.internal.cli.json.GfJsonArray;
import org.apache.geode.management.internal.cli.json.GfJsonException;
import org.apache.geode.management.internal.cli.json.GfJsonObject;
import org.apache.geode.management.internal.cli.result.CompositeResultData;
@@ -31,26 +41,12 @@ import org.apache.geode.management.internal.cli.result.CompositeResultData.Secti
import org.apache.geode.management.internal.cli.result.ResultBuilder;
import org.apache.geode.management.internal.cli.result.TabularResultData;
import org.apache.geode.management.internal.cli.util.JsonUtil;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.json.JSONObject;
-
-import java.io.DataInput;
-import java.io.DataOutput;
-import java.io.IOException;
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
/**
* Domain object used for Data Commands Functions TODO : Implement DataSerializable
*/
public class DataCommandResult implements /* Data */ Serializable {
-
private static Logger logger = LogManager.getLogger();
private static final long serialVersionUID = 1L;
@@ -560,7 +556,7 @@ public class DataCommandResult implements /* Data */ Serializable {
section.addData("Message", infoString);
}
if (inputQuery != null) {
- if (this.limit != -1) {
+ if (this.limit > 0) {
section.addData("Limit", this.limit);
}
if (this.selectResult != null) {
@@ -575,67 +571,6 @@ public class DataCommandResult implements /* Data */ Serializable {
}
}
- /**
- * This method returns a "Page" as dictated by arguments startCount and endCount. Returned result
- * is not standard CommandResult and its consumed by Display Step
- */
- @SuppressWarnings({"rawtypes", "unchecked"})
- public Result pageResult(int startCount, int endCount, String step) {
- List<String> fields = new ArrayList<>();
- List values = new ArrayList<String>();
- fields.add(RESULT_FLAG);
- values.add(operationCompletedSuccessfully);
- fields.add(QUERY_PAGE_START);
- values.add(startCount);
- fields.add(QUERY_PAGE_END);
- values.add(endCount);
- if (errorString != null) {
- fields.add("Message");
- values.add(errorString);
- return createBannerResult(fields, values, step);
- } else {
-
- if (infoString != null) {
- fields.add("Message");
- values.add(infoString);
- }
-
- if (selectResult != null) {
- try {
- TabularResultData table = ResultBuilder.createTabularResultData();
- String[] headers;
- Object[][] rows;
- int rowCount = buildTable(table, startCount, endCount);
- GfJsonArray array = table.getHeaders();
- headers = new String[array.size()];
- rows = new Object[rowCount][array.size()];
- for (int i = 0; i < array.size(); i++) {
- headers[i] = (String) array.get(i);
- List<String> list = table.retrieveAllValues(headers[i]);
- for (int j = 0; j < list.size(); j++) {
- rows[j][i] = list.get(j);
- }
- }
- fields.add(NUM_ROWS);
- values.add((selectResult == null) ? 0 : selectResult.size());
- if (queryTraceString != null) {
- fields.add(QUERY_TRACE);
- values.add(queryTraceString);
- }
- return createPageResult(fields, values, step, headers, rows);
- } catch (GfJsonException e) {
- String[] headers = new String[] {"Error"};
- Object[][] rows = {{e.getMessage()}};
- String fieldsArray[] = {QUERY_PAGE_START, QUERY_PAGE_END};
- Object valuesArray[] = {startCount, endCount};
- return createPageResult(fieldsArray, valuesArray, step, headers, rows);
- }
- } else {
- return createBannerResult(fields, values, step);
- }
- }
- }
-
private int buildTable(TabularResultData table, int startCount, int endCount) {
// Three steps:
// 1a. Convert each row object to a Json object.
@@ -729,6 +664,10 @@ public class DataCommandResult implements /* Data */ Serializable {
this.inputQuery = inputQuery;
}
+ public void setLimit(int limit) {
+ this.limit = limit;
+ }
+
public static class KeyInfo implements /* Data */ Serializable {
private String memberId;
http://git-wip-us.apache.org/repos/asf/geode/blob/070ea49d/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/DataCommandFunction.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/DataCommandFunction.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/DataCommandFunction.java
index e2164a3..4199f9e 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/DataCommandFunction.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/DataCommandFunction.java
@@ -14,7 +14,18 @@
*/
package org.apache.geode.management.internal.cli.functions;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+
import org.apache.commons.lang.StringUtils;
+import org.apache.logging.log4j.Logger;
+import org.json.JSONArray;
+
import org.apache.geode.cache.CacheFactory;
import org.apache.geode.cache.DataPolicy;
import org.apache.geode.cache.Region;
@@ -24,16 +35,13 @@ import org.apache.geode.cache.partition.PartitionRegionHelper;
import org.apache.geode.cache.query.FunctionDomainException;
import org.apache.geode.cache.query.NameResolutionException;
import org.apache.geode.cache.query.Query;
-import org.apache.geode.cache.query.QueryInvalidException;
import org.apache.geode.cache.query.QueryInvocationTargetException;
import org.apache.geode.cache.query.QueryService;
import org.apache.geode.cache.query.SelectResults;
import org.apache.geode.cache.query.Struct;
import org.apache.geode.cache.query.TypeMismatchException;
-import org.apache.geode.cache.query.internal.CompiledValue;
import org.apache.geode.cache.query.internal.DefaultQuery;
import org.apache.geode.cache.query.internal.IndexTrackingQueryObserver;
-import org.apache.geode.cache.query.internal.QCompiler;
import org.apache.geode.cache.query.internal.QueryObserver;
import org.apache.geode.cache.query.internal.QueryObserverHolder;
import org.apache.geode.cache.query.internal.StructImpl;
@@ -46,37 +54,14 @@ import org.apache.geode.internal.cache.InternalCache;
import org.apache.geode.internal.cache.PartitionedRegion;
import org.apache.geode.internal.logging.LogService;
import org.apache.geode.internal.security.SecurityService;
-import org.apache.geode.management.cli.Result;
-import org.apache.geode.management.internal.cli.CliUtil;
-import org.apache.geode.management.internal.cli.commands.DataCommands;
import org.apache.geode.management.internal.cli.domain.DataCommandRequest;
import org.apache.geode.management.internal.cli.domain.DataCommandResult;
import org.apache.geode.management.internal.cli.domain.DataCommandResult.SelectResultRow;
import org.apache.geode.management.internal.cli.i18n.CliStrings;
import org.apache.geode.management.internal.cli.json.GfJsonException;
import org.apache.geode.management.internal.cli.json.GfJsonObject;
-import org.apache.geode.management.internal.cli.multistep.CLIMultiStepHelper;
-import org.apache.geode.management.internal.cli.remote.CommandExecutionContext;
-import org.apache.geode.management.internal.cli.result.CommandResult;
-import org.apache.geode.management.internal.cli.result.CompositeResultData;
-import org.apache.geode.management.internal.cli.result.CompositeResultData.SectionResultData;
-import org.apache.geode.management.internal.cli.result.ResultBuilder;
-import org.apache.geode.management.internal.cli.shell.Gfsh;
import org.apache.geode.management.internal.cli.util.JsonUtil;
import org.apache.geode.pdx.PdxInstance;
-import org.apache.logging.log4j.Logger;
-import org.apache.shiro.subject.Subject;
-import org.json.JSONArray;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
/**
* @since GemFire 7.0
@@ -88,10 +73,6 @@ public class DataCommandFunction extends FunctionAdapter implements InternalEnti
private boolean optimizeForWrite = false;
- protected static final String SELECT_STEP_DISPLAY = "SELECT_DISPLAY";
- protected static final String SELECT_STEP_MOVE = "SELECT_PAGE_MOVE";
- protected static final String SELECT_STEP_END = "SELECT_END";
- protected static final String SELECT_STEP_EXEC = "SELECT_EXEC";
private static final int NESTED_JSON_LENGTH = 20;
// this needs to be static so that it won't get serialized
@@ -802,7 +783,6 @@ public class DataCommandFunction extends FunctionAdapter implements InternalEnti
}
-
/**
* Returns a sorted list of all region full paths found in the specified cache.
*
@@ -836,237 +816,6 @@ public class DataCommandFunction extends FunctionAdapter implements InternalEnti
return list;
}
- private static DataCommandResult cachedResult = null;
-
- public static class SelectDisplayStep extends CLIMultiStepHelper.LocalStep {
-
- public SelectDisplayStep(Object[] arguments) {
- super(SELECT_STEP_DISPLAY, arguments);
- }
-
- @Override
- public Result exec() {
- boolean interactive = (Boolean) commandArguments[2];
- GfJsonObject args = CLIMultiStepHelper.getStepArgs();
- int startCount = args.getInt(DataCommandResult.QUERY_PAGE_START);
- int endCount = args.getInt(DataCommandResult.QUERY_PAGE_END);
- int rows = args.getInt(DataCommandResult.NUM_ROWS); // returns Zero if no rows added so it
- // works.
- boolean flag = args.getBoolean(DataCommandResult.RESULT_FLAG);
- CommandResult commandResult = CLIMultiStepHelper.getDisplayResultFromArgs(args);
- Gfsh.println();
- while (commandResult.hasNextLine()) {
- Gfsh.println(commandResult.nextLine());
- }
-
- if (flag) {
- boolean paginationNeeded = startCount < rows && endCount < rows && interactive;
- if (paginationNeeded) {
- while (true) {
- String message = ("Press n to move to next page, q to quit and p to previous page : ");
- try {
- String step = Gfsh.getCurrentInstance().interact(message);
- if ("n".equals(step)) {
- int nextStart = startCount + getPageSize();
- return CLIMultiStepHelper.createBannerResult(
- new String[] {DataCommandResult.QUERY_PAGE_START,
- DataCommandResult.QUERY_PAGE_END,},
- new Object[] {nextStart, (nextStart + getPageSize())}, SELECT_STEP_MOVE);
- } else if ("p".equals(step)) {
- int nextStart = startCount - getPageSize();
- if (nextStart < 0) {
- nextStart = 0;
- }
- return CLIMultiStepHelper.createBannerResult(
- new String[] {DataCommandResult.QUERY_PAGE_START,
- DataCommandResult.QUERY_PAGE_END},
- new Object[] {nextStart, (nextStart + getPageSize())}, SELECT_STEP_MOVE);
- } else if ("q".equals(step)) {
- return CLIMultiStepHelper.createBannerResult(new String[] {}, new Object[] {},
- SELECT_STEP_END);
- } else {
- Gfsh.println("Unknown option ");
- }
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
- }
- }
- return CLIMultiStepHelper.createBannerResult(new String[] {}, new Object[] {},
- SELECT_STEP_END);
- }
- }
-
- public static class SelectMoveStep extends CLIMultiStepHelper.RemoteStep {
-
- private static final long serialVersionUID = 1L;
-
- public SelectMoveStep(Object[] arguments) {
- super(SELECT_STEP_MOVE, arguments);
- }
-
- @Override
- public Result exec() {
- GfJsonObject args = CLIMultiStepHelper.getStepArgs();
- int startCount = args.getInt(DataCommandResult.QUERY_PAGE_START);
- int endCount = args.getInt(DataCommandResult.QUERY_PAGE_END);
- return cachedResult.pageResult(startCount, endCount, SELECT_STEP_DISPLAY);
- }
- }
-
- public static class SelectExecStep extends CLIMultiStepHelper.RemoteStep {
-
- private static final long serialVersionUID = 1L;
-
- private static SecurityService securityService = SecurityService.getSecurityService();
-
- public SelectExecStep(Object[] arguments) {
- super(SELECT_STEP_EXEC, arguments);
- }
-
- @Override
- public Result exec() {
- String remainingQuery = (String) commandArguments[0];
- boolean interactive = (Boolean) commandArguments[2];
- DataCommandResult result = _select(remainingQuery);
- int endCount = 0;
- cachedResult = result;
- if (interactive) {
- endCount = getPageSize();
- } else {
- if (result.getSelectResult() != null) {
- endCount = result.getSelectResult().size();
- }
- }
- if (interactive) {
- return result.pageResult(0, endCount, SELECT_STEP_DISPLAY);
- } else {
- return CLIMultiStepHelper.createBannerResult(new String[] {}, new Object[] {},
- SELECT_STEP_END);
- }
- }
-
- public DataCommandResult _select(String query) {
- InternalCache cache = (InternalCache) CacheFactory.getAnyInstance();
- DataCommandResult dataResult;
-
- if (StringUtils.isEmpty(query)) {
- dataResult = DataCommandResult.createSelectInfoResult(null, null, -1, null,
- CliStrings.QUERY__MSG__QUERY_EMPTY, false);
- return dataResult;
- }
-
- Object array[] = DataCommands.replaceGfshEnvVar(query, CommandExecutionContext.getShellEnv());
- query = (String) array[1];
- query = addLimit(query);
-
- @SuppressWarnings("deprecation")
- QCompiler compiler = new QCompiler();
- Set<String> regionsInQuery;
- try {
- CompiledValue compiledQuery = compiler.compileQuery(query);
- Set<String> regions = new HashSet<>();
- compiledQuery.getRegionsInQuery(regions, null);
-
- // authorize data read on these regions
- for (String region : regions) {
- securityService.authorizeRegionRead(region);
- }
-
- regionsInQuery = Collections.unmodifiableSet(regions);
- if (regionsInQuery.size() > 0) {
- Set<DistributedMember> members =
- DataCommands.getQueryRegionsAssociatedMembers(regionsInQuery, cache, false);
- if (members != null && members.size() > 0) {
- DataCommandFunction function = new DataCommandFunction();
- DataCommandRequest request = new DataCommandRequest();
- request.setCommand(CliStrings.QUERY);
- request.setQuery(query);
- Subject subject = securityService.getSubject();
- if (subject != null) {
- request.setPrincipal(subject.getPrincipal());
- }
- dataResult = DataCommands.callFunctionForRegion(request, function, members);
- dataResult.setInputQuery(query);
- return dataResult;
- } else {
- return DataCommandResult.createSelectInfoResult(null, null, -1, null, CliStrings.format(
- CliStrings.QUERY__MSG__REGIONS_NOT_FOUND, regionsInQuery.toString()), false);
- }
- } else {
- return DataCommandResult.createSelectInfoResult(null, null, -1, null,
- CliStrings.format(CliStrings.QUERY__MSG__INVALID_QUERY,
- "Region mentioned in query probably missing /"),
- false);
- }
- } catch (QueryInvalidException qe) {
- logger.error("{} Failed Error {}", query, qe.getMessage(), qe);
- return DataCommandResult.createSelectInfoResult(null, null, -1, null,
- CliStrings.format(CliStrings.QUERY__MSG__INVALID_QUERY, qe.getMessage()), false);
- }
- }
-
- private String addLimit(String query) {
- if (StringUtils.containsIgnoreCase(query, " limit")
- || StringUtils.containsIgnoreCase(query, " count(")) {
- return query;
- }
- return query + " limit " + getFetchSize();
- }
- }
-
- public static class SelectQuitStep extends CLIMultiStepHelper.RemoteStep {
-
- public SelectQuitStep(Object[] arguments) {
- super(SELECT_STEP_END, arguments);
- }
-
- private static final long serialVersionUID = 1L;
-
- @Override
- public Result exec() {
- boolean interactive = (Boolean) commandArguments[2];
- GfJsonObject args = CLIMultiStepHelper.getStepArgs();
- DataCommandResult dataResult = cachedResult;
- cachedResult = null;
- if (interactive) {
- return CLIMultiStepHelper.createEmptyResult("END");
- } else {
- CompositeResultData rd = dataResult.toSelectCommandResult();
- SectionResultData section = rd.addSection(CLIMultiStepHelper.STEP_SECTION);
- section.addData(CLIMultiStepHelper.NEXT_STEP_NAME, "END");
- return ResultBuilder.buildResult(rd);
- }
- }
- }
-
- public static int getPageSize() {
- int pageSize = -1;
- Map<String, String> session;
- if (CliUtil.isGfshVM()) {
- session = Gfsh.getCurrentInstance().getEnv();
- } else {
- session = CommandExecutionContext.getShellEnv();
- }
- if (session != null) {
- String size = session.get(Gfsh.ENV_APP_COLLECTION_LIMIT);
- if (StringUtils.isEmpty(size)) {
- pageSize = Gfsh.DEFAULT_APP_COLLECTION_LIMIT;
- } else {
- pageSize = Integer.parseInt(size);
- }
- }
- if (pageSize == -1) {
- pageSize = Gfsh.DEFAULT_APP_COLLECTION_LIMIT;
- }
- return pageSize;
- }
-
- private static int getFetchSize() {
- return CommandExecutionContext.getShellFetchSize();
- }
-
public static String getLogMessage(QueryObserver observer, long startTime, String query) {
String usedIndexesString = null;
float time = 0.0f;
http://git-wip-us.apache.org/repos/asf/geode/blob/070ea49d/geode-core/src/main/java/org/apache/geode/management/internal/cli/shell/Gfsh.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/shell/Gfsh.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/shell/Gfsh.java
index c5ff6b6..2124d64 100755
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/shell/Gfsh.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/shell/Gfsh.java
@@ -14,8 +14,35 @@
*/
package org.apache.geode.management.internal.cli.shell;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.net.URL;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.TreeMap;
+import java.util.logging.Level;
+import java.util.logging.LogManager;
+import java.util.logging.Logger;
+
import jline.Terminal;
import jline.console.ConsoleReader;
+import org.springframework.shell.core.AbstractShell;
+import org.springframework.shell.core.ExecutionStrategy;
+import org.springframework.shell.core.ExitShellRequest;
+import org.springframework.shell.core.JLineLogHandler;
+import org.springframework.shell.core.JLineShell;
+import org.springframework.shell.core.Parser;
+import org.springframework.shell.event.ShellStatus.Status;
+
import org.apache.geode.internal.Banner;
import org.apache.geode.internal.GemFireVersion;
import org.apache.geode.internal.lang.ClassUtils;
@@ -39,32 +66,6 @@ import org.apache.geode.management.internal.cli.shell.jline.GfshHistory;
import org.apache.geode.management.internal.cli.shell.jline.GfshUnsupportedTerminal;
import org.apache.geode.management.internal.cli.shell.unsafe.GfshSignalHandler;
import org.apache.geode.management.internal.cli.util.CommentSkipHelper;
-import org.springframework.shell.core.AbstractShell;
-import org.springframework.shell.core.ExecutionStrategy;
-import org.springframework.shell.core.ExitShellRequest;
-import org.springframework.shell.core.JLineLogHandler;
-import org.springframework.shell.core.JLineShell;
-import org.springframework.shell.core.Parser;
-import org.springframework.shell.event.ShellStatus.Status;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.net.URL;
-import java.text.MessageFormat;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Scanner;
-import java.util.TreeMap;
-import java.util.logging.Level;
-import java.util.logging.LogManager;
-import java.util.logging.Logger;
/**
* Extends an interactive shell provided by
@@ -84,7 +85,7 @@ import java.util.logging.Logger;
* @since GemFire 7.0
*/
public class Gfsh extends JLineShell {
- public static final int DEFAULT_APP_FETCH_SIZE = 1000;
+ public static final int DEFAULT_APP_FETCH_SIZE = 100;
public static final int DEFAULT_APP_LAST_EXIT_STATUS = 0;
public static final int DEFAULT_APP_COLLECTION_LIMIT = 20;
public static final boolean DEFAULT_APP_QUIET_EXECUTION = false;
http://git-wip-us.apache.org/repos/asf/geode/blob/070ea49d/geode-core/src/main/java/org/apache/geode/management/internal/web/controllers/DataCommandsController.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/web/controllers/DataCommandsController.java b/geode-core/src/main/java/org/apache/geode/management/internal/web/controllers/DataCommandsController.java
index ce2ed54..9312051 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/web/controllers/DataCommandsController.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/web/controllers/DataCommandsController.java
@@ -14,9 +14,8 @@
*/
package org.apache.geode.management.internal.web.controllers;
-import org.apache.geode.internal.lang.StringUtils;
-import org.apache.geode.management.internal.cli.i18n.CliStrings;
-import org.apache.geode.management.internal.cli.util.CommandStringBuilder;
+import java.util.concurrent.Callable;
+
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
@@ -26,7 +25,9 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.WebRequest;
-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.util.CommandStringBuilder;
/**
* The DataCommandsController class implements GemFire Management REST API web service endpoints for
@@ -185,8 +186,6 @@ public class DataCommandsController extends AbstractCommandsController {
@RequestMapping(method = RequestMethod.GET, value = "/regions/data/query")
public Callable<ResponseEntity<String>> query(final WebRequest request,
@RequestParam(CliStrings.QUERY__QUERY) final String oql,
- @RequestParam(value = CliStrings.QUERY__STEPNAME,
- defaultValue = CliStrings.QUERY__STEPNAME__DEFAULTVALUE) final String stepName,
@RequestParam(value = CliStrings.QUERY__INTERACTIVE,
defaultValue = "true") final Boolean interactive) {
// logRequest(request);
@@ -194,7 +193,6 @@ public class DataCommandsController extends AbstractCommandsController {
final CommandStringBuilder command = new CommandStringBuilder(CliStrings.QUERY);
command.addOption(CliStrings.QUERY__QUERY, decode(oql));
- command.addOption(CliStrings.QUERY__STEPNAME, stepName);
command.addOption(CliStrings.QUERY__INTERACTIVE,
String.valueOf(Boolean.TRUE.equals(interactive)));
http://git-wip-us.apache.org/repos/asf/geode/blob/070ea49d/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/QueryCommandTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/QueryCommandTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/QueryCommandTest.java
new file mode 100644
index 0000000..8f30bfe
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/QueryCommandTest.java
@@ -0,0 +1,267 @@
+/*
+ * 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.assertj.core.api.Assertions.assertThat;
+
+import java.io.File;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import com.google.common.io.Files;
+import org.apache.commons.io.FileUtils;
+import org.junit.Before;
+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 org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import org.apache.geode.cache.Cache;
+import org.apache.geode.cache.Region;
+import org.apache.geode.cache.RegionShortcut;
+import org.apache.geode.management.cli.Result;
+import org.apache.geode.management.internal.cli.result.CommandResult;
+import org.apache.geode.management.internal.cli.shell.Gfsh;
+import org.apache.geode.test.dunit.rules.GfshShellConnectionRule;
+import org.apache.geode.test.dunit.rules.ServerStarterRule;
+import org.apache.geode.test.junit.categories.IntegrationTest;
+
+@Category(IntegrationTest.class)
+@RunWith(Parameterized.class)
+public class QueryCommandTest {
+ @Parameterized.Parameters(name = "Connect via http: {0}")
+ public static Object[] data() {
+ return new Object[] {true, false};
+ }
+
+ @Parameterized.Parameter
+ public boolean useHttp;
+
+ @ClassRule
+ public static ServerStarterRule server =
+ new ServerStarterRule().withJMXManager().withRegion(RegionShortcut.REPLICATE, "simpleRegion")
+ .withRegion(RegionShortcut.REPLICATE, "complexRegion");
+
+ @Rule
+ public GfshShellConnectionRule gfsh = new GfshShellConnectionRule();
+
+ @Rule
+ public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ @BeforeClass
+ public static void populateRegions() {
+ Cache cache = server.getCache();
+ Region<String, String> simpleRegion = cache.getRegion("simpleRegion");
+ Region<String, Customer> complexRegion = cache.getRegion("complexRegion");
+
+ for (int i = 0; i < Gfsh.DEFAULT_APP_FETCH_SIZE + 1; i++) {
+ String key = "key" + i;
+
+ simpleRegion.put(key, "value" + i);
+ complexRegion.put(key, new Customer("name" + i, "address" + i));
+ }
+ }
+
+ @Before
+ public void connect() throws Exception {
+ if (useHttp) {
+ gfsh.connectAndVerify(server.getHttpPort(), GfshShellConnectionRule.PortType.http);
+ } else {
+ gfsh.connectAndVerify(server.getJmxPort(), GfshShellConnectionRule.PortType.jmxManger);
+ }
+ }
+
+ @Test
+ public void doesShowLimitIfLimitNotInQuery() throws Exception {
+ String output = gfsh.execute("query --query='select * from /simpleRegion'");
+ assertThat(output).contains("Rows : " + Gfsh.DEFAULT_APP_FETCH_SIZE);
+ assertThat(output).contains("Limit : " + Gfsh.DEFAULT_APP_FETCH_SIZE);
+ assertThatOutputHasResult(output);
+ }
+
+ @Test
+ public void doesNotShowLimitIfLimitInQuery() throws Exception {
+ String output = gfsh.execute("query --query='select * from /simpleRegion limit 50'");
+ assertThat(output).contains("Rows : 50");
+ assertThat(output).doesNotContain("Limit");
+ assertThatOutputHasResult(output);
+ }
+
+ @Test
+ public void invalidQueryShouldNotCreateFile() throws Exception {
+ File outputFile = temporaryFolder.newFile("queryOutput.txt");
+ FileUtils.deleteQuietly(outputFile);
+
+ String output =
+ gfsh.execute("query --query='invalid query' --file=" + outputFile.getAbsolutePath());
+ assertThat(outputFile).doesNotExist();
+
+ assertThatOutputHasNoResult(output);
+ assertThat(output).doesNotContain("Query results output to");
+ }
+
+ @Test
+ public void queryWithInvalidRegionNameDoesNotCreateFile() throws Exception {
+ File outputFile = temporaryFolder.newFile("queryOutput.txt");
+ FileUtils.deleteQuietly(outputFile);
+
+ String output = gfsh.execute(
+ "query --query='select * from /nonExistentRegion' --file=" + outputFile.getAbsolutePath());
+ assertThat(outputFile).doesNotExist();
+
+ assertThatOutputHasNoResult(output);
+ assertThat(output).doesNotContain("Query results output to");
+ }
+
+ @Test
+ public void outputToFileStillDisplaysResultMetaData() throws Exception {
+ File outputFile = temporaryFolder.newFile("queryOutput.txt");
+ FileUtils.deleteQuietly(outputFile);
+
+ String output = gfsh.execute(
+ "query --query='select * from /simpleRegion' --file=" + outputFile.getAbsolutePath());
+
+ assertThat(output).contains("Rows");
+ assertThat(output).contains("Limit");
+ assertThat(output).contains("Query results output to");
+ assertThatOutputHasResult(output);
+ }
+
+ @Test
+ public void doesNotOverwriteExistingFile() throws Exception {
+ File outputFile = temporaryFolder.newFile("queryOutput.txt");
+ assertThat(outputFile).exists();
+
+ CommandResult result = gfsh.executeCommand(
+ "query --query='select * from /simpleRegion' --file=" + outputFile.getAbsolutePath());
+
+ assertThat(result.getStatus()).isEqualTo(Result.Status.ERROR);
+ assertThat(result.getContent().getString("message"))
+ .contains("The specified output file already exists.");
+ }
+
+ @Test
+ public void canOutputSimpleRegionToFile() throws Exception {
+ File outputFile = temporaryFolder.newFile("queryOutput.txt");
+ FileUtils.deleteQuietly(outputFile);
+
+ CommandResult result = gfsh.executeAndVerifyCommand(
+ "query --query='select * from /simpleRegion' --file=" + outputFile.getAbsolutePath());
+ assertThat(outputFile).exists();
+ assertThat(result.getContent().toString()).contains(outputFile.getAbsolutePath());
+
+ List<String> lines = Files.readLines(outputFile, StandardCharsets.UTF_8);
+
+ assertThat(lines.get(0)).isEqualTo("Result");
+ assertThat(lines.get(1)).isEqualTo("--------");
+ lines.subList(2, lines.size()).forEach(line -> assertThat(line).matches("value\\d+"));
+ }
+
+ @Test
+ public void canOutputComplexRegionToFile() throws Exception {
+ File outputFile = temporaryFolder.newFile("queryOutput.txt");
+ FileUtils.deleteQuietly(outputFile);
+
+ CommandResult result = gfsh.executeAndVerifyCommand(
+ "query --query='select c.name, c.address from /complexRegion c' --file="
+ + outputFile.getAbsolutePath());
+ assertThat(outputFile).exists();
+ assertThat(result.getContent().toString()).contains(outputFile.getAbsolutePath());
+
+ List<String> lines = Files.readLines(outputFile, StandardCharsets.UTF_8);
+
+ assertThat(lines.get(0)).containsPattern("name\\s+\\|\\s+address");
+ lines.subList(2, lines.size())
+ .forEach(line -> assertThat(line).matches("name\\d+\\s+\\|\\s+address\\d+"));
+ }
+
+ @Test
+ public void outputDisplaysResultsFromComplexRegion() throws Exception {
+ String result = gfsh.execute("query --query='select c.name, c.address from /complexRegion c'");
+
+ String[] resultLines = splitOnLineBreaks(result);
+
+ assertThat(resultLines[0]).containsPattern("Result\\s+:\\s+true");
+ assertThat(resultLines[1]).containsPattern("Limit\\s+:\\s+100");
+ assertThat(resultLines[2]).containsPattern("Rows\\s+:\\s+100");
+ assertThat(resultLines[3]).containsPattern("name\\s+\\|\\s+address");
+ Arrays.asList(resultLines).subList(5, resultLines.length)
+ .forEach(line -> assertThat(line).matches("name\\d+\\s+\\|\\s+address\\d+"));
+ }
+
+ @Test
+ public void queryWithInvalidRegionNameGivesDescriptiveErrorMessage() throws Exception {
+ String output = gfsh.execute("query --query='select * from /nonExistentRegion'");
+ assertThatOutputHasNoResult(output);
+ assertThatOutputHasMessage(output,
+ "Cannot find regions <[/nonExistentRegion]> in any of the members");
+ }
+
+ @Test
+ public void invalidQueryGivesDescriptiveErrorMessage() throws Exception {
+ String output = gfsh.execute("query --query='this is not a valid query'");
+
+ assertThatOutputHasNoResult(output);
+ assertThatOutputHasMessage(output, "Query is invalid due for error : <Syntax error in query:");
+ }
+
+ @Test
+ public void queryGivesDescriptiveErrorMessageIfNoQueryIsSpecified() throws Exception {
+ String output = gfsh.execute("query");
+
+ assertThat(output)
+ .contains("You should specify option (--query, --file, --interactive) for this command");
+ }
+
+ private void assertThatOutputHasResult(String output) {
+ String resultPattern = "Result\\s+:\\s+" + "true";
+ assertThat(output).containsPattern(resultPattern);
+ }
+
+ private void assertThatOutputHasNoResult(String output) {
+ String resultPattern = "Result\\s+:\\s+" + "false";
+ assertThat(output).containsPattern(resultPattern);
+ }
+
+ private void assertThatOutputHasMessage(String output, String message) {
+ String patternToMatch = "Message\\s+:\\s+" + Pattern.quote(message);
+ assertThat(output).containsPattern(patternToMatch);
+ }
+
+ private String[] splitOnLineBreaks(String multilineString) {
+ return multilineString.split("[\\r\\n]+");
+ }
+
+ public static class Customer {
+ public String name;
+ public String address;
+
+ public Customer(String name, String address) {
+ this.name = name;
+ this.address = address;
+ }
+
+ public String toString() {
+ return name + address;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/geode/blob/070ea49d/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/QueryCommandUnitTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/QueryCommandUnitTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/QueryCommandUnitTest.java
new file mode 100644
index 0000000..11da4b7
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/QueryCommandUnitTest.java
@@ -0,0 +1,30 @@
+/*
+ * 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 org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import org.apache.geode.test.junit.categories.UnitTest;
+
+@Category(UnitTest.class)
+public class QueryCommandUnitTest {
+
+ @Test
+ public void query() throws Exception {
+ QueryCommand queryCommand = new QueryCommand();
+ }
+}
http://git-wip-us.apache.org/repos/asf/geode/blob/070ea49d/geode-core/src/test/java/org/apache/geode/management/internal/cli/result/ResultBuilderTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/result/ResultBuilderTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/result/ResultBuilderTest.java
new file mode 100644
index 0000000..0ce89c1
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/cli/result/ResultBuilderTest.java
@@ -0,0 +1,131 @@
+/*
+ * 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.result;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.json.JSONArray;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import org.apache.geode.management.cli.Result;
+import org.apache.geode.test.junit.categories.UnitTest;
+
+@Category(UnitTest.class)
+public class ResultBuilderTest {
+
+ @Test
+ public void messageExistsForString() throws Exception {
+ CommandResult result = (CommandResult) ResultBuilder.createInfoResult("test message");
+ assertThat(result.getContent().get("message")).isInstanceOf(JSONArray.class);
+ assertThat(result.getContent().get("message").toString()).isEqualTo("[\"test message\"]");
+ }
+
+ @Test
+ public void messageExistsForEmpty() throws Exception {
+ CommandResult result = (CommandResult) ResultBuilder.createInfoResult("");
+ assertThat(result.getContent().get("message")).isInstanceOf(JSONArray.class);
+ assertThat(result.getContent().get("message").toString()).isEqualTo("[\"\"]");
+
+ }
+
+ @Test
+ public void messageExistsForNull() throws Exception {
+ CommandResult result = (CommandResult) ResultBuilder.createInfoResult(null);
+ assertThat(result.getContent().get("message")).isInstanceOf(JSONArray.class);
+ assertThat(result.getContent().get("message").toString()).isEqualTo("[null]");
+
+ }
+
+ @Test
+ public void infoResultDataStructure() throws Exception {
+ InfoResultData result = ResultBuilder.createInfoResultData();
+ result.addLine("line 1");
+ result.addLine("line 2");
+ result.setFooter("Feet!");
+ result.setHeader("Header");
+ CommandResult cmdResult = (CommandResult) ResultBuilder.buildResult(result);
+
+ assertThat(cmdResult.getGfJsonObject().has("header")).isTrue();
+ assertThat(cmdResult.getGfJsonObject().has("content")).isTrue();
+ assertThat(cmdResult.getGfJsonObject().has("footer")).isTrue();
+
+ assertThat(cmdResult.getContent().has("message")).isTrue();
+
+ assertThat(cmdResult.getStatus()).isEqualTo(Result.Status.OK);
+ }
+
+ @Test
+ public void errorResultDataStructure() throws Exception {
+ ErrorResultData result = ResultBuilder.createErrorResultData();
+ result.addLine("line 1");
+ result.addLine("line 2");
+ result.setFooter("Feet!");
+ result.setHeader("Header");
+ CommandResult cmdResult = (CommandResult) ResultBuilder.buildResult(result);
+
+ assertThat(cmdResult.getGfJsonObject().has("header")).isTrue();
+ assertThat(cmdResult.getGfJsonObject().has("content")).isTrue();
+ assertThat(cmdResult.getGfJsonObject().has("footer")).isTrue();
+
+ assertThat(cmdResult.getContent().has("message")).isTrue();
+
+ assertThat(cmdResult.getStatus()).isEqualTo(Result.Status.ERROR);
+ }
+
+ @Test
+ public void tabularResultDataStructure() throws Exception {
+ TabularResultData result = ResultBuilder.createTabularResultData();
+ result.accumulate("column1", "value11");
+ result.accumulate("column1", "value12");
+ result.accumulate("column2", "value21");
+ result.accumulate("column2", "value22");
+
+ result.setFooter("Feet!");
+ result.setHeader("Header");
+ CommandResult cmdResult = (CommandResult) ResultBuilder.buildResult(result);
+
+ assertThat(cmdResult.getGfJsonObject().has("header")).isTrue();
+ assertThat(cmdResult.getGfJsonObject().has("content")).isTrue();
+ assertThat(cmdResult.getGfJsonObject().has("footer")).isTrue();
+
+ assertThat(cmdResult.getContent().has("column1")).isTrue();
+ assertThat(cmdResult.getContent().has("column2")).isTrue();
+
+ assertThat(cmdResult.getContent().getJSONArray("column1").toString()).contains("value11");
+ assertThat(cmdResult.getContent().getJSONArray("column1").toString()).contains("value12");
+ assertThat(cmdResult.getContent().getJSONArray("column2").toString()).contains("value21");
+ assertThat(cmdResult.getContent().getJSONArray("column2").toString()).contains("value22");
+ }
+
+ @Test
+ public void compositeResultDataStructure() throws Exception {
+ CompositeResultData result = ResultBuilder.createCompositeResultData();
+
+ result.setFooter("Feet!");
+ result.setHeader("Header");
+
+ assertThat(result.getGfJsonObject().has("header")).isTrue();
+ assertThat(result.getGfJsonObject().has("content")).isTrue();
+ assertThat(result.getGfJsonObject().has("footer")).isTrue();
+
+ // build up an example
+ result.addSection().addData("section 0 key", "section 0 value");
+ result.addSection().addTable().accumulate("table 1 column", "table 1 value");
+
+ result.addSection();
+
+ }
+}