You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by kl...@apache.org on 2017/08/11 22:54:49 UTC

geode git commit: GEODE-3260: Refactoring FunctionCommands

Repository: geode
Updated Branches:
  refs/heads/develop 5d6cad775 -> 90f5440de


GEODE-3260: Refactoring FunctionCommands

This closes #691


Project: http://git-wip-us.apache.org/repos/asf/geode/repo
Commit: http://git-wip-us.apache.org/repos/asf/geode/commit/90f5440d
Tree: http://git-wip-us.apache.org/repos/asf/geode/tree/90f5440d
Diff: http://git-wip-us.apache.org/repos/asf/geode/diff/90f5440d

Branch: refs/heads/develop
Commit: 90f5440de8ec747f301a309a0a34101e8defcd29
Parents: 5d6cad7
Author: YehEmily <em...@gmail.com>
Authored: Mon Aug 7 12:56:17 2017 -0700
Committer: Kirk Lund <kl...@apache.org>
Committed: Fri Aug 11 15:45:06 2017 -0700

----------------------------------------------------------------------
 .../cli/commands/DestroyFunctionCommand.java    | 163 ++++++
 .../cli/commands/ExecuteFunctionCommand.java    | 330 ++++++++++++
 .../internal/cli/commands/FunctionCommands.java | 537 -------------------
 .../cli/commands/ListFunctionCommand.java       | 100 ++++
 .../controllers/FunctionCommandsController.java |  13 +-
 .../internal/security/TestCommand.java          |   4 +-
 6 files changed, 603 insertions(+), 544 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/geode/blob/90f5440d/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DestroyFunctionCommand.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DestroyFunctionCommand.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DestroyFunctionCommand.java
new file mode 100644
index 0000000..37beb97
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DestroyFunctionCommand.java
@@ -0,0 +1,163 @@
+/*
+ * 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.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.springframework.shell.core.annotation.CliCommand;
+import org.springframework.shell.core.annotation.CliOption;
+
+import org.apache.geode.cache.execute.Execution;
+import org.apache.geode.cache.execute.Function;
+import org.apache.geode.cache.execute.FunctionException;
+import org.apache.geode.cache.execute.FunctionService;
+import org.apache.geode.distributed.DistributedMember;
+import org.apache.geode.internal.cache.InternalCache;
+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.AbstractCliAroundInterceptor;
+import org.apache.geode.management.internal.cli.GfshParseResult;
+import org.apache.geode.management.internal.cli.functions.UnregisterFunction;
+import org.apache.geode.management.internal.cli.i18n.CliStrings;
+import org.apache.geode.management.internal.cli.result.ErrorResultData;
+import org.apache.geode.management.internal.cli.result.ResultBuilder;
+import org.apache.geode.management.internal.security.ResourceOperation;
+import org.apache.geode.security.ResourcePermission;
+
+public class DestroyFunctionCommand implements GfshCommand {
+  @CliCommand(value = CliStrings.DESTROY_FUNCTION, help = CliStrings.DESTROY_FUNCTION__HELP)
+  @CliMetaData(relatedTopic = {CliStrings.TOPIC_GEODE_FUNCTION},
+      interceptor = "org.apache.geode.management.internal.cli.commands.DestroyFunctionCommand$Interceptor")
+  @ResourceOperation(resource = ResourcePermission.Resource.CLUSTER,
+      operation = ResourcePermission.Operation.MANAGE, target = ResourcePermission.Target.JAR)
+  // TODO: Add optioncontext for functionId
+  public Result destroyFunction(
+      @CliOption(key = CliStrings.DESTROY_FUNCTION__ID, mandatory = true,
+          help = CliStrings.DESTROY_FUNCTION__HELP) String functionId,
+      @CliOption(key = {CliStrings.GROUP, CliStrings.GROUPS},
+          optionContext = ConverterHint.MEMBERGROUP,
+          help = CliStrings.DESTROY_FUNCTION__ONGROUPS__HELP) String[] groups,
+      @CliOption(key = CliStrings.MEMBER, optionContext = ConverterHint.MEMBERIDNAME,
+          help = CliStrings.DESTROY_FUNCTION__ONMEMBER__HELP) String memberId) {
+    try {
+      InternalCache cache = getCache();
+      Set<DistributedMember> dsMembers = new HashSet<>();
+      if (groups != null && memberId != null) {
+        return ResultBuilder
+            .createUserErrorResult(CliStrings.DESTROY_FUNCTION__MSG__PROVIDE_OPTION);
+      } else if (groups != null && groups.length > 0) {
+        // execute on group members
+        for (String grp : groups) {
+          dsMembers.addAll(cache.getDistributedSystem().getGroupMembers(grp));
+        }
+        @SuppressWarnings("unchecked")
+        Result results = executeFunction(cache, dsMembers, functionId);
+        return results;
+      } else if (memberId != null) {
+        // execute on member
+        dsMembers.add(getMember(cache, memberId));
+        @SuppressWarnings("unchecked")
+        Result results = executeFunction(cache, dsMembers, functionId);
+        return results;
+      } else {
+        // no option provided.
+        @SuppressWarnings("unchecked")
+        Result results = executeFunction(cache, cache.getMembers(), functionId);
+        return results;
+      }
+    } catch (Exception e) {
+      ErrorResultData errorResultData = ResultBuilder.createErrorResultData()
+          .setErrorCode(ResultBuilder.ERRORCODE_DEFAULT).addLine(e.getMessage());
+      return ResultBuilder.buildResult(errorResultData);
+    }
+  }
+
+  private Result executeFunction(InternalCache cache, Set<DistributedMember> DsMembers,
+      String functionId) {
+    // unregister on a set of of members
+    Function unregisterFunction = new UnregisterFunction();
+    FunctionService.registerFunction(unregisterFunction);
+    List resultList;
+
+    if (DsMembers.isEmpty()) {
+      return ResultBuilder.createInfoResult("No members for execution");
+    }
+    Object[] obj = new Object[1];
+    obj[0] = functionId;
+
+    Execution execution = FunctionService.onMembers(DsMembers).setArguments(obj);
+
+    if (execution == null) {
+      cache.getLogger().error("executeUnregister execution is null");
+      ErrorResultData errorResultData =
+          ResultBuilder.createErrorResultData().setErrorCode(ResultBuilder.ERRORCODE_DEFAULT)
+              .addLine(CliStrings.DESTROY_FUNCTION__MSG__CANNOT_EXECUTE);
+      return (ResultBuilder.buildResult(errorResultData));
+    }
+    try {
+      resultList = (ArrayList) execution.execute(unregisterFunction).getResult();
+    } catch (FunctionException ex) {
+      ErrorResultData errorResultData = ResultBuilder.createErrorResultData()
+          .setErrorCode(ResultBuilder.ERRORCODE_DEFAULT).addLine(ex.getMessage());
+      return (ResultBuilder.buildResult(errorResultData));
+    }
+    String resultStr = ((String) resultList.get(0));
+    if (resultStr.equals("Succeeded in unregistering")) {
+      StringBuilder members = new StringBuilder();
+      for (DistributedMember member : DsMembers) {
+        members.append(member.getId());
+        members.append(",");
+      }
+      return ResultBuilder.createInfoResult("Destroyed " + functionId + " Successfully on "
+          + members.toString().substring(0, members.toString().length() - 1));
+    } else {
+      return ResultBuilder.createInfoResult("Failed in unregistering");
+    }
+  }
+
+  /**
+   * Interceptor used by gfsh to intercept execution of destroy.
+   */
+  public static class Interceptor extends AbstractCliAroundInterceptor {
+    @Override
+    public Result preExecution(GfshParseResult parseResult) {
+      Map<String, String> paramValueMap = parseResult.getParamValueStrings();
+      paramValueMap.entrySet();
+      String onGroup = paramValueMap.get(CliStrings.GROUP);
+      String onMember = paramValueMap.get(CliStrings.MEMBER);
+
+      if ((onGroup == null && onMember == null)) {
+        Response response = readYesNo("Do you really want to destroy "
+            + paramValueMap.get(CliStrings.DESTROY_FUNCTION__ID) + " on entire DS?", Response.NO);
+        if (response == Response.NO) {
+          return ResultBuilder.createShellClientAbortOperationResult(
+              "Aborted destroy of " + paramValueMap.get(CliStrings.DESTROY_FUNCTION__ID));
+        } else {
+          return ResultBuilder
+              .createInfoResult("Destroying " + paramValueMap.get(CliStrings.DESTROY_FUNCTION__ID));
+        }
+      } else {
+        return ResultBuilder
+            .createInfoResult("Destroying " + paramValueMap.get(CliStrings.DESTROY_FUNCTION__ID));
+      }
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/90f5440d/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/ExecuteFunctionCommand.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/ExecuteFunctionCommand.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/ExecuteFunctionCommand.java
new file mode 100644
index 0000000..dad9742
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/ExecuteFunctionCommand.java
@@ -0,0 +1,330 @@
+/*
+ * 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.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import org.apache.commons.lang.BooleanUtils;
+import org.springframework.shell.core.annotation.CliCommand;
+import org.springframework.shell.core.annotation.CliOption;
+
+import org.apache.geode.cache.Region;
+import org.apache.geode.cache.execute.Execution;
+import org.apache.geode.cache.execute.Function;
+import org.apache.geode.cache.execute.FunctionException;
+import org.apache.geode.cache.execute.FunctionService;
+import org.apache.geode.cache.execute.ResultCollector;
+import org.apache.geode.distributed.DistributedMember;
+import org.apache.geode.internal.ClassPathLoader;
+import org.apache.geode.internal.cache.InternalCache;
+import org.apache.geode.management.DistributedRegionMXBean;
+import org.apache.geode.management.ManagementService;
+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.MBeanJMXAdapter;
+import org.apache.geode.management.internal.cli.CliUtil;
+import org.apache.geode.management.internal.cli.LogWrapper;
+import org.apache.geode.management.internal.cli.functions.UserFunctionExecution;
+import org.apache.geode.management.internal.cli.i18n.CliStrings;
+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.security.ResourceOperation;
+import org.apache.geode.security.ResourcePermission;
+
+public class ExecuteFunctionCommand implements GfshCommand {
+  @CliCommand(value = CliStrings.EXECUTE_FUNCTION, help = CliStrings.EXECUTE_FUNCTION__HELP)
+  @CliMetaData(relatedTopic = {CliStrings.TOPIC_GEODE_FUNCTION})
+  @ResourceOperation(resource = ResourcePermission.Resource.DATA,
+      operation = ResourcePermission.Operation.WRITE)
+  public Result executeFunction(
+      // TODO: Add optioncontext for functionID
+      @CliOption(key = CliStrings.EXECUTE_FUNCTION__ID, mandatory = true,
+          help = CliStrings.EXECUTE_FUNCTION__ID__HELP) String functionId,
+      @CliOption(key = {CliStrings.GROUP, CliStrings.GROUPS},
+          optionContext = ConverterHint.MEMBERGROUP,
+          help = CliStrings.EXECUTE_FUNCTION__ONGROUPS__HELP) String[] onGroups,
+      @CliOption(key = CliStrings.MEMBER, optionContext = ConverterHint.MEMBERIDNAME,
+          help = CliStrings.EXECUTE_FUNCTION__ONMEMBER__HELP) String onMember,
+      @CliOption(key = CliStrings.EXECUTE_FUNCTION__ONREGION,
+          optionContext = ConverterHint.REGION_PATH,
+          help = CliStrings.EXECUTE_FUNCTION__ONREGION__HELP) String onRegion,
+      @CliOption(key = CliStrings.EXECUTE_FUNCTION__ARGUMENTS,
+          help = CliStrings.EXECUTE_FUNCTION__ARGUMENTS__HELP) String[] arguments,
+      @CliOption(key = CliStrings.EXECUTE_FUNCTION__RESULTCOLLECTOR,
+          help = CliStrings.EXECUTE_FUNCTION__RESULTCOLLECTOR__HELP) String resultCollector,
+      @CliOption(key = CliStrings.EXECUTE_FUNCTION__FILTER,
+          help = CliStrings.EXECUTE_FUNCTION__FILTER__HELP) String filterString) {
+    CompositeResultData executeFunctionResultTable = ResultBuilder.createCompositeResultData();
+    TabularResultData resultTable = executeFunctionResultTable.addSection().addTable("Table1");
+    String headerText = "Execution summary";
+    resultTable.setHeader(headerText);
+    ResultCollector resultCollectorInstance = null;
+    Set<String> filters = new HashSet<>();
+    Execution execution;
+    if (functionId != null) {
+      functionId = functionId.trim();
+    }
+    if (onRegion != null) {
+      onRegion = onRegion.trim();
+    }
+    if (onMember != null) {
+      onMember = onMember.trim();
+    }
+    if (filterString != null) {
+      filterString = filterString.trim();
+    }
+
+    try {
+      // validate otherwise return right away. no need to process anything
+      if (functionId == null || functionId.length() == 0) {
+        ErrorResultData errorResultData =
+            ResultBuilder.createErrorResultData().setErrorCode(ResultBuilder.ERRORCODE_DEFAULT)
+                .addLine(CliStrings.EXECUTE_FUNCTION__MSG__MISSING_FUNCTIONID);
+        return ResultBuilder.buildResult(errorResultData);
+      }
+
+      if (moreThanOneIsTrue(onRegion != null, onMember != null, onGroups != null)) {
+        // Provide Only one of region/member/groups
+        ErrorResultData errorResultData =
+            ResultBuilder.createErrorResultData().setErrorCode(ResultBuilder.ERRORCODE_DEFAULT)
+                .addLine(CliStrings.EXECUTE_FUNCTION__MSG__OPTIONS);
+        return ResultBuilder.buildResult(errorResultData);
+      } else if ((onRegion == null || onRegion.length() == 0) && (filterString != null)) {
+        ErrorResultData errorResultData = ResultBuilder.createErrorResultData()
+            .setErrorCode(ResultBuilder.ERRORCODE_DEFAULT)
+            .addLine(CliStrings.EXECUTE_FUNCTION__MSG__MEMBER_SHOULD_NOT_HAVE_FILTER_FOR_EXECUTION);
+        return ResultBuilder.buildResult(errorResultData);
+      }
+
+      InternalCache cache = getCache();
+
+      if (resultCollector != null) {
+        resultCollectorInstance =
+            (ResultCollector) ClassPathLoader.getLatest().forName(resultCollector).newInstance();
+      }
+
+      if (filterString != null && filterString.length() > 0) {
+        filters.add(filterString);
+      }
+
+      if (onRegion == null && onMember == null && onGroups == null) {
+        // run function on all the members excluding locators bug#46113
+        // if user wish to execute on locator then he can choose --member or --group option
+        Set<DistributedMember> dsMembers = CliUtil.getAllNormalMembers(cache);
+        if (dsMembers.size() > 0) {
+          new UserFunctionExecution();
+          LogWrapper.getInstance().info(CliStrings
+              .format(CliStrings.EXECUTE_FUNCTION__MSG__EXECUTING_0_ON_ENTIRE_DS, functionId));
+          for (DistributedMember member : dsMembers) {
+            executeAndGetResults(functionId, null, resultCollector, arguments, member, resultTable,
+                null);
+          }
+          return ResultBuilder.buildResult(resultTable);
+        } else {
+          return ResultBuilder
+              .createUserErrorResult(CliStrings.EXECUTE_FUNCTION__MSG__DS_HAS_NO_MEMBERS);
+        }
+      } else if (onRegion != null && onRegion.length() > 0) {
+        if (cache.getRegion(onRegion) == null) {
+          // find a member where region is present
+          DistributedRegionMXBean bean = ManagementService.getManagementService(getCache())
+              .getDistributedRegionMXBean(onRegion);
+          if (bean == null) {
+            bean = ManagementService.getManagementService(getCache())
+                .getDistributedRegionMXBean(Region.SEPARATOR + onRegion);
+
+            if (bean == null) {
+              return ResultBuilder.createGemFireErrorResult(CliStrings
+                  .format(CliStrings.EXECUTE_FUNCTION__MSG__MXBEAN_0_FOR_NOT_FOUND, onRegion));
+            }
+          }
+
+          DistributedMember member = null;
+          String[] membersName = bean.getMembers();
+          Set<DistributedMember> dsMembers = CliUtil.getAllMembers(cache);
+          Iterator it = dsMembers.iterator();
+          boolean matchFound = false;
+
+          if (membersName.length > 0) {
+            while (it.hasNext() && !matchFound) {
+              DistributedMember dsmember = (DistributedMember) it.next();
+              for (String memberName : membersName) {
+                if (MBeanJMXAdapter.getMemberNameOrId(dsmember).equals(memberName)) {
+                  member = dsmember;
+                  matchFound = true;
+                  break;
+                }
+              }
+            }
+          }
+          if (matchFound) {
+            executeAndGetResults(functionId, filterString, resultCollector, arguments, member,
+                resultTable, onRegion);
+            return ResultBuilder.buildResult(resultTable);
+          } else {
+            return ResultBuilder.createGemFireErrorResult(CliStrings.format(
+                CliStrings.EXECUTE_FUNCTION__MSG__NO_ASSOCIATED_MEMBER_REGION, " " + onRegion));
+          }
+        } else {
+          execution = FunctionService.onRegion(cache.getRegion(onRegion));
+          if (execution != null) {
+            if (resultCollectorInstance != null) {
+              execution = execution.withCollector(resultCollectorInstance);
+            }
+            if (filters.size() > 0) {
+              execution = execution.withFilter(filters);
+            }
+            if (arguments != null && arguments.length > 0) {
+              execution = execution.setArguments(arguments);
+            }
+
+            try {
+              List<Object> results = (List<Object>) execution.execute(functionId).getResult();
+              if (results.size() > 0) {
+                StringBuilder strResult = new StringBuilder();
+                for (Object obj : results) {
+                  strResult.append(obj);
+                }
+                toTabularResultData(resultTable,
+                    cache.getDistributedSystem().getDistributedMember().getId(),
+                    strResult.toString());
+              }
+              return ResultBuilder.buildResult(resultTable);
+            } catch (FunctionException e) {
+              return ResultBuilder.createGemFireErrorResult(CliStrings.format(
+                  CliStrings.EXECUTE_FUNCTION__MSG__ERROR_IN_EXECUTING_0_ON_REGION_1_DETAILS_2,
+                  functionId, onRegion, e.getMessage()));
+            }
+          } else {
+            return ResultBuilder.createGemFireErrorResult(CliStrings.format(
+                CliStrings.EXECUTE_FUNCTION__MSG__ERROR_IN_EXECUTING_0_ON_REGION_1_DETAILS_2,
+                functionId, onRegion,
+                CliStrings.EXECUTE_FUNCTION__MSG__ERROR_IN_RETRIEVING_EXECUTOR));
+          }
+        }
+      } else if (onGroups != null) {
+        // execute on group members
+        Set<DistributedMember> dsMembers = new HashSet<>();
+        for (String grp : onGroups) {
+          dsMembers.addAll(cache.getDistributedSystem().getGroupMembers(grp));
+        }
+
+        if (dsMembers.size() > 0) {
+          for (DistributedMember member : dsMembers) {
+            executeAndGetResults(functionId, filterString, resultCollector, arguments, member,
+                resultTable, onRegion);
+          }
+          return ResultBuilder.buildResult(resultTable);
+        } else {
+          StringBuilder grps = new StringBuilder();
+          for (String grp : onGroups) {
+            grps.append(grp);
+            grps.append(", ");
+          }
+          return ResultBuilder.createUserErrorResult(
+              CliStrings.format(CliStrings.EXECUTE_FUNCTION__MSG__GROUPS_0_HAS_NO_MEMBERS,
+                  grps.toString().substring(0, grps.toString().length() - 1)));
+        }
+      } else if (onMember != null && onMember.length() > 0) {
+        DistributedMember member = CliUtil.getDistributedMemberByNameOrId(onMember); // fix for bug
+        // 45658
+        if (member != null) {
+          executeAndGetResults(functionId, filterString, resultCollector, arguments, member,
+              resultTable, onRegion);
+        } else {
+          toTabularResultData(resultTable, onMember, CliStrings
+              .format(CliStrings.EXECUTE_FUNCTION__MSG__NO_ASSOCIATED_MEMBER + " " + onMember));
+        }
+        return ResultBuilder.buildResult(resultTable);
+      }
+    } catch (Exception e) {
+      ErrorResultData errorResultData = ResultBuilder.createErrorResultData()
+          .setErrorCode(ResultBuilder.ERRORCODE_DEFAULT).addLine(e.getMessage());
+      return ResultBuilder.buildResult(errorResultData);
+    }
+    return null;
+  }
+
+  private boolean moreThanOneIsTrue(Boolean... values) {
+    return Stream.of(values).mapToInt(BooleanUtils::toInteger).sum() > 1;
+  }
+
+  private void executeAndGetResults(String functionId, String filterString, String resultCollector,
+      String[] arguments, DistributedMember member, TabularResultData resultTable,
+      String onRegion) {
+    StringBuilder resultMessage = new StringBuilder();
+    try {
+      Function function = new UserFunctionExecution();
+      Object[] args = new Object[5];
+      args[0] = functionId;
+      if (filterString != null) {
+        args[1] = filterString;
+      }
+      if (resultCollector != null) {
+        args[2] = resultCollector;
+      }
+      if (arguments != null && arguments.length > 0) {
+        args[3] = "";
+        for (String str : arguments) {
+          // send via CSV separated value format
+          if (str != null) {
+            args[3] = args[3] + str + ",";
+          }
+        }
+      }
+      args[4] = onRegion;
+
+      Execution execution = FunctionService.onMember(member).setArguments(args);
+      if (execution != null) {
+        List<Object> results = (List<Object>) execution.execute(function).getResult();
+        if (results != null) {
+          for (Object resultObj : results) {
+            if (resultObj != null) {
+              if (resultObj instanceof String) {
+                resultMessage.append(((String) resultObj));
+              } else if (resultObj instanceof Exception) {
+                resultMessage.append(((Exception) resultObj).getMessage());
+              } else {
+                resultMessage.append(resultObj);
+              }
+            }
+          }
+        }
+        toTabularResultData(resultTable, member.getId(), resultMessage.toString());
+      } else {
+        toTabularResultData(resultTable, member.getId(),
+            CliStrings.EXECUTE_FUNCTION__MSG__ERROR_IN_RETRIEVING_EXECUTOR);
+      }
+    } catch (Exception e) {
+      resultMessage.append(CliStrings.format(
+          CliStrings.EXECUTE_FUNCTION__MSG__COULD_NOT_EXECUTE_FUNCTION_0_ON_MEMBER_1_ERROR_2,
+          functionId, member.getId(), e.getMessage()));
+      toTabularResultData(resultTable, member.getId(), resultMessage.toString());
+    }
+  }
+
+  private void toTabularResultData(TabularResultData table, String memberId, String memberResult) {
+    table.accumulate("Member ID/Name", memberId);
+    table.accumulate("Function Execution Result", memberResult);
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/90f5440d/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/FunctionCommands.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/FunctionCommands.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/FunctionCommands.java
deleted file mode 100644
index b037d64..0000000
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/FunctionCommands.java
+++ /dev/null
@@ -1,537 +0,0 @@
-/*
- * 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.util.ArrayList;
-import java.util.Arrays;
-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.stream.Stream;
-
-import org.apache.commons.lang.BooleanUtils;
-import org.springframework.shell.core.annotation.CliCommand;
-import org.springframework.shell.core.annotation.CliOption;
-
-import org.apache.geode.SystemFailure;
-import org.apache.geode.cache.Region;
-import org.apache.geode.cache.execute.Execution;
-import org.apache.geode.cache.execute.Function;
-import org.apache.geode.cache.execute.FunctionException;
-import org.apache.geode.cache.execute.FunctionService;
-import org.apache.geode.cache.execute.ResultCollector;
-import org.apache.geode.distributed.DistributedMember;
-import org.apache.geode.internal.ClassPathLoader;
-import org.apache.geode.internal.cache.InternalCache;
-import org.apache.geode.management.DistributedRegionMXBean;
-import org.apache.geode.management.ManagementService;
-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.cli.Result.Status;
-import org.apache.geode.management.internal.MBeanJMXAdapter;
-import org.apache.geode.management.internal.cli.AbstractCliAroundInterceptor;
-import org.apache.geode.management.internal.cli.CliUtil;
-import org.apache.geode.management.internal.cli.GfshParseResult;
-import org.apache.geode.management.internal.cli.LogWrapper;
-import org.apache.geode.management.internal.cli.functions.CliFunctionResult;
-import org.apache.geode.management.internal.cli.functions.ListFunctionFunction;
-import org.apache.geode.management.internal.cli.functions.UnregisterFunction;
-import org.apache.geode.management.internal.cli.functions.UserFunctionExecution;
-import org.apache.geode.management.internal.cli.i18n.CliStrings;
-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.security.ResourceOperation;
-import org.apache.geode.security.ResourcePermission.Operation;
-import org.apache.geode.security.ResourcePermission.Resource;
-import org.apache.geode.security.ResourcePermission.Target;
-
-/**
- * @since GemFire 7.0
- */
-@SuppressWarnings("unused")
-public class FunctionCommands implements GfshCommand {
-
-  private final ListFunctionFunction listFunctionFunction = new ListFunctionFunction();
-
-  @CliCommand(value = CliStrings.EXECUTE_FUNCTION, help = CliStrings.EXECUTE_FUNCTION__HELP)
-  @CliMetaData(relatedTopic = {CliStrings.TOPIC_GEODE_FUNCTION})
-  @ResourceOperation(resource = Resource.DATA, operation = Operation.WRITE)
-  public Result executeFunction(
-      // TODO: Add optioncontext for functionID
-      @CliOption(key = CliStrings.EXECUTE_FUNCTION__ID, mandatory = true,
-          help = CliStrings.EXECUTE_FUNCTION__ID__HELP) String functionId,
-      @CliOption(key = {CliStrings.GROUP, CliStrings.GROUPS},
-          optionContext = ConverterHint.MEMBERGROUP,
-          help = CliStrings.EXECUTE_FUNCTION__ONGROUPS__HELP) String[] onGroups,
-      @CliOption(key = CliStrings.MEMBER, optionContext = ConverterHint.MEMBERIDNAME,
-          help = CliStrings.EXECUTE_FUNCTION__ONMEMBER__HELP) String onMember,
-      @CliOption(key = CliStrings.EXECUTE_FUNCTION__ONREGION,
-          optionContext = ConverterHint.REGION_PATH,
-          help = CliStrings.EXECUTE_FUNCTION__ONREGION__HELP) String onRegion,
-      @CliOption(key = CliStrings.EXECUTE_FUNCTION__ARGUMENTS,
-          help = CliStrings.EXECUTE_FUNCTION__ARGUMENTS__HELP) String[] arguments,
-      @CliOption(key = CliStrings.EXECUTE_FUNCTION__RESULTCOLLECTOR,
-          help = CliStrings.EXECUTE_FUNCTION__RESULTCOLLECTOR__HELP) String resultCollector,
-      @CliOption(key = CliStrings.EXECUTE_FUNCTION__FILTER,
-          help = CliStrings.EXECUTE_FUNCTION__FILTER__HELP) String filterString) {
-
-    Result result = null;
-    CompositeResultData executeFunctionResultTable = ResultBuilder.createCompositeResultData();
-    TabularResultData resultTable = executeFunctionResultTable.addSection().addTable("Table1");
-    String headerText = "Execution summary";
-    resultTable.setHeader(headerText);
-    ResultCollector resultCollectorInstance = null;
-    Function function;
-    Set<String> filters = new HashSet<>();
-    Execution execution;
-    if (functionId != null) {
-      functionId = functionId.trim();
-    }
-    if (onRegion != null) {
-      onRegion = onRegion.trim();
-    }
-    if (onMember != null) {
-      onMember = onMember.trim();
-    }
-    if (filterString != null) {
-      filterString = filterString.trim();
-    }
-
-    try {
-      // validate otherwise return right away. no need to process anything
-      if (functionId == null || functionId.length() == 0) {
-        ErrorResultData errorResultData =
-            ResultBuilder.createErrorResultData().setErrorCode(ResultBuilder.ERRORCODE_DEFAULT)
-                .addLine(CliStrings.EXECUTE_FUNCTION__MSG__MISSING_FUNCTIONID);
-        result = ResultBuilder.buildResult(errorResultData);
-        return result;
-      }
-
-      if (isMoreThanOneIsTrue(onRegion != null, onMember != null, onGroups != null)) {
-        // Provide Only one of region/member/groups
-        ErrorResultData errorResultData =
-            ResultBuilder.createErrorResultData().setErrorCode(ResultBuilder.ERRORCODE_DEFAULT)
-                .addLine(CliStrings.EXECUTE_FUNCTION__MSG__OPTIONS);
-        result = ResultBuilder.buildResult(errorResultData);
-        return result;
-      } else if ((onRegion == null || onRegion.length() == 0) && (filterString != null)) {
-        ErrorResultData errorResultData = ResultBuilder.createErrorResultData()
-            .setErrorCode(ResultBuilder.ERRORCODE_DEFAULT)
-            .addLine(CliStrings.EXECUTE_FUNCTION__MSG__MEMBER_SHOULD_NOT_HAVE_FILTER_FOR_EXECUTION);
-        result = ResultBuilder.buildResult(errorResultData);
-        return result;
-      }
-
-      InternalCache cache = getCache();
-
-      if (resultCollector != null) {
-        resultCollectorInstance =
-            (ResultCollector) ClassPathLoader.getLatest().forName(resultCollector).newInstance();
-      }
-
-      if (filterString != null && filterString.length() > 0) {
-        filters.add(filterString);
-      }
-
-      if (onRegion == null && onMember == null && onGroups == null) {
-        // run function on all the members excluding locators bug#46113
-        // if user wish to execute on locator then he can choose --member or --group option
-        Set<DistributedMember> dsMembers = CliUtil.getAllNormalMembers(cache);
-        if (dsMembers.size() > 0) {
-          function = new UserFunctionExecution();
-          LogWrapper.getInstance().info(CliStrings
-              .format(CliStrings.EXECUTE_FUNCTION__MSG__EXECUTING_0_ON_ENTIRE_DS, functionId));
-          for (DistributedMember member : dsMembers) {
-            executeAndGetResults(functionId, filterString, resultCollector, arguments, cache,
-                member, resultTable, onRegion);
-          }
-          return ResultBuilder.buildResult(resultTable);
-        } else {
-          return ResultBuilder
-              .createUserErrorResult(CliStrings.EXECUTE_FUNCTION__MSG__DS_HAS_NO_MEMBERS);
-        }
-      } else if (onRegion != null && onRegion.length() > 0) {
-        if (cache.getRegion(onRegion) == null) {
-          // find a member where region is present
-          DistributedRegionMXBean bean = ManagementService.getManagementService(getCache())
-              .getDistributedRegionMXBean(onRegion);
-          if (bean == null) {
-            bean = ManagementService.getManagementService(getCache())
-                .getDistributedRegionMXBean(Region.SEPARATOR + onRegion);
-
-            if (bean == null) {
-              return ResultBuilder.createGemFireErrorResult(CliStrings
-                  .format(CliStrings.EXECUTE_FUNCTION__MSG__MXBEAN_0_FOR_NOT_FOUND, onRegion));
-            }
-          }
-
-          DistributedMember member = null;
-          String[] membersName = bean.getMembers();
-          Set<DistributedMember> dsMembers = CliUtil.getAllMembers(cache);
-          Iterator it = dsMembers.iterator();
-          boolean matchFound = false;
-
-          if (membersName.length > 0) {
-            while (it.hasNext() && !matchFound) {
-              DistributedMember dsmember = (DistributedMember) it.next();
-              for (String memberName : membersName) {
-                if (MBeanJMXAdapter.getMemberNameOrId(dsmember).equals(memberName)) {
-                  member = dsmember;
-                  matchFound = true;
-                  break;
-                }
-              }
-            }
-          }
-          if (matchFound) {
-            executeAndGetResults(functionId, filterString, resultCollector, arguments, cache,
-                member, resultTable, onRegion);
-            return ResultBuilder.buildResult(resultTable);
-          } else {
-            return ResultBuilder.createGemFireErrorResult(CliStrings.format(
-                CliStrings.EXECUTE_FUNCTION__MSG__NO_ASSOCIATED_MEMBER_REGION, " " + onRegion));
-          }
-        } else {
-          execution = FunctionService.onRegion(cache.getRegion(onRegion));
-          if (execution != null) {
-            if (resultCollectorInstance != null) {
-              execution = execution.withCollector(resultCollectorInstance);
-            }
-            if (filters != null && filters.size() > 0) {
-              execution = execution.withFilter(filters);
-            }
-            if (arguments != null && arguments.length > 0) {
-              execution = execution.setArguments(arguments);
-            }
-
-            try {
-              List<Object> results = (List<Object>) execution.execute(functionId).getResult();
-              if (results.size() > 0) {
-                StringBuilder strResult = new StringBuilder();
-                for (Object obj : results) {
-                  strResult.append(obj);
-                }
-                toTabularResultData(resultTable,
-                    cache.getDistributedSystem().getDistributedMember().getId(),
-                    strResult.toString());
-              }
-              return ResultBuilder.buildResult(resultTable);
-            } catch (FunctionException e) {
-              return ResultBuilder.createGemFireErrorResult(CliStrings.format(
-                  CliStrings.EXECUTE_FUNCTION__MSG__ERROR_IN_EXECUTING_0_ON_REGION_1_DETAILS_2,
-                  functionId, onRegion, e.getMessage()));
-            }
-          } else {
-            return ResultBuilder.createGemFireErrorResult(CliStrings.format(
-                CliStrings.EXECUTE_FUNCTION__MSG__ERROR_IN_EXECUTING_0_ON_REGION_1_DETAILS_2,
-                functionId, onRegion,
-                CliStrings.EXECUTE_FUNCTION__MSG__ERROR_IN_RETRIEVING_EXECUTOR));
-          }
-        }
-      } else if (onGroups != null) {
-        // execute on group members
-        Set<DistributedMember> dsMembers = new HashSet<>();
-        for (String grp : onGroups) {
-          dsMembers.addAll(cache.getDistributedSystem().getGroupMembers(grp));
-        }
-
-        StringBuilder successMessage = new StringBuilder();
-        if (dsMembers.size() > 0) {
-          for (DistributedMember member : dsMembers) {
-            executeAndGetResults(functionId, filterString, resultCollector, arguments, cache,
-                member, resultTable, onRegion);
-          }
-          return ResultBuilder.buildResult(resultTable);
-        } else {
-          StringBuilder grps = new StringBuilder();
-          for (String grp : onGroups) {
-            grps.append(grp);
-            grps.append(", ");
-          }
-          return ResultBuilder.createUserErrorResult(
-              CliStrings.format(CliStrings.EXECUTE_FUNCTION__MSG__GROUPS_0_HAS_NO_MEMBERS,
-                  grps.toString().substring(0, grps.toString().length() - 1)));
-        }
-      } else if (onMember != null && onMember.length() > 0) {
-        DistributedMember member = CliUtil.getDistributedMemberByNameOrId(onMember); // fix for bug
-        // 45658
-        if (member != null) {
-          executeAndGetResults(functionId, filterString, resultCollector, arguments, cache, member,
-              resultTable, onRegion);
-        } else {
-          toTabularResultData(resultTable, onMember, CliStrings
-              .format(CliStrings.EXECUTE_FUNCTION__MSG__NO_ASSOCIATED_MEMBER + " " + onMember));
-        }
-        return ResultBuilder.buildResult(resultTable);
-      }
-    } catch (Exception e) {
-      ErrorResultData errorResultData = ResultBuilder.createErrorResultData()
-          .setErrorCode(ResultBuilder.ERRORCODE_DEFAULT).addLine(e.getMessage());
-      result = ResultBuilder.buildResult(errorResultData);
-      return result;
-    }
-
-    return result;
-  }
-
-  private boolean isMoreThanOneIsTrue(Boolean... values) {
-    return Stream.of(values).mapToInt(BooleanUtils::toInteger).sum() > 1;
-  }
-
-  void executeAndGetResults(String functionId, String filterString, String resultCollector,
-      String[] arguments, InternalCache cache, DistributedMember member,
-      TabularResultData resultTable, String onRegion) {
-    StringBuilder resultMessage = new StringBuilder();
-    try {
-      Function function = new UserFunctionExecution();
-      Object[] args = new Object[5];
-      args[0] = functionId;
-      if (filterString != null) {
-        args[1] = filterString;
-      }
-      if (resultCollector != null) {
-        args[2] = resultCollector;
-      }
-      if (arguments != null && arguments.length > 0) {
-        args[3] = "";
-        for (String str : arguments) {
-          // send via CSV separated value format
-          if (str != null) {
-            args[3] = args[3] + str + ",";
-          }
-        }
-      }
-      args[4] = onRegion;
-
-      Execution execution = FunctionService.onMember(member).setArguments(args);
-      if (execution != null) {
-        List<Object> results = (List<Object>) execution.execute(function).getResult();
-        if (results != null) {
-          for (Object resultObj : results) {
-            if (resultObj != null) {
-              if (resultObj instanceof String) {
-                resultMessage.append(((String) resultObj));
-              } else if (resultObj instanceof Exception) {
-                resultMessage.append(((Exception) resultObj).getMessage());
-              } else {
-                resultMessage.append(resultObj);
-              }
-            }
-          }
-        }
-        toTabularResultData(resultTable, member.getId(), resultMessage.toString());
-      } else {
-        toTabularResultData(resultTable, member.getId(),
-            CliStrings.EXECUTE_FUNCTION__MSG__ERROR_IN_RETRIEVING_EXECUTOR);
-      }
-    } catch (Exception e) {
-      resultMessage.append(CliStrings.format(
-          CliStrings.EXECUTE_FUNCTION__MSG__COULD_NOT_EXECUTE_FUNCTION_0_ON_MEMBER_1_ERROR_2,
-          functionId, member.getId(), e.getMessage()));
-      toTabularResultData(resultTable, member.getId(), resultMessage.toString());
-    }
-  }
-
-  protected void toTabularResultData(TabularResultData table, String memberId,
-      String memberResult) {
-    String newLine = System.getProperty("line.separator");
-    table.accumulate("Member ID/Name", memberId);
-    table.accumulate("Function Execution Result", memberResult);
-  }
-
-  @CliCommand(value = CliStrings.DESTROY_FUNCTION, help = CliStrings.DESTROY_FUNCTION__HELP)
-  @CliMetaData(relatedTopic = {CliStrings.TOPIC_GEODE_FUNCTION},
-      interceptor = "org.apache.geode.management.internal.cli.commands.FunctionCommands$Interceptor")
-  @ResourceOperation(resource = Resource.CLUSTER, operation = Operation.MANAGE, target = Target.JAR)
-  // TODO: Add optioncontext for functionId
-  public Result destroyFunction(
-      @CliOption(key = CliStrings.DESTROY_FUNCTION__ID, mandatory = true,
-          help = CliStrings.DESTROY_FUNCTION__ID__HELP) String functionId,
-      @CliOption(key = {CliStrings.GROUP, CliStrings.GROUPS},
-          optionContext = ConverterHint.MEMBERGROUP,
-          help = CliStrings.DESTROY_FUNCTION__ONGROUPS__HELP) String[] groups,
-      @CliOption(key = CliStrings.MEMBER, optionContext = ConverterHint.MEMBERIDNAME,
-          help = CliStrings.DESTROY_FUNCTION__ONMEMBER__HELP) String memberId) {
-    Result result;
-    try {
-      InternalCache cache = getCache();
-      Set<DistributedMember> dsMembers = new HashSet<>();
-      if (groups != null && memberId != null) {
-        return ResultBuilder
-            .createUserErrorResult(CliStrings.DESTROY_FUNCTION__MSG__PROVIDE_OPTION);
-      } else if (groups != null && groups.length > 0) {
-        // execute on group members
-        for (String grp : groups) {
-          dsMembers.addAll(cache.getDistributedSystem().getGroupMembers(grp));
-        }
-        @SuppressWarnings("unchecked")
-        Result results = executeFunction(cache, dsMembers, functionId);
-        return results;
-      } else if (memberId != null) {
-        // execute on member
-        dsMembers.add(getMember(cache, memberId));
-        @SuppressWarnings("unchecked")
-        Result results = executeFunction(cache, dsMembers, functionId);
-        return results;
-      } else {
-        // no option provided.
-        @SuppressWarnings("unchecked")
-        Result results = executeFunction(cache, cache.getMembers(), functionId);
-        return results;
-      }
-    } catch (Exception e) {
-      ErrorResultData errorResultData = ResultBuilder.createErrorResultData()
-          .setErrorCode(ResultBuilder.ERRORCODE_DEFAULT).addLine(e.getMessage());
-      result = ResultBuilder.buildResult(errorResultData);
-      return result;
-    }
-  }
-
-  /**
-   * Interceptor used by gfsh to intercept execution of destroy.
-   */
-  public static class Interceptor extends AbstractCliAroundInterceptor {
-    @Override
-    public Result preExecution(GfshParseResult parseResult) {
-      Map<String, String> paramValueMap = parseResult.getParamValueStrings();
-      Set<Entry<String, String>> setEnvMap = paramValueMap.entrySet();
-      String onGroup = paramValueMap.get(CliStrings.GROUP);
-      String onMember = paramValueMap.get(CliStrings.MEMBER);
-
-      if ((onGroup == null && onMember == null)) {
-        Response response = readYesNo("Do you really want to destroy "
-            + paramValueMap.get(CliStrings.DESTROY_FUNCTION__ID) + " on entire DS?", Response.NO);
-        if (response == Response.NO) {
-          return ResultBuilder.createShellClientAbortOperationResult(
-              "Aborted destroy of " + paramValueMap.get(CliStrings.DESTROY_FUNCTION__ID));
-        } else {
-          return ResultBuilder
-              .createInfoResult("Destroying " + paramValueMap.get(CliStrings.DESTROY_FUNCTION__ID));
-        }
-      } else {
-        return ResultBuilder
-            .createInfoResult("Destroying " + paramValueMap.get(CliStrings.DESTROY_FUNCTION__ID));
-      }
-    }
-  }
-
-  Result executeFunction(InternalCache cache, Set<DistributedMember> DsMembers, String functionId) {
-    // unregister on a set of of members
-    Function unregisterFunction = new UnregisterFunction();
-    FunctionService.registerFunction(unregisterFunction);
-    List resultList;
-
-    if (DsMembers.isEmpty()) {
-      return ResultBuilder.createInfoResult("No members for execution");
-    }
-    Object[] obj = new Object[1];
-    obj[0] = functionId;
-
-    Execution execution = FunctionService.onMembers(DsMembers).setArguments(obj);
-
-    if (execution == null) {
-      cache.getLogger().error("executeUnregister execution is null");
-      ErrorResultData errorResultData =
-          ResultBuilder.createErrorResultData().setErrorCode(ResultBuilder.ERRORCODE_DEFAULT)
-              .addLine(CliStrings.DESTROY_FUNCTION__MSG__CANNOT_EXECUTE);
-      return (ResultBuilder.buildResult(errorResultData));
-    }
-    try {
-      resultList = (ArrayList) execution.execute(unregisterFunction).getResult();
-    } catch (FunctionException ex) {
-      ErrorResultData errorResultData = ResultBuilder.createErrorResultData()
-          .setErrorCode(ResultBuilder.ERRORCODE_DEFAULT).addLine(ex.getMessage());
-      return (ResultBuilder.buildResult(errorResultData));
-    }
-    String resultStr = ((String) resultList.get(0));
-    if (resultStr.equals("Succeeded in unregistering")) {
-      StringBuilder members = new StringBuilder();
-      for (DistributedMember member : DsMembers) {
-        members.append(member.getId());
-        members.append(",");
-      }
-      return ResultBuilder.createInfoResult("Destroyed " + functionId + " Successfully on "
-          + members.toString().substring(0, members.toString().length() - 1));
-    } else {
-      return ResultBuilder.createInfoResult("Failed in unregistering");
-    }
-  }
-
-  @CliCommand(value = CliStrings.LIST_FUNCTION, help = CliStrings.LIST_FUNCTION__HELP)
-  @CliMetaData(relatedTopic = {CliStrings.TOPIC_GEODE_FUNCTION})
-  @ResourceOperation(resource = Resource.CLUSTER, operation = Operation.READ)
-  public Result listFunction(
-      @CliOption(key = CliStrings.LIST_FUNCTION__MATCHES,
-          help = CliStrings.LIST_FUNCTION__MATCHES__HELP) String matches,
-      @CliOption(key = {CliStrings.GROUP, CliStrings.GROUPS},
-          optionContext = ConverterHint.MEMBERGROUP,
-          help = CliStrings.LIST_FUNCTION__GROUP__HELP) String[] groups,
-      @CliOption(key = {CliStrings.MEMBER, CliStrings.MEMBERS},
-          optionContext = ConverterHint.MEMBERIDNAME,
-          help = CliStrings.LIST_FUNCTION__MEMBER__HELP) String[] members) {
-    TabularResultData tabularData = ResultBuilder.createTabularResultData();
-    boolean accumulatedData = false;
-
-    InternalCache cache = getCache();
-
-    Set<DistributedMember> targetMembers = CliUtil.findMembers(groups, members);
-
-    if (targetMembers.isEmpty()) {
-      return ResultBuilder.createUserErrorResult(CliStrings.NO_MEMBERS_FOUND_MESSAGE);
-    }
-
-    try {
-      ResultCollector<?, ?> rc =
-          CliUtil.executeFunction(this.listFunctionFunction, new Object[] {matches}, targetMembers);
-      List<CliFunctionResult> results = CliFunctionResult.cleanResults((List<?>) rc.getResult());
-
-      for (CliFunctionResult result : results) {
-        if (result.getThrowable() != null) {
-          tabularData.accumulate("Member", result.getMemberIdOrName());
-          tabularData.accumulate("Function", "<ERROR: " + result.getThrowable().getMessage() + ">");
-          accumulatedData = true;
-          tabularData.setStatus(Status.ERROR);
-        } else if (result.isSuccessful()) {
-          String[] strings = (String[]) result.getSerializables();
-          Arrays.sort(strings);
-          for (String string : strings) {
-            tabularData.accumulate("Member", result.getMemberIdOrName());
-            tabularData.accumulate("Function", string);
-            accumulatedData = true;
-          }
-        }
-      }
-
-      if (!accumulatedData) {
-        return ResultBuilder
-            .createInfoResult(CliStrings.LIST_FUNCTION__NO_FUNCTIONS_FOUND_ERROR_MESSAGE);
-      }
-      return ResultBuilder.buildResult(tabularData);
-    } catch (VirtualMachineError e) {
-      SystemFailure.initiateFailure(e);
-      throw e;
-    } catch (Throwable th) {
-      SystemFailure.checkFailure();
-      return ResultBuilder.createGemFireErrorResult(
-          "Exception while attempting to list functions: " + th.getMessage());
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/geode/blob/90f5440d/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/ListFunctionCommand.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/ListFunctionCommand.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/ListFunctionCommand.java
new file mode 100644
index 0000000..81ef437
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/ListFunctionCommand.java
@@ -0,0 +1,100 @@
+/*
+ * 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.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+import org.springframework.shell.core.annotation.CliCommand;
+import org.springframework.shell.core.annotation.CliOption;
+
+import org.apache.geode.SystemFailure;
+import org.apache.geode.cache.execute.ResultCollector;
+import org.apache.geode.distributed.DistributedMember;
+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.CliUtil;
+import org.apache.geode.management.internal.cli.functions.CliFunctionResult;
+import org.apache.geode.management.internal.cli.functions.ListFunctionFunction;
+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.result.TabularResultData;
+import org.apache.geode.management.internal.security.ResourceOperation;
+import org.apache.geode.security.ResourcePermission;
+
+public class ListFunctionCommand implements GfshCommand {
+  private final ListFunctionFunction listFunctionFunction = new ListFunctionFunction();
+
+  @CliCommand(value = CliStrings.LIST_FUNCTION, help = CliStrings.LIST_FUNCTION__HELP)
+  @CliMetaData(relatedTopic = {CliStrings.TOPIC_GEODE_FUNCTION})
+  @ResourceOperation(resource = ResourcePermission.Resource.CLUSTER,
+      operation = ResourcePermission.Operation.READ)
+  public Result listFunction(
+      @CliOption(key = CliStrings.LIST_FUNCTION__MATCHES,
+          help = CliStrings.LIST_FUNCTION__MATCHES__HELP) String matches,
+      @CliOption(key = {CliStrings.GROUP, CliStrings.GROUPS},
+          optionContext = ConverterHint.MEMBERGROUP,
+          help = CliStrings.LIST_FUNCTION__GROUP__HELP) String[] groups,
+      @CliOption(key = {CliStrings.MEMBER, CliStrings.MEMBERS},
+          optionContext = ConverterHint.MEMBERIDNAME,
+          help = CliStrings.LIST_FUNCTION__MEMBER__HELP) String[] members) {
+    TabularResultData tabularData = ResultBuilder.createTabularResultData();
+    boolean accumulatedData = false;
+    Set<DistributedMember> targetMembers = CliUtil.findMembers(groups, members);
+
+    if (targetMembers.isEmpty()) {
+      return ResultBuilder.createUserErrorResult(CliStrings.NO_MEMBERS_FOUND_MESSAGE);
+    }
+
+    try {
+      ResultCollector<?, ?> rc =
+          CliUtil.executeFunction(this.listFunctionFunction, new Object[] {matches}, targetMembers);
+      List<CliFunctionResult> results = CliFunctionResult.cleanResults((List<?>) rc.getResult());
+
+      for (CliFunctionResult result : results) {
+        if (result.getThrowable() != null) {
+          tabularData.accumulate("Member", result.getMemberIdOrName());
+          tabularData.accumulate("Function", "<ERROR: " + result.getThrowable().getMessage() + ">");
+          accumulatedData = true;
+          tabularData.setStatus(Result.Status.ERROR);
+        } else if (result.isSuccessful()) {
+          String[] strings = (String[]) result.getSerializables();
+          Arrays.sort(strings);
+          for (String string : strings) {
+            tabularData.accumulate("Member", result.getMemberIdOrName());
+            tabularData.accumulate("Function", string);
+            accumulatedData = true;
+          }
+        }
+      }
+
+      if (!accumulatedData) {
+        return ResultBuilder
+            .createInfoResult(CliStrings.LIST_FUNCTION__NO_FUNCTIONS_FOUND_ERROR_MESSAGE);
+      }
+      return ResultBuilder.buildResult(tabularData);
+    } catch (VirtualMachineError e) {
+      SystemFailure.initiateFailure(e);
+      throw e;
+    } catch (Throwable th) {
+      SystemFailure.checkFailure();
+      return ResultBuilder.createGemFireErrorResult(
+          "Exception while attempting to list functions: " + th.getMessage());
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/90f5440d/geode-core/src/main/java/org/apache/geode/management/internal/web/controllers/FunctionCommandsController.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/web/controllers/FunctionCommandsController.java b/geode-core/src/main/java/org/apache/geode/management/internal/web/controllers/FunctionCommandsController.java
index 855947e..da3fc65 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/web/controllers/FunctionCommandsController.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/web/controllers/FunctionCommandsController.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;
@@ -25,14 +24,18 @@ import org.springframework.web.bind.annotation.RequestMethod;
 import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.ResponseBody;
 
-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 FunctionCommandsController class implements GemFire Management REST API web service endpoints
  * for the Gfsh Function Commands.
  * <p/>
  * 
- * @see org.apache.geode.management.internal.cli.commands.FunctionCommands
+ * @see org.apache.geode.management.internal.cli.commands.DestroyFunctionCommand
+ * @see org.apache.geode.management.internal.cli.commands.ExecuteFunctionCommand
+ * @see org.apache.geode.management.internal.cli.commands.ListFunctionCommand
  * @see org.apache.geode.management.internal.web.controllers.AbstractCommandsController
  * @see org.springframework.stereotype.Controller
  * @see org.springframework.web.bind.annotation.PathVariable

http://git-wip-us.apache.org/repos/asf/geode/blob/90f5440d/geode-core/src/test/java/org/apache/geode/management/internal/security/TestCommand.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/security/TestCommand.java b/geode-core/src/test/java/org/apache/geode/management/internal/security/TestCommand.java
index 1a9c683..af7c9de 100644
--- a/geode-core/src/test/java/org/apache/geode/management/internal/security/TestCommand.java
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/security/TestCommand.java
@@ -183,8 +183,8 @@ public class TestCommand {
         clusterRead);
     createTestCommand("import cluster-configuration --zip-file-name=value.zip", clusterManage);
 
-    // FunctionCommands
-    // createTestCommand("destroy function --id=InterestCalculations", dataManage);
+    // DestroyFunctionCommand, ExecuteFunctionCommand, ListFunctionCommand
+    createTestCommand("destroy function --id=InterestCalculations", dataManage);
     createTestCommand("execute function --id=InterestCalculations --groups=Group1", dataWrite);
     createTestCommand("list functions", clusterRead);