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/10/04 21:13:37 UTC
[geode] branch develop updated: GEODE-3685: MBean wrappers are
properly invoked over http
This is an automated email from the ASF dual-hosted git repository.
jstewart pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/geode.git
The following commit(s) were added to refs/heads/develop by this push:
new db4a493 GEODE-3685: MBean wrappers are properly invoked over http
db4a493 is described below
commit db4a493efc09600bf0a9778d5274c09b23b16644
Author: Jared Stewart <js...@pivotal.io>
AuthorDate: Thu Sep 28 14:44:51 2017 -0700
GEODE-3685: MBean wrappers are properly invoked over http
This closes #838.
---
.../cli/commands/StopServerAcceptanceTest.java | 66 ++++
.../StopServerWithSecurityAcceptanceTest.java | 139 ++++++++
.../geode/examples/SimpleSecurityManager.java | 59 ++++
.../geode/management/internal/ManagementAgent.java | 36 +-
.../controllers/AbstractCommandsController.java | 378 +++------------------
.../AbstractMultiPartCommandsController.java | 77 -----
.../web/controllers/ShellCommandsController.java | 154 ++++-----
.../web/http/support/SimpleHttpRequester.java | 53 +--
.../internal/web/shell/HttpOperationInvoker.java | 122 ++-----
.../web/shell/RestOperationErrorHandler.java | 58 ++++
.../geode/test/junit/rules/gfsh/GfshExecution.java | 8 +
.../geode/test/junit/rules/gfsh/GfshRule.java | 3 +-
.../shell/HttpOperationInvokerSecurityTest.java | 11 +-
13 files changed, 486 insertions(+), 678 deletions(-)
diff --git a/geode-assembly/src/test/java/org/apache/geode/management/internal/cli/commands/StopServerAcceptanceTest.java b/geode-assembly/src/test/java/org/apache/geode/management/internal/cli/commands/StopServerAcceptanceTest.java
new file mode 100644
index 0000000..247052a
--- /dev/null
+++ b/geode-assembly/src/test/java/org/apache/geode/management/internal/cli/commands/StopServerAcceptanceTest.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.management.internal.cli.commands;
+
+import static org.apache.geode.distributed.ConfigurationProperties.SECURITY_MANAGER;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.util.Properties;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import org.apache.geode.examples.SimpleSecurityManager;
+import org.apache.geode.management.internal.cli.util.CommandStringBuilder;
+import org.apache.geode.test.junit.categories.AcceptanceTest;
+import org.apache.geode.test.junit.rules.gfsh.GfshExecution;
+import org.apache.geode.test.junit.rules.gfsh.GfshRule;
+import org.apache.geode.test.junit.rules.gfsh.GfshScript;
+
+@Category(AcceptanceTest.class)
+public class StopServerAcceptanceTest {
+
+ @Rule
+ public GfshRule gfshRule = new GfshRule();
+
+
+ @Before
+ public void startCluster() {
+ gfshRule.execute("start locator --name=locator", "start server --name=server");
+ }
+
+ @Test
+ public void canStopServerByNameWhenConnectedOverJmx() throws Exception {
+
+ gfshRule.execute("connect", "stop server --name=server");
+ }
+
+ @Test
+ public void canStopServerByNameWhenConnectedOverHttp() throws Exception {
+
+ gfshRule.execute("connect --use-http", "stop server --name=server");
+ }
+
+ @Test
+ public void cannotStopServerByNameWhenNotConnected() throws Exception {
+ startCluster();
+
+ gfshRule.execute(GfshScript.of("stop server --name=server").expectFailure());
+ }
+}
diff --git a/geode-assembly/src/test/java/org/apache/geode/management/internal/cli/commands/StopServerWithSecurityAcceptanceTest.java b/geode-assembly/src/test/java/org/apache/geode/management/internal/cli/commands/StopServerWithSecurityAcceptanceTest.java
new file mode 100644
index 0000000..4588ee6
--- /dev/null
+++ b/geode-assembly/src/test/java/org/apache/geode/management/internal/cli/commands/StopServerWithSecurityAcceptanceTest.java
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.management.internal.cli.commands;
+
+import static org.apache.geode.distributed.ConfigurationProperties.SECURITY_MANAGER;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.util.Properties;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import org.apache.geode.examples.SimpleSecurityManager;
+import org.apache.geode.management.internal.cli.util.CommandStringBuilder;
+import org.apache.geode.test.junit.categories.AcceptanceTest;
+import org.apache.geode.test.junit.rules.gfsh.GfshExecution;
+import org.apache.geode.test.junit.rules.gfsh.GfshRule;
+import org.apache.geode.test.junit.rules.gfsh.GfshScript;
+
+@Category(AcceptanceTest.class)
+public class StopServerWithSecurityAcceptanceTest {
+
+ @Rule
+ public GfshRule gfshRule = new GfshRule();
+
+ private static final Properties securityProps = new Properties();
+
+ static {
+ securityProps.setProperty(SECURITY_MANAGER, SimpleSecurityManager.class.getName());
+ securityProps.setProperty("security-username", "cluster");
+ securityProps.setProperty("security-password", "cluster");
+ }
+
+ private File securityPropertiesFile;
+
+ @Before
+ public void before() throws Exception {
+ securityPropertiesFile = gfshRule.getTemporaryFolder().newFile("security.properties");
+ securityProps.store(new FileOutputStream(securityPropertiesFile), null);
+ }
+
+ @Test
+ public void cannotStopServerAsDataReaderOverHttp() throws Exception {
+ startCluster();
+
+ GfshExecution stopServer = dataReaderCannotStopServer(true);
+ assertThat(stopServer.getStdErrText()).contains("dataReader not authorized for CLUSTER:READ");
+ }
+
+ @Test
+ public void canStopServerAsClusterAdminOverHttp() throws Exception {
+ startCluster();
+
+ clusterAdminCanStopServer(true);
+ }
+
+ @Test
+ public void cannotStopServerAsDataReaderOverJmx() throws Exception {
+ startCluster();
+
+ GfshExecution stopServer = dataReaderCannotStopServer(false);
+ assertThat(stopServer.getStdErrText()).contains("dataReader not authorized for CLUSTER:READ");
+ }
+
+ @Test
+ public void canStopServerAsClusterAdminOverJmx() throws Exception {
+ startCluster();
+
+ clusterAdminCanStopServer(false);
+ }
+
+ @Test
+ public void cannotStopServerAsClusterReaderOverJmx() throws Exception {
+ startCluster();
+
+ GfshExecution stopServer = clusterReaderCannotStopServer(false);
+ assertThat(stopServer.getStdErrText())
+ .contains("clusterRead not authorized for CLUSTER:MANAGE");
+ }
+
+ @Test
+ public void cannotStopServerAsClusterReaderOverHttp() throws Exception {
+ startCluster();
+
+ GfshExecution stopServer = clusterReaderCannotStopServer(true);
+ assertThat(stopServer.getStdErrText())
+ .contains("clusterRead not authorized for CLUSTER:MANAGE");
+ }
+
+ private GfshExecution startCluster() {
+ String startLocator = new CommandStringBuilder("start locator").addOption("name", "locator")
+ .addOption("security-properties-file", securityPropertiesFile.getAbsolutePath()).toString();
+
+ String startServer = new CommandStringBuilder("start server").addOption("name", "server")
+ .addOption("security-properties-file", securityPropertiesFile.getAbsolutePath()).toString();
+
+ return GfshScript.of(startLocator, startServer).withName("cluster-setup").execute(gfshRule);
+ }
+
+ private GfshExecution dataReaderCannotStopServer(boolean useHttp) {
+ return GfshScript.of(connectCommand("dataReader", useHttp), "stop server --name=server")
+ .expectFailure().execute(gfshRule);
+ }
+
+ private GfshExecution clusterAdminCanStopServer(boolean useHttp) {
+ return GfshScript.of(connectCommand("cluster", useHttp), "stop server --name=server")
+ .execute(gfshRule);
+ }
+
+ private GfshExecution clusterReaderCannotStopServer(boolean useHttp) {
+ return GfshScript.of(connectCommand("clusterRead", useHttp), "stop server --name=server")
+ .expectFailure().execute(gfshRule);
+ }
+
+ private String connectCommand(String permission, boolean useHttp) {
+ CommandStringBuilder cmd = new CommandStringBuilder("connect").addOption("user", permission)
+ .addOption("password", permission);
+ if (useHttp) {
+ cmd.addOption("use-http");
+ }
+ return cmd.getCommandString();
+ }
+}
diff --git a/geode-core/src/main/java/org/apache/geode/examples/SimpleSecurityManager.java b/geode-core/src/main/java/org/apache/geode/examples/SimpleSecurityManager.java
new file mode 100644
index 0000000..7743ddb
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/examples/SimpleSecurityManager.java
@@ -0,0 +1,59 @@
+/*
+ * 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.examples;
+
+import java.util.Properties;
+
+import org.apache.geode.security.AuthenticationFailedException;
+import org.apache.geode.security.ResourcePermission;
+import org.apache.geode.security.SecurityManager;
+
+/**
+ * Intended for implementation testing, this class authenticates a user when the username matches
+ * the password, which also represents the permissions the user is granted.
+ */
+public class SimpleSecurityManager implements SecurityManager {
+
+ @Override
+ public void init(final Properties securityProps) {
+ // nothing
+ }
+
+ @Override
+ public Object authenticate(final Properties credentials) throws AuthenticationFailedException {
+ String username = credentials.getProperty("security-username");
+ String password = credentials.getProperty("security-password");
+ if (username != null && username.equals(password)) {
+ return username;
+ }
+ throw new AuthenticationFailedException("invalid username/password");
+ }
+
+ @Override
+ public boolean authorize(final Object principal, final ResourcePermission permission) {
+ String[] principals = principal.toString().toLowerCase().split(",");
+ for (String role : principals) {
+ String permissionString = permission.toString().replace(":", "").toLowerCase();
+ if (permissionString.startsWith(role))
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void close() {
+ // nothing
+ }
+}
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/ManagementAgent.java b/geode-core/src/main/java/org/apache/geode/management/internal/ManagementAgent.java
index 39ad703..243458d 100755
--- a/geode-core/src/main/java/org/apache/geode/management/internal/ManagementAgent.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/ManagementAgent.java
@@ -19,7 +19,6 @@ import java.io.Serializable;
import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.ServerSocket;
-import java.net.Socket;
import java.net.UnknownHostException;
import java.rmi.AlreadyBoundException;
import java.rmi.registry.LocateRegistry;
@@ -90,12 +89,12 @@ public class ManagementAgent {
*/
private boolean running = false;
private Registry registry;
+
private JMXConnectorServer jmxConnectorServer;
private JMXShiroAuthenticator shiroAuthenticator;
private final DistributionConfig config;
private final SecurityService securityService;
private boolean isHttpServiceRunning = false;
-
/**
* This system property is set to true when the embedded HTTP server is started so that the
* embedded pulse webapp can use a local MBeanServer instead of a remote JMX connection.
@@ -115,11 +114,11 @@ public class ManagementAgent {
return this.running;
}
- public synchronized boolean isHttpServiceRunning() {
+ synchronized boolean isHttpServiceRunning() {
return isHttpServiceRunning;
}
- public synchronized void setHttpServiceRunning(boolean isHttpServiceRunning) {
+ private synchronized void setHttpServiceRunning(boolean isHttpServiceRunning) {
this.isHttpServiceRunning = isHttpServiceRunning;
}
@@ -180,7 +179,7 @@ public class ManagementAgent {
private Server httpServer;
private final String GEMFIRE_VERSION = GemFireVersion.getGemFireVersion();
- private AgentUtil agentUtil = new AgentUtil(GEMFIRE_VERSION);
+ private final AgentUtil agentUtil = new AgentUtil(GEMFIRE_VERSION);
private void startHttpService(boolean isServer) {
final SystemManagementService managementService = (SystemManagementService) ManagementService
@@ -461,8 +460,7 @@ public class ManagementAgent {
try {
registry.bind("jmxrmi", stub);
} catch (AlreadyBoundException x) {
- final IOException io = new IOException(x.getMessage(), x);
- throw io;
+ throw new IOException(x.getMessage(), x);
}
super.start();
}
@@ -513,30 +511,18 @@ public class ManagementAgent {
logger.info("Registered AccessControlMBean on " + accessControlMBeanON);
} catch (InstanceAlreadyExistsException | MBeanRegistrationException
| NotCompliantMBeanException e) {
- throw new GemFireConfigException("Error while configuring accesscontrol for jmx resource",
- e);
+ throw new GemFireConfigException(
+ "Error while configuring access control for jmx resource", e);
}
}
} catch (MalformedObjectNameException e) {
- throw new GemFireConfigException("Error while configuring accesscontrol for jmx resource", e);
+ throw new GemFireConfigException("Error while configuring access control for jmx resource",
+ e);
}
}
- private static class GemFireRMIClientSocketFactory
- implements RMIClientSocketFactory, Serializable {
-
- private static final long serialVersionUID = -7604285019188827617L;
-
- private transient SocketCreator sc;
-
- public GemFireRMIClientSocketFactory(SocketCreator sc) {
- this.sc = sc;
- }
-
- @Override
- public Socket createSocket(String host, int port) throws IOException {
- return this.sc.connectForClient(host, port, 0);
- }
+ public JMXConnectorServer getJmxConnectorServer() {
+ return jmxConnectorServer;
}
private static class GemFireRMIServerSocketFactory
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/web/controllers/AbstractCommandsController.java b/geode-core/src/main/java/org/apache/geode/management/internal/web/controllers/AbstractCommandsController.java
index aa530a5..fc11fc9 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/web/controllers/AbstractCommandsController.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/web/controllers/AbstractCommandsController.java
@@ -17,19 +17,14 @@ package org.apache.geode.management.internal.web.controllers;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.management.ManagementFactory;
-import java.net.URI;
-import java.util.Collection;
import java.util.HashMap;
-import java.util.Iterator;
import java.util.Map;
-import java.util.Set;
+import javax.management.InstanceNotFoundException;
import javax.management.JMX;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
-import javax.management.Query;
-import javax.management.QueryExp;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.propertyeditors.StringArrayPropertyEditor;
@@ -39,25 +34,25 @@ import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ResponseBody;
-import org.springframework.web.bind.annotation.ResponseStatus;
-import org.springframework.web.context.request.WebRequest;
-import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import org.apache.geode.internal.cache.GemFireCacheImpl;
import org.apache.geode.internal.cache.InternalCache;
-import org.apache.geode.internal.lang.StringUtils;
import org.apache.geode.internal.logging.LogService;
import org.apache.geode.internal.logging.log4j.LogMarker;
+import org.apache.geode.internal.security.SecurityService;
import org.apache.geode.internal.util.ArrayUtils;
import org.apache.geode.management.DistributedSystemMXBean;
import org.apache.geode.management.ManagementService;
import org.apache.geode.management.MemberMXBean;
import org.apache.geode.management.internal.MBeanJMXAdapter;
-import org.apache.geode.management.internal.ManagementConstants;
+import org.apache.geode.management.internal.ManagementAgent;
import org.apache.geode.management.internal.SystemManagementService;
import org.apache.geode.management.internal.cli.shell.Gfsh;
+import org.apache.geode.management.internal.security.MBeanServerWrapper;
import org.apache.geode.management.internal.web.controllers.support.LoginHandlerInterceptor;
import org.apache.geode.management.internal.web.util.UriUtils;
+import org.apache.geode.security.AuthenticationFailedException;
+import org.apache.geode.security.GemFireSecurityException;
import org.apache.geode.security.NotAuthorizedException;
/**
@@ -67,14 +62,15 @@ import org.apache.geode.security.NotAuthorizedException;
*
* @see org.apache.geode.management.MemberMXBean
* @see org.apache.geode.management.internal.cli.shell.Gfsh
+ * @see org.springframework.http.ResponseEntity
* @see org.springframework.stereotype.Controller
* @see org.springframework.web.bind.annotation.ExceptionHandler
* @see org.springframework.web.bind.annotation.InitBinder
+ * @see org.springframework.web.bind.annotation.ResponseBody
* @since GemFire 8.0
*/
@SuppressWarnings("unused")
public abstract class AbstractCommandsController {
-
private static final Logger logger = LogService.getLogger();
protected static final String DEFAULT_ENCODING = UriUtils.DEFAULT_ENCODING;
@@ -84,242 +80,53 @@ public abstract class AbstractCommandsController {
private Class accessControlKlass;
- private InternalCache getCache() {
- return GemFireCacheImpl.getInstance();
- }
-
- // Convert a predefined exception to an HTTP Status code
- @ResponseStatus(value = HttpStatus.UNAUTHORIZED, reason = "Not authenticated") // 401
- @ExceptionHandler(org.apache.geode.security.AuthenticationFailedException.class)
- public void authenticate() {
- // nothing
- }
-
- // Convert a predefined exception to an HTTP Status code
- @ResponseStatus(value = HttpStatus.FORBIDDEN, reason = "Access Denied") // 403
- @ExceptionHandler(java.lang.SecurityException.class)
- public void authorize() {
- // nothing
- }
-
- /**
- * Asserts the argument is valid, as determined by the caller passing the result of an evaluated
- * expression to this assertion.
- *
- * @param validArg a boolean value indicating the evaluation of the expression validating the
- * argument.
- * @param message a String value used as the message when constructing an
- * IllegalArgumentException.
- * @param args Object arguments used to populate placeholder's in the message.
- * @throws IllegalArgumentException if the argument is not valid.
- * @see java.lang.String#format(String, Object...)
- */
- protected static void assertArgument(final boolean validArg, final String message,
- final Object... args) {
- if (!validArg) {
- throw new IllegalArgumentException(String.format(message, args));
- }
- }
-
- /**
- * Asserts the Object reference is not null!
- *
- * @param obj the reference to the Object.
- * @param message the String value used as the message when constructing and throwing a
- * NullPointerException.
- * @param args Object arguments used to populate placeholder's in the message.
- * @throws NullPointerException if the Object reference is null.
- * @see java.lang.String#format(String, Object...)
- */
- protected static void assertNotNull(final Object obj, final String message,
- final Object... args) {
- if (obj == null) {
- throw new NullPointerException(String.format(message, args));
- }
- }
-
- /**
- * Asserts whether state, based on the evaluation of a conditional expression, passed to this
- * assertion is valid.
- *
- * @param validState a boolean value indicating the evaluation of the expression from which the
- * conditional state is based. For example, a caller might use an expression of the form
- * (initableObj.isInitialized()).
- * @param message a String values used as the message when constructing an IllegalStateException.
- * @param args Object arguments used to populate placeholder's in the message.
- * @throws IllegalStateException if the conditional state is not valid.
- * @see java.lang.String#format(String, Object...)
- */
- protected static void assertState(final boolean validState, final String message,
- final Object... args) {
- if (!validState) {
- throw new IllegalStateException(String.format(message, args));
- }
- }
-
- /**
- * Decodes the encoded String value using the default encoding UTF-8. It is assumed the String
- * value was encoded with the URLEncoder using the UTF-8 encoding. This method handles
- * UnsupportedEncodingException by just returning the encodedValue.
- *
- * @param encodedValue the encoded String value to decode.
- * @return the decoded value of the String or encodedValue if the UTF-8 encoding is unsupported.
- * @see org.apache.geode.management.internal.web.util.UriUtils#decode(String)
- */
- protected static String decode(final String encodedValue) {
- return UriUtils.decode(encodedValue);
+ @ExceptionHandler(Exception.class)
+ public ResponseEntity<String> internalError(final Exception e) {
+ final String stackTrace = getPrintableStackTrace(e);
+ logger.fatal(stackTrace);
+ return new ResponseEntity<>(stackTrace, HttpStatus.INTERNAL_SERVER_ERROR);
}
- /**
- * Decodes the encoded String value using the specified encoding (such as UTF-8). It is assumed
- * the String value was encoded with the URLEncoder using the specified encoding. This method
- * handles UnsupportedEncodingException by just returning the encodedValue.
- *
- * @param encodedValue a String value encoded in the encoding.
- * @param encoding a String value specifying the encoding.
- * @return the decoded value of the String or encodedValue if the specified encoding is
- * unsupported.
- * @see org.apache.geode.management.internal.web.util.UriUtils#decode(String, String)
- */
- protected static String decode(final String encodedValue, final String encoding) {
- return UriUtils.decode(encodedValue, encoding);
+ @ExceptionHandler(AuthenticationFailedException.class)
+ public ResponseEntity<String> unauthorized(AuthenticationFailedException e) {
+ return new ResponseEntity<>(e.getMessage(), HttpStatus.UNAUTHORIZED);
}
- /**
- * Gets the specified value if not null or empty, otherwise returns the default value.
- *
- * @param value the String value being evaluated for having value (not null and not empty).
- * @param defaultValue the default String value returned if 'value' has no value.
- * @return 'value' if not null or empty, otherwise returns the default value.
- * @see #hasValue(String)
- */
- protected static String defaultIfNoValue(final String value, final String defaultValue) {
- return (hasValue(value) ? value : defaultValue);
+ @ExceptionHandler({NotAuthorizedException.class, java.lang.SecurityException.class})
+ public ResponseEntity<String> forbidden(Exception e) {
+ return new ResponseEntity<>(e.getMessage(), HttpStatus.FORBIDDEN);
}
- /**
- * Encodes the String value using the default encoding UTF-8.
- *
- * @param value the String value to encode.
- * @return an encoded value of the String using the default encoding UTF-8 or value if the UTF-8
- * encoding is unsupported.
- * @see org.apache.geode.management.internal.web.util.UriUtils#encode(String)
- */
- protected static String encode(final String value) {
- return UriUtils.encode(value);
+ @ExceptionHandler(MalformedObjectNameException.class)
+ public ResponseEntity<String> badRequest(final MalformedObjectNameException e) {
+ logger.info(e);
+ return new ResponseEntity<>(getPrintableStackTrace(e), HttpStatus.BAD_REQUEST);
}
- /**
- * Encodes the String value using the specified encoding (such as UTF-8).
- *
- * @param value the String value to encode.
- * @param encoding a String value indicating the encoding.
- * @return an encoded value of the String using the specified encoding or value if the specified
- * encoding is unsupported.
- * @see org.apache.geode.management.internal.web.util.UriUtils#encode(String, String)
- */
- protected static String encode(final String value, final String encoding) {
- return UriUtils.encode(value, encoding);
+ @ExceptionHandler(InstanceNotFoundException.class)
+ public ResponseEntity<String> notFound(final InstanceNotFoundException e) {
+ logger.info(e);
+ return new ResponseEntity<>(getPrintableStackTrace(e), HttpStatus.NOT_FOUND);
}
- /**
- * Determines whether the specified Object has value, which is determined by a non-null Object
- * reference.
- *
- * @param value the Object value being evaluated for value.
- * @return a boolean value indicating whether the specified Object has value.
- * @see java.lang.Object
- */
- protected static boolean hasValue(final Object value) {
- return (value instanceof String[] ? hasValue((String[]) value)
- : (value instanceof String ? hasValue((String) value) : value != null));
- }
-
- /**
- * Determines whether the specified String has value, determined by whether the String is
- * non-null, not empty and not blank.
- *
- * @param value the String being evaluated for value.
- * @return a boolean indicating whether the specified String has value or not.
- * @see java.lang.String
- */
- protected static boolean hasValue(final String value) {
- return StringUtils.isNotBlank(value);
- }
-
- /**
- * Determines whether the specified String array has any value, which is determined by a non-null
- * String array reference along with containing at least 1 non-null, not empty and not blank
- * element.
- *
- * @param array an String array being evaluated for value.
- * @return a boolean indicating whether the specified String array has any value.
- * @see #hasValue(String)
- * @see java.lang.String
- */
- protected static boolean hasValue(final String[] array) {
- if (array != null && array.length > 0) {
- for (final String element : array) {
- if (hasValue(element)) {
- return true;
- }
- }
- }
-
- return false;
- }
/**
* Writes the stack trace of the Throwable to a String.
- *
+ *
* @param t a Throwable object who's stack trace will be written to a String.
* @return a String containing the stack trace of the Throwable.
* @see java.io.StringWriter
* @see java.lang.Throwable#printStackTrace(java.io.PrintWriter)
*/
- protected static String printStackTrace(final Throwable t) {
+ private static String getPrintableStackTrace(final Throwable t) {
final StringWriter stackTraceWriter = new StringWriter();
t.printStackTrace(new PrintWriter(stackTraceWriter));
return stackTraceWriter.toString();
}
/**
- * Converts the URI relative path to an absolute path based on the Servlet context information.
- *
- * @param path the URI relative path to append to the Servlet context path.
- * @param scheme the scheme to use for the URI
- * @return a URI constructed with all component path information.
- * @see java.net.URI
- * @see org.springframework.web.servlet.support.ServletUriComponentsBuilder
- */
- public static URI toUri(final String path, final String scheme) {
- return ServletUriComponentsBuilder.fromCurrentContextPath().path(REST_API_VERSION).path(path)
- .scheme(scheme).build().toUri();
- }
-
- /**
- * Handles any Exception thrown by a REST API web service endpoint, HTTP request handler method
- * during the invocation and processing of a command.
- *
- * @param cause the Exception causing the error.
- * @return a ResponseEntity with an appropriate HTTP status code (500 - Internal Server Error) and
- * HTTP response body containing the stack trace of the Exception.
- * @see java.lang.Exception
- * @see org.springframework.http.ResponseEntity
- * @see org.springframework.web.bind.annotation.ExceptionHandler
- * @see org.springframework.web.bind.annotation.ResponseBody
- */
- @ExceptionHandler(Exception.class)
- @ResponseBody
- public ResponseEntity<String> handleException(final Exception cause) {
- final String stackTrace = printStackTrace(cause);
- logger.fatal(stackTrace);
- return new ResponseEntity<String>(stackTrace, HttpStatus.INTERNAL_SERVER_ERROR);
- }
-
- /**
* Initializes data bindings for various HTTP request handler method parameter Java class types.
- *
+ *
* @param dataBinder the DataBinder implementation used for Web transactions.
* @see org.springframework.web.bind.WebDataBinder
* @see org.springframework.web.bind.annotation.InitBinder
@@ -330,110 +137,27 @@ public abstract class AbstractCommandsController {
new StringArrayPropertyEditor(StringArrayPropertyEditor.DEFAULT_SEPARATOR, false));
}
- /**
- * Logs the client's HTTP (web) request including details of the HTTP headers and request
- * parameters along with the web request context and description.
- *
- * @param request the object encapsulating the details of the client's HTTP (web) request.
- * @see org.springframework.web.context.request.WebRequest
- */
- protected void logRequest(final WebRequest request) {
- if (request != null) {
- final Map<String, String> headers = new HashMap<java.lang.String, java.lang.String>();
-
- for (Iterator<String> it = request.getHeaderNames(); it.hasNext();) {
- final String headerName = it.next();
- headers.put(headerName,
- ArrayUtils.toString((Object[]) request.getHeaderValues(headerName)));
- }
-
- final Map<String, String> parameters =
- new HashMap<String, String>(request.getParameterMap().size());
-
- for (Iterator<String> it = request.getParameterNames(); it.hasNext();) {
- final String parameterName = it.next();
- parameters.put(parameterName,
- ArrayUtils.toString((Object[]) request.getParameterValues(parameterName)));
- }
-
- logger.info("HTTP-request: description ({}), context ({}), headers ({}), parameters ({})",
- request.getDescription(false), request.getContextPath(), headers, parameters);
- }
- }
/**
* Gets a reference to the platform MBeanServer running in this JVM process. The MBeanServer
- * instance constitutes a connection to the MBeanServer.
+ * instance constitutes a connection to the MBeanServer. This method returns a security-wrapped
+ * MBean if integrated security is active.
*
* @return a reference to the platform MBeanServer for this JVM process.
* @see java.lang.management.ManagementFactory#getPlatformMBeanServer()
* @see javax.management.MBeanServer
*/
protected MBeanServer getMBeanServer() {
- return ManagementFactory.getPlatformMBeanServer();
- }
-
- /**
- * Gets the MemberMXBean from the JVM Platform MBeanServer for the specified member, identified by
- * name or ID in the GemFire cluster.
- *
- * @param memberNameId a String indicating the name or ID of the GemFire member.
- * @return a proxy to the GemFire member's MemberMXBean.
- * @throws IllegalStateException if no MemberMXBean could be found for GemFire member with ID or
- * name.
- * @throws RuntimeException wrapping the MalformedObjectNameException if the ObjectName pattern is
- * malformed.
- * @see #getMBeanServer()
- * @see #isMemberMXBeanFound(java.util.Collection)
- * @see javax.management.ObjectName
- * @see javax.management.QueryExp
- * @see javax.management.MBeanServer#queryNames(javax.management.ObjectName,
- * javax.management.QueryExp)
- * @see javax.management.JMX#newMXBeanProxy(javax.management.MBeanServerConnection,
- * javax.management.ObjectName, Class)
- * @see org.apache.geode.management.MemberMXBean
- */
- protected MemberMXBean getMemberMXBean(final String memberNameId) {
- try {
- final MBeanServer connection = getMBeanServer();
-
- final String objectNamePattern =
- ManagementConstants.OBJECTNAME__PREFIX.concat("type=Member,*");
-
- // NOTE throws a MalformedObjectNameException, but this should not happen since we constructed
- // the ObjectName above
- final ObjectName objectName = ObjectName.getInstance(objectNamePattern);
-
- final QueryExp query = Query.or(Query.eq(Query.attr("Name"), Query.value(memberNameId)),
- Query.eq(Query.attr("Id"), Query.value(memberNameId)));
-
- final Set<ObjectName> objectNames = connection.queryNames(objectName, query);
-
- assertState(isMemberMXBeanFound(objectNames),
- "No MemberMXBean with ObjectName (%1$s) based on Query (%2$s) was found in the Platform MBeanServer for member (%3$s)!",
- objectName, query, memberNameId);
-
- return JMX.newMXBeanProxy(connection, objectNames.iterator().next(), MemberMXBean.class);
- } catch (MalformedObjectNameException e) {
- throw new RuntimeException(e);
- }
- }
-
- /**
- * Determines whether the desired MemberMXBean, identified by name or ID, was found in the
- * platform MBeanServer of this JVM process.
- *
- * @param objectNames a Collection of ObjectNames possibly referring to the desired MemberMXBean.
- * @return a boolean value indicating whether the desired MemberMXBean was found.
- * @see javax.management.ObjectName
- */
- private boolean isMemberMXBeanFound(final Collection<ObjectName> objectNames) {
- return !(objectNames == null || objectNames.isEmpty());
+ InternalCache cache = GemFireCacheImpl.getInstance();
+ SystemManagementService service =
+ (SystemManagementService) ManagementService.getExistingManagementService(cache);
+ ManagementAgent managementAgent = service.getManagementAgent();
+ return managementAgent.getJmxConnectorServer().getMBeanServer();
}
/**
* Lookup operation for the MemberMXBean representing the Manager in the GemFire cluster. This
- * method gets an instance fo the Platform MBeanServer for this JVM process and uses it to lookup
+ * method gets an instance of the Platform MBeanServer for this JVM process and uses it to lookup
* the MemberMXBean for the GemFire Manager based on the ObjectName declared in the
* DistributedSystemMXBean.getManagerObjectName() operation.
*
@@ -444,15 +168,11 @@ public abstract class AbstractCommandsController {
* @see org.apache.geode.management.DistributedSystemMXBean
* @see org.apache.geode.management.MemberMXBean
*/
- protected synchronized MemberMXBean getManagingMemberMXBean() {
+ private synchronized MemberMXBean getManagingMemberMXBean() {
if (managingMemberMXBeanProxy == null) {
- SystemManagementService service =
- (SystemManagementService) ManagementService.getExistingManagementService(getCache());
MBeanServer mbs = getMBeanServer();
-
- final DistributedSystemMXBean distributedSystemMXBean = JMX.newMXBeanProxy(mbs,
+ DistributedSystemMXBean distributedSystemMXBean = JMX.newMXBeanProxy(mbs,
MBeanJMXAdapter.getDistributedSystemName(), DistributedSystemMXBean.class);
-
managingMemberMXBeanProxy = createMemberMXBeanForManagerUsingProxy(mbs,
distributedSystemMXBean.getMemberObjectName());
}
@@ -461,11 +181,9 @@ public abstract class AbstractCommandsController {
}
protected synchronized ObjectName getMemberObjectName() {
- final MBeanServer platformMBeanServer = getMBeanServer();
-
- final DistributedSystemMXBean distributedSystemMXBean = JMX.newMXBeanProxy(platformMBeanServer,
+ MBeanServer platformMBeanServer = getMBeanServer();
+ DistributedSystemMXBean distributedSystemMXBean = JMX.newMXBeanProxy(platformMBeanServer,
MBeanJMXAdapter.getDistributedSystemName(), DistributedSystemMXBean.class);
-
return distributedSystemMXBean.getMemberObjectName();
}
@@ -494,7 +212,7 @@ public abstract class AbstractCommandsController {
* @see LoginHandlerInterceptor#getEnvironment()
*/
protected Map<String, String> getEnvironment() {
- final Map<String, String> environment = new HashMap<String, String>();
+ final Map<String, String> environment = new HashMap<>();
environment.putAll(LoginHandlerInterceptor.getEnvironment());
environment.put(Gfsh.ENV_APP_NAME, Gfsh.GFSH_APP_NAME);
@@ -525,13 +243,7 @@ public abstract class AbstractCommandsController {
logger.debug(LogMarker.CONFIG,
"Processing Command ({}) with Environment ({}) having File Data ({})...", command,
environment, (fileData != null && fileData.length > 0));
- return getManagingMemberMXBean().processCommand(command, environment,
- ArrayUtils.toByteArray(fileData));
+ MemberMXBean manager = getManagingMemberMXBean();
+ return manager.processCommand(command, environment, ArrayUtils.toByteArray(fileData));
}
-
- @ExceptionHandler(NotAuthorizedException.class)
- public ResponseEntity<String> handleAppException(NotAuthorizedException ex) {
- return new ResponseEntity<String>(ex.getMessage(), HttpStatus.FORBIDDEN);
- }
-
}
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/web/controllers/AbstractMultiPartCommandsController.java b/geode-core/src/main/java/org/apache/geode/management/internal/web/controllers/AbstractMultiPartCommandsController.java
deleted file mode 100644
index 14b3d05..0000000
--- a/geode-core/src/main/java/org/apache/geode/management/internal/web/controllers/AbstractMultiPartCommandsController.java
+++ /dev/null
@@ -1,77 +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.web.controllers;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-
-import org.apache.geode.internal.lang.SystemUtils;
-import org.apache.geode.internal.util.IOUtils;
-
-import org.springframework.web.multipart.MultipartFile;
-
-/**
- * The AbstractMultiPartCommandsController class is a abstract base class encapsulating all common
- * functionality for handling multi-part (file upload) HTTP requests.
- * <p/>
- *
- * @see org.apache.geode.management.internal.web.controllers.AbstractCommandsController
- * @since GemFire 8.0
- */
-@SuppressWarnings("unused")
-public class AbstractMultiPartCommandsController extends AbstractCommandsController {
-
- protected static final String RESOURCES_REQUEST_PARAMETER = "resources";
-
- /**
- * Saves an array of File objects to this system's file system.
- * <p/>
- *
- * @param files an array of MultipartFile objects to persist to the file system.
- * @throws IOException if I/O error occurs while saving the Files to the file system.
- * @see org.springframework.web.multipart.MultipartFile
- */
- protected static void save(final MultipartFile... files) throws IOException {
- if (files != null) {
- for (final MultipartFile file : files) {
- save(file);
- }
- }
- }
-
- /**
- * Saves a multi-part File to this system's file system.
- * <p/>
- *
- * @param file the MultipartFile object to persist to the file system.
- * @throws IOException if I/O error occurs while saving the File to the file system.
- * @see org.springframework.web.multipart.MultipartFile
- */
- protected static void save(final MultipartFile file) throws IOException {
- final File saveFile = new File(SystemUtils.CURRENT_DIRECTORY, file.getOriginalFilename());
-
- FileOutputStream fileWriter = null;
-
- try {
- fileWriter = new FileOutputStream(saveFile, false);
- fileWriter.write(file.getBytes());
- fileWriter.flush();
- } finally {
- IOUtils.close(fileWriter);
- }
- }
-
-}
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/web/controllers/ShellCommandsController.java b/geode-core/src/main/java/org/apache/geode/management/internal/web/controllers/ShellCommandsController.java
index 02c3e93..9c65090 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/web/controllers/ShellCommandsController.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/web/controllers/ShellCommandsController.java
@@ -14,7 +14,8 @@
*/
package org.apache.geode.management.internal.web.controllers;
-import static org.apache.geode.management.internal.web.controllers.AbstractMultiPartCommandsController.RESOURCES_REQUEST_PARAMETER;
+import static org.apache.commons.io.IOUtils.toInputStream;
+import static org.apache.geode.management.internal.web.util.UriUtils.decode;
import java.io.FileInputStream;
import java.io.IOException;
@@ -23,8 +24,11 @@ import java.util.Set;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceNotFoundException;
+import javax.management.MBeanException;
+import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
+import javax.management.ReflectionException;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.ArrayUtils;
@@ -65,113 +69,53 @@ import org.apache.geode.management.internal.web.util.ConvertUtils;
@RequestMapping(AbstractCommandsController.REST_API_VERSION)
@SuppressWarnings("unused")
public class ShellCommandsController extends AbstractCommandsController {
-
- protected static final String MBEAN_ATTRIBUTE_LINK_RELATION = "mbean-attribute";
- protected static final String MBEAN_OPERATION_LINK_RELATION = "mbean-operation";
- protected static final String MBEAN_QUERY_LINK_RELATION = "mbean-query";
- protected static final String PING_LINK_RELATION = "ping";
-
@RequestMapping(method = {RequestMethod.GET, RequestMethod.POST}, value = "/management/commands")
public ResponseEntity<InputStreamResource> command(@RequestParam(value = "cmd") String command,
- @RequestParam(value = RESOURCES_REQUEST_PARAMETER,
- required = false) MultipartFile[] fileResource)
+ @RequestParam(value = "resources", required = false) MultipartFile[] fileResource)
throws IOException {
String result =
processCommand(decode(command), getEnvironment(), ConvertUtils.convert(fileResource));
return getResponse(result);
}
- ResponseEntity<InputStreamResource> getResponse(String result) {
- // the result is json string from CommandResult
- CommandResult commandResult = ResultBuilder.fromJson(result);
-
- if (commandResult.getStatus().equals(Result.Status.OK) && commandResult.hasFileToDownload()) {
- return getFileDownloadResponse(commandResult);
- } else {
- return getJsonResponse(result);
- }
- }
-
- private ResponseEntity<InputStreamResource> getJsonResponse(String result) {
- // if the command is successful, the output is the filepath, else we need to send the
- // original result back so that the receiver will know to turn it into a Result object
-
- HttpHeaders respHeaders = new HttpHeaders();
- try {
- InputStreamResource isr =
- new InputStreamResource(org.apache.commons.io.IOUtils.toInputStream(result, "UTF-8"));
- respHeaders.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
- return new ResponseEntity<>(isr, respHeaders, HttpStatus.OK);
- } catch (Exception e) {
- throw new RuntimeException("IO Error writing file to output stream", e);
- }
- }
-
- private ResponseEntity<InputStreamResource> getFileDownloadResponse(CommandResult commandResult) {
- HttpHeaders respHeaders = new HttpHeaders();
- InputStreamResource isr;// if the command is successful, the output is the filepath,
-
- Path filePath = commandResult.getFileToDownload();
- try {
- isr = new InputStreamResource(new FileInputStream(filePath.toFile()));
- respHeaders.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE);
- return new ResponseEntity<>(isr, respHeaders, HttpStatus.OK);
- } catch (Exception e) {
- throw new RuntimeException("IO Error writing file to output stream", e);
- } finally {
- FileUtils.deleteQuietly(filePath.toFile());
- }
- }
-
@RequestMapping(method = RequestMethod.GET, value = "/mbean/attribute")
public ResponseEntity<?> getAttribute(@RequestParam("resourceName") final String resourceName,
- @RequestParam("attributeName") final String attributeName) {
- try {
- final Object attributeValue = getMBeanServer()
- .getAttribute(ObjectName.getInstance(decode(resourceName)), decode(attributeName));
-
- return new ResponseEntity<>(IOUtils.serializeObject(attributeValue), HttpStatus.OK);
- } catch (AttributeNotFoundException | MalformedObjectNameException e) {
- return new ResponseEntity<>(printStackTrace(e), HttpStatus.BAD_REQUEST);
- } catch (InstanceNotFoundException e) {
- return new ResponseEntity<>(printStackTrace(e), HttpStatus.NOT_FOUND);
- } catch (Exception e) {
- return new ResponseEntity<>(printStackTrace(e), HttpStatus.INTERNAL_SERVER_ERROR);
- }
+ @RequestParam("attributeName") final String attributeName)
+ throws AttributeNotFoundException, MBeanException, ReflectionException,
+ InstanceNotFoundException, IOException, MalformedObjectNameException {
+ // Exceptions are caught by the @ExceptionHandler AbstractCommandsController.handleAppException
+ MBeanServer mBeanServer = getMBeanServer();
+ ObjectName objectName = ObjectName.getInstance(decode(resourceName));
+ final Object attributeValue = mBeanServer.getAttribute(objectName, decode(attributeName));
+ byte[] serializedResult = IOUtils.serializeObject(attributeValue);
+ return new ResponseEntity<>(serializedResult, HttpStatus.OK);
}
@RequestMapping(method = RequestMethod.POST, value = "/mbean/operation")
public ResponseEntity<?> invoke(@RequestParam("resourceName") final String resourceName,
@RequestParam("operationName") final String operationName,
@RequestParam(value = "signature", required = false) String[] signature,
- @RequestParam(value = "parameters", required = false) Object[] parameters) {
+ @RequestParam(value = "parameters", required = false) Object[] parameters)
+ throws MalformedObjectNameException, MBeanException, InstanceNotFoundException,
+ ReflectionException, IOException {
+ // Exceptions are caught by the @ExceptionHandler AbstractCommandsController.handleAppException
signature = (signature != null ? signature : ArrayUtils.EMPTY_STRING_ARRAY);
parameters = (parameters != null ? parameters : ObjectUtils.EMPTY_OBJECT_ARRAY);
-
- try {
- final Object result = getMBeanServer().invoke(ObjectName.getInstance(decode(resourceName)),
- decode(operationName), parameters, signature);
-
- return new ResponseEntity<>(IOUtils.serializeObject(result), HttpStatus.OK);
- } catch (InstanceNotFoundException e) {
- return new ResponseEntity<>(printStackTrace(e), HttpStatus.NOT_FOUND);
- } catch (MalformedObjectNameException e) {
- return new ResponseEntity<>(printStackTrace(e), HttpStatus.BAD_REQUEST);
- } catch (Exception e) {
- return new ResponseEntity<>(printStackTrace(e), HttpStatus.INTERNAL_SERVER_ERROR);
- }
+ MBeanServer mBeanServer = getMBeanServer();
+ ObjectName objectName = ObjectName.getInstance(decode(resourceName));
+ final Object result =
+ mBeanServer.invoke(objectName, decode(operationName), parameters, signature);
+ byte[] serializedResult = IOUtils.serializeObject(result);
+ return new ResponseEntity<>(serializedResult, HttpStatus.OK);
}
@RequestMapping(method = RequestMethod.POST, value = "/mbean/query")
- public ResponseEntity<?> queryNames(@RequestBody final QueryParameterSource query) {
- try {
- final Set<ObjectName> objectNames =
- getMBeanServer().queryNames(query.getObjectName(), query.getQueryExpression());
-
- return new ResponseEntity<>(IOUtils.serializeObject(objectNames), HttpStatus.OK);
- } catch (IOException e) {
- return new ResponseEntity<>(printStackTrace(e), HttpStatus.INTERNAL_SERVER_ERROR);
- }
+ public ResponseEntity<?> queryNames(@RequestBody final QueryParameterSource query)
+ throws IOException {
+ // Exceptions are caught by the @ExceptionHandler AbstractCommandsController.handleAppException
+ final Set<ObjectName> objectNames =
+ getMBeanServer().queryNames(query.getObjectName(), query.getQueryExpression());
+ return new ResponseEntity<>(IOUtils.serializeObject(objectNames), HttpStatus.OK);
}
@RequestMapping(method = {RequestMethod.GET, RequestMethod.HEAD}, value = "/ping")
@@ -187,8 +131,42 @@ public class ShellCommandsController extends AbstractCommandsController {
@RequestMapping(method = RequestMethod.GET, value = "/version/full")
@ResponseBody
- public String versionSimple() {
+ public String fullVersion() {
return GemFireVersion.asString();
}
+
+ private ResponseEntity<InputStreamResource> getResponse(String result) {
+ CommandResult commandResult = ResultBuilder.fromJson(result);
+ if (commandResult.getStatus().equals(Result.Status.OK) && commandResult.hasFileToDownload()) {
+ return getFileDownloadResponse(commandResult);
+ } else {
+ return getJsonResponse(result);
+ }
+ }
+
+ private ResponseEntity<InputStreamResource> getJsonResponse(String result) {
+ HttpHeaders respHeaders = new HttpHeaders();
+ try {
+ InputStreamResource isr = new InputStreamResource(toInputStream(result, "UTF-8"));
+ respHeaders.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
+ return new ResponseEntity<>(isr, respHeaders, HttpStatus.OK);
+ } catch (Exception e) {
+ throw new RuntimeException("IO Error writing file to output stream", e);
+ }
+ }
+
+ private ResponseEntity<InputStreamResource> getFileDownloadResponse(CommandResult commandResult) {
+ HttpHeaders respHeaders = new HttpHeaders();
+ Path filePath = commandResult.getFileToDownload();
+ try {
+ InputStreamResource isr = new InputStreamResource(new FileInputStream(filePath.toFile()));
+ respHeaders.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE);
+ return new ResponseEntity<>(isr, respHeaders, HttpStatus.OK);
+ } catch (Exception e) {
+ throw new RuntimeException("IO Error writing file to output stream", e);
+ } finally {
+ FileUtils.deleteQuietly(filePath.toFile());
+ }
+ }
}
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/web/http/support/SimpleHttpRequester.java b/geode-core/src/main/java/org/apache/geode/management/internal/web/http/support/SimpleHttpRequester.java
index 23c4482..8c4f91f 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/web/http/support/SimpleHttpRequester.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/web/http/support/SimpleHttpRequester.java
@@ -15,7 +15,6 @@
package org.apache.geode.management.internal.web.http.support;
-import java.io.IOException;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
@@ -23,16 +22,12 @@ import java.util.Set;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
-import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
-import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
-import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
import org.apache.geode.management.internal.cli.shell.Gfsh;
-import org.apache.geode.security.AuthenticationFailedException;
-import org.apache.geode.security.NotAuthorizedException;
+import org.apache.geode.management.internal.web.shell.RestOperationErrorHandler;
/**
@@ -78,51 +73,7 @@ public class SimpleHttpRequester {
this.securityProperties = securityProperties;
this.restTemplate = new RestTemplate(clientHttpRequestFactory);
- this.restTemplate.setErrorHandler(new ResponseErrorHandler() {
- @Override
- public boolean hasError(final ClientHttpResponse response) throws IOException {
- final HttpStatus status = response.getStatusCode();
-
- switch (status) {
- case BAD_REQUEST: // 400 *
- case UNAUTHORIZED: // 401
- case FORBIDDEN: // 403
- case NOT_FOUND: // 404 *
- case METHOD_NOT_ALLOWED: // 405 *
- case NOT_ACCEPTABLE: // 406 *
- case REQUEST_TIMEOUT: // 408
- case CONFLICT: // 409
- case REQUEST_ENTITY_TOO_LARGE: // 413
- case REQUEST_URI_TOO_LONG: // 414
- case UNSUPPORTED_MEDIA_TYPE: // 415 *
- case TOO_MANY_REQUESTS: // 429
- case INTERNAL_SERVER_ERROR: // 500 *
- case NOT_IMPLEMENTED: // 501
- case BAD_GATEWAY: // 502 ?
- case SERVICE_UNAVAILABLE: // 503
- return true;
- default:
- return false;
- }
- }
-
- @Override
- public void handleError(final ClientHttpResponse response) throws IOException {
- final String message = String.format("The HTTP request failed with: %1$d - %2$s",
- response.getRawStatusCode(), response.getStatusText());
-
- if (response.getRawStatusCode() == 401) {
- throw new AuthenticationFailedException(message);
- } else if (response.getRawStatusCode() == 403) {
- throw new NotAuthorizedException(message);
- } else {
- throw new RuntimeException(message);
- }
-
- }
-
-
- });
+ this.restTemplate.setErrorHandler(new RestOperationErrorHandler());
}
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/web/shell/HttpOperationInvoker.java b/geode-core/src/main/java/org/apache/geode/management/internal/web/shell/HttpOperationInvoker.java
index a6f6c6f..577280d 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/web/shell/HttpOperationInvoker.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/web/shell/HttpOperationInvoker.java
@@ -14,9 +14,7 @@
*/
package org.apache.geode.management.internal.web.shell;
-import java.io.BufferedReader;
import java.io.IOException;
-import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Files;
@@ -33,12 +31,12 @@ import javax.management.ObjectName;
import javax.management.QueryExp;
import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.Logger;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
-import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpResponse;
@@ -48,13 +46,11 @@ import org.springframework.http.converter.json.MappingJackson2HttpMessageConvert
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.ResourceAccessException;
-import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import org.apache.geode.annotations.TestingOnly;
import org.apache.geode.internal.GemFireVersion;
-import org.apache.geode.internal.lang.StringUtils;
import org.apache.geode.internal.logging.LogService;
import org.apache.geode.internal.util.IOUtils;
import org.apache.geode.management.DistributedSystemMXBean;
@@ -68,8 +64,6 @@ import org.apache.geode.management.internal.web.http.converter.SerializableObjec
import org.apache.geode.management.internal.web.http.support.SimpleHttpRequester;
import org.apache.geode.management.internal.web.shell.support.HttpMBeanProxyFactory;
import org.apache.geode.management.internal.web.util.ConvertUtils;
-import org.apache.geode.security.AuthenticationFailedException;
-import org.apache.geode.security.NotAuthorizedException;
/**
* The HttpOperationInvoker class is an abstract base class encapsulating common functionality for
@@ -85,23 +79,23 @@ import org.apache.geode.security.NotAuthorizedException;
@SuppressWarnings("unused")
public class HttpOperationInvoker implements OperationInvoker {
- protected static final long DEFAULT_INITIAL_DELAY = TimeUnit.SECONDS.toMillis(1);
- protected static final long DEFAULT_PERIOD = TimeUnit.MILLISECONDS.toMillis(2000);
+ private static final long DEFAULT_INITIAL_DELAY = TimeUnit.SECONDS.toMillis(1);
+ private static final long DEFAULT_PERIOD = TimeUnit.MILLISECONDS.toMillis(2000);
- protected static final String REST_API_BASE_URL = "http://localhost:8080";
- protected static final String REST_API_VERSION = "/v1";
- protected static final String REST_API_WEB_APP_CONTEXT = "/geode-mgmt";
- protected static final String REST_API_URL =
+ private static final String REST_API_BASE_URL = "http://localhost:8080";
+ private static final String REST_API_VERSION = "/v1";
+ private static final String REST_API_WEB_APP_CONTEXT = "/geode-mgmt";
+ private static final String REST_API_URL =
REST_API_BASE_URL + REST_API_WEB_APP_CONTEXT + REST_API_VERSION;
- protected static final String USER_AGENT_HTTP_REQUEST_HEADER_VALUE =
+ private static final String USER_AGENT_HTTP_REQUEST_HEADER_VALUE =
"GemFire-Shell/v" + GemFireVersion.getGemFireVersion();
- protected static final TimeUnit DEFAULT_TIME_UNIT = TimeUnit.MILLISECONDS;
+ private static final TimeUnit DEFAULT_TIME_UNIT = TimeUnit.MILLISECONDS;
// the ID of the GemFire distributed system (cluster)
private Integer clusterId = CLUSTER_ID_WHEN_NOT_CONNECTED;
- protected static final String RESOURCES_REQUEST_PARAMETER = "resources";
+ private static final String RESOURCES_REQUEST_PARAMETER = "resources";
// Executor for scheduling periodic Runnable task to assess the state of the Manager's HTTP
// service or Web Service
@@ -144,7 +138,7 @@ public class HttpOperationInvoker implements OperationInvoker {
* testing purposes.
*/
@TestingOnly
- HttpOperationInvoker(final String baseUrl) {
+ private HttpOperationInvoker(final String baseUrl) {
this.baseUrl = baseUrl;
this.executorService = null;
this.gfsh = null;
@@ -154,7 +148,7 @@ public class HttpOperationInvoker implements OperationInvoker {
/**
* Constructs an instance of the HttpOperationInvoker class with a reference to the GemFire shell
* (Gfsh) instance using this HTTP-based OperationInvoker to send commands to the GemFire Manager
- * via HTTP for procsessing along with the base URL to the GemFire Manager's embedded HTTP service
+ * via HTTP for processing along with the base URL to the GemFire Manager's embedded HTTP service
* hosting the HTTP (REST) interface.
*
* @param gfsh a reference to the instance of the GemFire shell (Gfsh) using this HTTP-based
@@ -178,9 +172,8 @@ public class HttpOperationInvoker implements OperationInvoker {
// constructs an instance of the Spring RestTemplate for M&M REST API (interface) operations
this.restTemplate = new RestTemplate(new SimpleClientHttpRequestFactory());
- // add our custom HttpMessageConverter for serializing DTO Objects into the HTTP request message
- // body
- // and de-serializing HTTP response message body content back into DTO Objects
+ // add our custom HttpMessageConverter for serializing DTO Objects into the HTTP request
+ // message body and de-serializing HTTP response message body content back into DTO Objects
List<HttpMessageConverter<?>> converters = this.restTemplate.getMessageConverters();
// remove the MappingJacksonHttpConverter
for (int i = converters.size() - 1; i >= 0; i--) {
@@ -192,77 +185,13 @@ public class HttpOperationInvoker implements OperationInvoker {
converters.add(new SerializableObjectHttpMessageConverter());
// set the ResponseErrorHandler handling any errors originating from our HTTP request
- this.restTemplate.setErrorHandler(new ResponseErrorHandler() {
- @Override
- public boolean hasError(final ClientHttpResponse response) throws IOException {
- final HttpStatus status = response.getStatusCode();
-
- switch (status) {
- case BAD_REQUEST: // 400 *
- case UNAUTHORIZED: // 401
- case FORBIDDEN: // 403
- case NOT_FOUND: // 404 *
- case METHOD_NOT_ALLOWED: // 405 *
- case NOT_ACCEPTABLE: // 406 *
- case REQUEST_TIMEOUT: // 408
- case CONFLICT: // 409
- case REQUEST_ENTITY_TOO_LARGE: // 413
- case REQUEST_URI_TOO_LONG: // 414
- case UNSUPPORTED_MEDIA_TYPE: // 415 *
- case TOO_MANY_REQUESTS: // 429
- case INTERNAL_SERVER_ERROR: // 500 *
- case NOT_IMPLEMENTED: // 501
- case BAD_GATEWAY: // 502 ?
- case SERVICE_UNAVAILABLE: // 503
- return true;
- default:
- return false;
- }
- }
-
- @Override
- public void handleError(final ClientHttpResponse response) throws IOException {
- String body = readBody(response);
- final String message = String.format("The HTTP request failed with: %1$d - %2$s.",
- response.getRawStatusCode(), body);
-
- if (gfsh.getDebug()) {
- gfsh.logSevere(body, null);
- }
-
- if (response.getRawStatusCode() == 401) {
- throw new AuthenticationFailedException(message);
- } else if (response.getRawStatusCode() == 403) {
- throw new NotAuthorizedException(message);
- } else {
- throw new RuntimeException(message);
- }
- }
-
- private String readBody(final ClientHttpResponse response) throws IOException {
- BufferedReader responseBodyReader = null;
+ this.restTemplate.setErrorHandler(new RestOperationErrorHandler(gfsh));
- try {
- responseBodyReader = new BufferedReader(new InputStreamReader(response.getBody()));
-
- final StringBuilder buffer = new StringBuilder();
- String line;
-
- while ((line = responseBodyReader.readLine()) != null) {
- buffer.append(line).append(StringUtils.LINE_SEPARATOR);
- }
-
- return buffer.toString().trim();
- } finally {
- IOUtils.close(responseBodyReader);
- }
- }
- });
setupBackgroundPingRequest();
initClusterId();
}
- protected void setupBackgroundPingRequest() {
+ private void setupBackgroundPingRequest() {
SimpleHttpRequester requester = new SimpleHttpRequester(gfsh, securityProperties);
getExecutorService().scheduleAtFixedRate(() -> {
try {
@@ -312,7 +241,7 @@ public class HttpOperationInvoker implements OperationInvoker {
* @return an instance of the ScheduledExecutorService for scheduling periodic or delayed tasks.
* @see java.util.concurrent.ScheduledExecutorService
*/
- protected ScheduledExecutorService getExecutorService() {
+ private ScheduledExecutorService getExecutorService() {
return this.executorService;
}
@@ -335,7 +264,7 @@ public class HttpOperationInvoker implements OperationInvoker {
* @return an instance of the Spring RestTemplate used to make REST API web service calls.
* @see org.springframework.web.client.RestTemplate
*/
- protected RestTemplate getRestTemplate() {
+ private RestTemplate getRestTemplate() {
return this.restTemplate;
}
@@ -365,7 +294,7 @@ public class HttpOperationInvoker implements OperationInvoker {
* @see #isDebugEnabled()
* @see #printInfo(String, Object...)
*/
- protected void printDebug(final String message, final Object... args) {
+ private void printDebug(final String message, final Object... args) {
if (isDebugEnabled()) {
printInfo(message, args);
}
@@ -446,8 +375,7 @@ public class HttpOperationInvoker implements OperationInvoker {
}
}
- void addHeaderValues(org.springframework.http.client.ClientHttpRequest request)
- throws IOException {
+ private void addHeaderValues(org.springframework.http.client.ClientHttpRequest request) {
// update the headers
request.getHeaders().add(HttpHeaders.USER_AGENT, USER_AGENT_HTTP_REQUEST_HEADER_VALUE);
request.getHeaders().setAccept(acceptableMediaTypes);
@@ -533,7 +461,7 @@ public class HttpOperationInvoker implements OperationInvoker {
return clusterId;
}
- protected void initClusterId() {
+ private void initClusterId() {
if (isReady()) {
try {
clusterId = (Integer) getAttribute(ManagementConstants.OBJECTNAME__DISTRIBUTEDSYSTEM_MXBEAN,
@@ -593,7 +521,7 @@ public class HttpOperationInvoker implements OperationInvoker {
final String[] signatures) {
final URI link = createURI("/mbean/operation");
- MultiValueMap<String, Object> content = new LinkedMultiValueMap<String, Object>();
+ MultiValueMap<String, Object> content = new LinkedMultiValueMap<>();
content.add("resourceName", resourceName);
content.add("operationName", operationName);
@@ -609,8 +537,8 @@ public class HttpOperationInvoker implements OperationInvoker {
}
try {
- return IOUtils
- .deserializeObject(post(link, MediaType.MULTIPART_FORM_DATA, content, byte[].class));
+ byte[] postResult = post(link, MediaType.MULTIPART_FORM_DATA, content, byte[].class);
+ return IOUtils.deserializeObject(postResult);
} catch (IOException e) {
throw new MBeanAccessException(String.format(
"De-serializing the result from invoking operation (%1$s) on MBean (%2$s) failed!",
@@ -678,7 +606,7 @@ public class HttpOperationInvoker implements OperationInvoker {
public Object processCommand(final CommandRequest command) {
URI link = command.getHttpRequestUrl(baseUrl);
if (command.hasFileData()) {
- MultiValueMap<String, Object> content = new LinkedMultiValueMap<String, Object>();
+ MultiValueMap<String, Object> content = new LinkedMultiValueMap<>();
Resource[] resources = ConvertUtils.convert(command.getFileData());
for (Resource resource : resources) {
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/web/shell/RestOperationErrorHandler.java b/geode-core/src/main/java/org/apache/geode/management/internal/web/shell/RestOperationErrorHandler.java
new file mode 100644
index 0000000..676030b
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/web/shell/RestOperationErrorHandler.java
@@ -0,0 +1,58 @@
+/*
+ * 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.web.shell;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.commons.io.IOUtils;
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.web.client.DefaultResponseErrorHandler;
+
+import org.apache.geode.management.internal.cli.shell.Gfsh;
+import org.apache.geode.security.AuthenticationFailedException;
+import org.apache.geode.security.NotAuthorizedException;
+
+public class RestOperationErrorHandler extends DefaultResponseErrorHandler {
+ private final Gfsh gfsh;
+
+ RestOperationErrorHandler(Gfsh gfsh) {
+ this.gfsh = gfsh;
+ }
+
+ public RestOperationErrorHandler() {
+ this(null);
+ }
+
+ @Override
+ public void handleError(final ClientHttpResponse response) throws IOException {
+ String body = IOUtils.toString(response.getBody(), StandardCharsets.UTF_8);
+ final String message = String.format("The HTTP request failed with: %1$d - %2$s",
+ response.getRawStatusCode(), body);
+
+ if (gfsh != null && gfsh.getDebug()) {
+ gfsh.logSevere(body, null);
+ }
+
+ if (response.getRawStatusCode() == 401) {
+ throw new AuthenticationFailedException(message);
+ } else if (response.getRawStatusCode() == 403) {
+ throw new NotAuthorizedException(message);
+ } else {
+ throw new RuntimeException(message);
+ }
+
+ }
+}
diff --git a/geode-junit/src/main/java/org/apache/geode/test/junit/rules/gfsh/GfshExecution.java b/geode-junit/src/main/java/org/apache/geode/test/junit/rules/gfsh/GfshExecution.java
index af23cc0..23d83e6 100644
--- a/geode-junit/src/main/java/org/apache/geode/test/junit/rules/gfsh/GfshExecution.java
+++ b/geode-junit/src/main/java/org/apache/geode/test/junit/rules/gfsh/GfshExecution.java
@@ -14,9 +14,13 @@
*/
package org.apache.geode.test.junit.rules.gfsh;
+import static java.util.stream.Collectors.joining;
+
import java.io.File;
import java.util.List;
+import org.apache.commons.lang.SystemUtils;
+
public class GfshExecution {
private final Process process;
private final File workingDir;
@@ -36,6 +40,10 @@ public class GfshExecution {
return processLogger.getStdErrLines();
}
+ public String getStdErrText() {
+ return getStdErrLines().stream().collect(joining(SystemUtils.LINE_SEPARATOR));
+ }
+
/**
* Note that this is the working directory of gfsh itself. If your script started a server or
* locator, this will be the parent directory of that member's working directory.
diff --git a/geode-junit/src/main/java/org/apache/geode/test/junit/rules/gfsh/GfshRule.java b/geode-junit/src/main/java/org/apache/geode/test/junit/rules/gfsh/GfshRule.java
index 595f9fd..68baaf5 100644
--- a/geode-junit/src/main/java/org/apache/geode/test/junit/rules/gfsh/GfshRule.java
+++ b/geode-junit/src/main/java/org/apache/geode/test/junit/rules/gfsh/GfshRule.java
@@ -89,7 +89,8 @@ public class GfshRule extends ExternalResource {
public GfshExecution execute(GfshScript gfshScript) {
GfshExecution gfshExecution;
try {
- File workingDir = temporaryFolder.newFolder(gfshScript.getName());
+ File workingDir = new File(temporaryFolder.getRoot(), gfshScript.getName());
+ workingDir.mkdirs();
Process process = toProcessBuilder(gfshScript, gfsh, workingDir).start();
gfshExecution = new GfshExecution(process, workingDir);
gfshExecutions.add(gfshExecution);
diff --git a/geode-web/src/test/java/org/apache/geode/management/internal/web/shell/HttpOperationInvokerSecurityTest.java b/geode-web/src/test/java/org/apache/geode/management/internal/web/shell/HttpOperationInvokerSecurityTest.java
index a610676..5e74033 100644
--- a/geode-web/src/test/java/org/apache/geode/management/internal/web/shell/HttpOperationInvokerSecurityTest.java
+++ b/geode-web/src/test/java/org/apache/geode/management/internal/web/shell/HttpOperationInvokerSecurityTest.java
@@ -55,7 +55,7 @@ public class HttpOperationInvokerSecurityTest {
private static CommandRequest request;
@Test
- public void performBeanOperationsNoAuthorizationCheck() throws Exception {
+ public void performBeanOperationsHasAuthorizationCheck() throws Exception {
gfsh.secureConnectAndVerify(locator.getHttpPort(), GfshShellConnectionRule.PortType.http,
"test", "test");
invoker = (HttpOperationInvoker) gfsh.getGfsh().getOperationInvoker();
@@ -70,11 +70,11 @@ public class HttpOperationInvokerSecurityTest {
DistributedSystemMXBean bean = invoker.getDistributedSystemMXBean();
assertThat(bean).isInstanceOf(DistributedSystemMXBean.class);
- String[] gatewayReceivers =
- (String[]) invoker.invoke(ManagementConstants.OBJECTNAME__DISTRIBUTEDSYSTEM_MXBEAN,
- "listGatewayReceivers", new Object[0], new String[0]);
+ assertThatThrownBy(
+ () -> invoker.invoke(ManagementConstants.OBJECTNAME__DISTRIBUTEDSYSTEM_MXBEAN,
+ "listGatewayReceivers", new Object[0], new String[0]))
+ .isInstanceOf(NotAuthorizedException.class);
- assertThat(gatewayReceivers).isEmpty();
ObjectName objectName = ObjectName.getInstance("GemFire:type=Member,*");
QueryExp query = Query.eq(Query.attr("Name"), Query.value("mock"));
@@ -94,7 +94,6 @@ public class HttpOperationInvokerSecurityTest {
when(request.getHttpRequestUrl(anyString())).thenCallRealMethod();
when(request.getUserInput()).thenReturn("list members");
-
assertThatThrownBy(() -> invoker.processCommand(request))
.isInstanceOf(NotAuthorizedException.class);
gfsh.disconnect();
--
To stop receiving notification emails like this one, please contact
['"commits@geode.apache.org" <co...@geode.apache.org>'].