diff --git a/hazelcast/src/main/java/com/hazelcast/internal/management/ManagementCenterService.java b/hazelcast/src/main/java/com/hazelcast/internal/management/ManagementCenterService.java index 073f4e2ec3e3..9e25aa5a513a 100644 --- a/hazelcast/src/main/java/com/hazelcast/internal/management/ManagementCenterService.java +++ b/hazelcast/src/main/java/com/hazelcast/internal/management/ManagementCenterService.java @@ -54,6 +54,7 @@ import com.hazelcast.nio.Address; import com.hazelcast.nio.IOUtil; import com.hazelcast.spi.ExecutionService; +import com.hazelcast.spi.InternalCompletableFuture; import com.hazelcast.spi.Operation; import com.hazelcast.spi.OperationService; import com.hazelcast.util.Clock; @@ -247,24 +248,26 @@ private void interruptThread(Thread thread) { } } - public Object callOnAddress(Address address, Operation operation) { + public InternalCompletableFuture callOnAddress(Address address, Operation operation) { // TODO: why are we always executing on the MapService? OperationService operationService = instance.node.nodeEngine.getOperationService(); - Future future = operationService.invokeOnTarget(MapService.SERVICE_NAME, operation, address); - try { - return future.get(); - } catch (Throwable t) { - return ExceptionUtil.toString(t); - } + return operationService.invokeOnTarget(MapService.SERVICE_NAME, operation, address); } - public Object callOnThis(Operation operation) { + public InternalCompletableFuture callOnThis(Operation operation) { return callOnAddress(instance.node.getThisAddress(), operation); } - public Object callOnMember(Member member, Operation operation) { - Address address = member.getAddress(); - return callOnAddress(address, operation); + public InternalCompletableFuture callOnMember(Member member, Operation operation) { + return callOnAddress(member.getAddress(), operation); + } + + public static Object resolveFuture(Future future) { + try { + return future.get(); + } catch (Throwable t) { + return ExceptionUtil.toString(t); + } } public void send(Address address, Operation operation) { @@ -637,8 +640,9 @@ public void memberAdded(MembershipEvent membershipEvent) { try { Member member = membershipEvent.getMember(); if (member != null && instance.node.isMaster() && urlChanged) { - Operation operation = new UpdateManagementCenterUrlOperation(managementCenterUrl); - callOnMember(member, operation); + UpdateManagementCenterUrlOperation operation + = new UpdateManagementCenterUrlOperation(managementCenterUrl); + resolveFuture(callOnMember(member, operation)); } } catch (Exception e) { logger.warning("Web server url cannot be send to the newly joined member", e); diff --git a/hazelcast/src/main/java/com/hazelcast/internal/management/operation/ScriptExecutorOperation.java b/hazelcast/src/main/java/com/hazelcast/internal/management/operation/ScriptExecutorOperation.java index 1f8edfff399f..cc7fc3371a49 100644 --- a/hazelcast/src/main/java/com/hazelcast/internal/management/operation/ScriptExecutorOperation.java +++ b/hazelcast/src/main/java/com/hazelcast/internal/management/operation/ScriptExecutorOperation.java @@ -16,18 +16,18 @@ package com.hazelcast.internal.management.operation; +import com.hazelcast.core.HazelcastException; import com.hazelcast.internal.management.ManagementDataSerializerHook; import com.hazelcast.internal.management.ScriptEngineManagerContext; import com.hazelcast.nio.ObjectDataInput; import com.hazelcast.nio.ObjectDataOutput; +import com.hazelcast.util.ExceptionUtil; + +import java.io.IOException; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; /** * Operation to execute script on the node. @@ -36,37 +36,29 @@ public class ScriptExecutorOperation extends AbstractManagementOperation { private String engineName; private String script; - private Map bindings; private Object result; @SuppressWarnings("unused") public ScriptExecutorOperation() { } - public ScriptExecutorOperation(String engineName, String script, Map bindings) { + public ScriptExecutorOperation(String engineName, String script) { this.engineName = engineName; this.script = script; - this.bindings = bindings; } @Override - public void run() throws Exception { + public void run() { ScriptEngineManager scriptEngineManager = ScriptEngineManagerContext.getScriptEngineManager(); ScriptEngine engine = scriptEngineManager.getEngineByName(engineName); if (engine == null) { throw new IllegalArgumentException("Could not find ScriptEngine named '" + engineName + "'."); } engine.put("hazelcast", getNodeEngine().getHazelcastInstance()); - if (bindings != null) { - Set> entries = bindings.entrySet(); - for (Map.Entry entry : entries) { - engine.put(entry.getKey(), entry.getValue()); - } - } try { this.result = engine.eval(script); } catch (ScriptException e) { - this.result = e.getMessage(); + throw new HazelcastException(ExceptionUtil.toString(e)); } } @@ -79,31 +71,16 @@ public Object getResponse() { protected void writeInternal(ObjectDataOutput out) throws IOException { out.writeUTF(engineName); out.writeUTF(script); - if (bindings != null) { - out.writeInt(bindings.size()); - Set> entries = bindings.entrySet(); - for (Map.Entry entry : entries) { - out.writeUTF(entry.getKey()); - out.writeObject(entry.getValue()); - } - } else { - out.writeInt(0); - } + // kept for compatibility + out.writeInt(0); } @Override protected void readInternal(ObjectDataInput in) throws IOException { engineName = in.readUTF(); script = in.readUTF(); - int size = in.readInt(); - if (size > 0) { - bindings = new HashMap(size); - for (int i = 0; i < size; i++) { - String key = in.readUTF(); - Object value = in.readObject(); - bindings.put(key, value); - } - } + // kept for compatibility + in.readInt(); } @Override diff --git a/hazelcast/src/main/java/com/hazelcast/internal/management/request/ChangeWanStateRequest.java b/hazelcast/src/main/java/com/hazelcast/internal/management/request/ChangeWanStateRequest.java index b4e82d41f3bd..8bc737bf0de6 100644 --- a/hazelcast/src/main/java/com/hazelcast/internal/management/request/ChangeWanStateRequest.java +++ b/hazelcast/src/main/java/com/hazelcast/internal/management/request/ChangeWanStateRequest.java @@ -20,6 +20,7 @@ import com.hazelcast.internal.management.ManagementCenterService; import com.hazelcast.internal.management.operation.ChangeWanStateOperation; +import static com.hazelcast.internal.management.ManagementCenterService.resolveFuture; import static com.hazelcast.util.JsonUtil.getBoolean; import static com.hazelcast.util.JsonUtil.getString; @@ -52,8 +53,9 @@ public int getType() { } @Override - public void writeResponse(ManagementCenterService mcs, JsonObject out) throws Exception { - Object operationResult = mcs.callOnThis(new ChangeWanStateOperation(schemeName, publisherName, start)); + public void writeResponse(ManagementCenterService mcs, JsonObject out) { + Object operationResult = resolveFuture( + mcs.callOnThis(new ChangeWanStateOperation(schemeName, publisherName, start))); JsonObject result = new JsonObject(); if (operationResult == null) { result.add("result", SUCCESS); diff --git a/hazelcast/src/main/java/com/hazelcast/internal/management/request/ClearWanQueuesRequest.java b/hazelcast/src/main/java/com/hazelcast/internal/management/request/ClearWanQueuesRequest.java index 1496c8db8197..f4ce8df2a8c4 100644 --- a/hazelcast/src/main/java/com/hazelcast/internal/management/request/ClearWanQueuesRequest.java +++ b/hazelcast/src/main/java/com/hazelcast/internal/management/request/ClearWanQueuesRequest.java @@ -20,6 +20,7 @@ import com.hazelcast.internal.management.ManagementCenterService; import com.hazelcast.internal.management.operation.ClearWanQueuesOperation; +import static com.hazelcast.internal.management.ManagementCenterService.resolveFuture; import static com.hazelcast.util.JsonUtil.getString; /** @@ -49,9 +50,9 @@ public int getType() { } @Override - public void writeResponse(ManagementCenterService mcs, JsonObject out) throws Exception { + public void writeResponse(ManagementCenterService mcs, JsonObject out) { ClearWanQueuesOperation operation = new ClearWanQueuesOperation(schemeName, publisherName); - Object operationResult = mcs.callOnThis(operation); + Object operationResult = resolveFuture(mcs.callOnThis(operation)); JsonObject result = new JsonObject(); if (operationResult == null) { result.add("result", SUCCESS); diff --git a/hazelcast/src/main/java/com/hazelcast/internal/management/request/ExecuteScriptRequest.java b/hazelcast/src/main/java/com/hazelcast/internal/management/request/ExecuteScriptRequest.java index bdc90d684bc3..0421e3498786 100644 --- a/hazelcast/src/main/java/com/hazelcast/internal/management/request/ExecuteScriptRequest.java +++ b/hazelcast/src/main/java/com/hazelcast/internal/management/request/ExecuteScriptRequest.java @@ -19,21 +19,21 @@ import com.eclipsesource.json.JsonArray; import com.eclipsesource.json.JsonObject; import com.eclipsesource.json.JsonValue; -import com.hazelcast.core.Member; import com.hazelcast.internal.management.ManagementCenterService; import com.hazelcast.internal.management.operation.ScriptExecutorOperation; import com.hazelcast.nio.Address; import com.hazelcast.util.AddressUtil; +import com.hazelcast.util.ExceptionUtil; +import com.hazelcast.util.MapUtil; -import java.util.ArrayList; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; import static com.hazelcast.util.JsonUtil.getArray; -import static com.hazelcast.util.JsonUtil.getBoolean; import static com.hazelcast.util.JsonUtil.getString; /** @@ -44,26 +44,14 @@ public class ExecuteScriptRequest implements ConsoleRequest { private String script; private String engine; private Set targets; - private boolean targetAllMembers; - private Map bindings; public ExecuteScriptRequest() { } - public ExecuteScriptRequest(String script, String engine, boolean targetAllMembers, Map bindings) { + public ExecuteScriptRequest(String script, String engine, Set targets) { this.script = script; this.engine = engine; - this.targets = new HashSet(0); - this.targetAllMembers = targetAllMembers; - this.bindings = bindings; - } - - public ExecuteScriptRequest(String script, String engine, Set targets, Map bindings) { - this.script = script; this.targets = targets; - this.engine = engine; - this.targetAllMembers = false; - this.bindings = bindings; } @Override @@ -73,47 +61,54 @@ public int getType() { @Override public void writeResponse(ManagementCenterService mcs, JsonObject root) throws Exception { - JsonObject jsonResult = new JsonObject(); - ArrayList results; - if (targetAllMembers) { - Set members = mcs.getHazelcastInstance().getCluster().getMembers(); - ArrayList list = new ArrayList(members.size()); - for (Member member : members) { - list.add(mcs.callOnMember(member, new ScriptExecutorOperation(engine, script, bindings))); - } - results = list; - } else { - ArrayList list = new ArrayList(targets.size()); - for (String address : targets) { - AddressUtil.AddressHolder addressHolder = AddressUtil.getAddressHolder(address); - Address targetAddress = new Address(addressHolder.getAddress(), addressHolder.getPort()); - list.add(mcs.callOnAddress(targetAddress, new ScriptExecutorOperation(engine, script, bindings))); + Map> futures = MapUtil.createHashMap(targets.size()); + + for (String address : targets) { + AddressUtil.AddressHolder addressHolder = AddressUtil.getAddressHolder(address); + Address targetAddress = new Address(addressHolder.getAddress(), addressHolder.getPort()); + futures.put(targetAddress, mcs.callOnAddress(targetAddress, new ScriptExecutorOperation(engine, script))); + } + + JsonObject responseJson = new JsonObject(); + StringBuilder scriptResult = new StringBuilder(); + for (Map.Entry> entry : futures.entrySet()) { + Address address = entry.getKey(); + Future future = entry.getValue(); + + try { + addSuccessResponse(responseJson, scriptResult, address, prettyPrint(future.get())); + } catch (ExecutionException e) { + addErrorResponse(responseJson, scriptResult, address, e); + } catch (InterruptedException e) { + addErrorResponse(responseJson, scriptResult, address, e); + Thread.currentThread().interrupt(); } - results = list; } + responseJson.add("scriptResult", scriptResult.toString()); + root.add("result", responseJson); + } + + private String prettyPrint(Object result) { StringBuilder sb = new StringBuilder(); - for (Object result : results) { - if (result instanceof String) { - sb.append(result); - } else if (result instanceof List) { - List list = (List) result; - for (Object o : list) { - sb.append(o).append("\n"); - } - } else if (result instanceof Map) { - Map map = (Map) result; - for (Object o : map.entrySet()) { - Map.Entry entry = (Map.Entry) o; - sb.append(entry.getKey()).append("->").append(entry.getValue()).append("\n"); - } - } else if (result == null) { - sb.append("error"); + if (result instanceof String) { + sb.append(result); + } else if (result instanceof List) { + List list = (List) result; + for (Object o : list) { + sb.append(o).append("\n"); } - sb.append("\n"); + } else if (result instanceof Map) { + Map map = (Map) result; + for (Object o : map.entrySet()) { + Map.Entry e = (Map.Entry) o; + sb.append(e.getKey()).append("->").append(e.getValue()).append("\n"); + } + } else if (result == null) { + sb.append("error"); } - jsonResult.add("scriptResult", sb.toString()); - root.add("result", jsonResult); + sb.append("\n"); + return sb.toString(); } @Override @@ -124,7 +119,27 @@ public void fromJson(JsonObject json) { for (JsonValue target : getArray(json, "targets", new JsonArray())) { targets.add(target.asString()); } - targetAllMembers = getBoolean(json, "targetAllMembers", false); - bindings = new HashMap(); + } + + private static void addSuccessResponse(JsonObject root, StringBuilder scriptResult, + Address address, String result) { + + addResponse(root, scriptResult, address, true, result); + } + + private static void addErrorResponse(JsonObject root, StringBuilder scriptResult, + Address address, Exception e) { + addResponse(root, scriptResult, address, false, ExceptionUtil.toString(e)); + } + + private static void addResponse(JsonObject root, StringBuilder scriptResult, + Address address, boolean success, String result) { + + JsonObject json = new JsonObject(); + json.add("success", success); + json.add("result", result); + root.add(address.toString(), json); + + scriptResult.append(result); } } diff --git a/hazelcast/src/main/java/com/hazelcast/internal/management/request/MapConfigRequest.java b/hazelcast/src/main/java/com/hazelcast/internal/management/request/MapConfigRequest.java index da8e5aabe891..5e2e52a955ac 100644 --- a/hazelcast/src/main/java/com/hazelcast/internal/management/request/MapConfigRequest.java +++ b/hazelcast/src/main/java/com/hazelcast/internal/management/request/MapConfigRequest.java @@ -26,6 +26,7 @@ import java.util.Set; +import static com.hazelcast.internal.management.ManagementCenterService.resolveFuture; import static com.hazelcast.util.JsonUtil.getBoolean; import static com.hazelcast.util.JsonUtil.getObject; import static com.hazelcast.util.JsonUtil.getString; @@ -60,11 +61,11 @@ public void writeResponse(ManagementCenterService mcs, JsonObject root) { if (update) { final Set members = mcs.getHazelcastInstance().getCluster().getMembers(); for (Member member : members) { - mcs.callOnMember(member, new UpdateMapConfigOperation(mapName, config.getMapConfig())); + resolveFuture(mcs.callOnMember(member, new UpdateMapConfigOperation(mapName, config.getMapConfig()))); } result.add("updateResult", "success"); } else { - MapConfig cfg = (MapConfig) mcs.callOnThis(new GetMapConfigOperation(mapName)); + MapConfig cfg = (MapConfig) resolveFuture(mcs.callOnThis(new GetMapConfigOperation(mapName))); if (cfg != null) { result.add("hasMapConfig", true); result.add("mapConfig", new MapConfigDTO(cfg).toJson()); diff --git a/hazelcast/src/main/java/com/hazelcast/internal/management/request/ThreadDumpRequest.java b/hazelcast/src/main/java/com/hazelcast/internal/management/request/ThreadDumpRequest.java index 88e121cfaab9..e6082c1a8b7f 100644 --- a/hazelcast/src/main/java/com/hazelcast/internal/management/request/ThreadDumpRequest.java +++ b/hazelcast/src/main/java/com/hazelcast/internal/management/request/ThreadDumpRequest.java @@ -19,6 +19,10 @@ import com.eclipsesource.json.JsonObject; import com.hazelcast.internal.management.ManagementCenterService; import com.hazelcast.internal.management.operation.ThreadDumpOperation; +import com.hazelcast.spi.InternalCompletableFuture; +import com.hazelcast.util.ExceptionUtil; + +import java.util.concurrent.ExecutionException; import static com.hazelcast.util.JsonUtil.getBoolean; @@ -44,13 +48,22 @@ public int getType() { @Override public void writeResponse(ManagementCenterService mcs, JsonObject root) { final JsonObject result = new JsonObject(); - String threadDump = (String) mcs.callOnThis(new ThreadDumpOperation(dumpDeadlocks)); - if (threadDump != null) { - result.add("hasDump", true); - result.add("dump", threadDump); - } else { - result.add("hasDump", false); + InternalCompletableFuture future = mcs.callOnThis(new ThreadDumpOperation(dumpDeadlocks)); + try { + String threadDump = (String) future.get(); + if (threadDump != null) { + result.add("hasDump", true); + result.add("dump", threadDump); + } else { + result.add("hasDump", false); + } + } catch (ExecutionException e) { + addError(result, e); + } catch (InterruptedException e) { + addError(result, e); + Thread.currentThread().interrupt(); } + root.add("result", result); } @@ -58,4 +71,9 @@ public void writeResponse(ManagementCenterService mcs, JsonObject root) { public void fromJson(JsonObject json) { dumpDeadlocks = getBoolean(json, "dumpDeadlocks", false); } + + private static void addError(JsonObject root, Exception e) { + root.add("hasDump", false); + root.add("error", ExceptionUtil.toString(e)); + } } diff --git a/hazelcast/src/test/java/com/hazelcast/internal/management/ExecuteScriptRequestTest.java b/hazelcast/src/test/java/com/hazelcast/internal/management/ExecuteScriptRequestTest.java index 9facb5c103e6..6d1a041228c0 100644 --- a/hazelcast/src/test/java/com/hazelcast/internal/management/ExecuteScriptRequestTest.java +++ b/hazelcast/src/test/java/com/hazelcast/internal/management/ExecuteScriptRequestTest.java @@ -17,15 +17,15 @@ package com.hazelcast.internal.management; import com.eclipsesource.json.JsonObject; -import com.hazelcast.core.Cluster; import com.hazelcast.core.HazelcastInstance; +import com.hazelcast.instance.Node; import com.hazelcast.internal.management.request.ExecuteScriptRequest; -import com.hazelcast.nio.Address; import com.hazelcast.test.HazelcastParallelClassRunner; import com.hazelcast.test.HazelcastTestSupport; import com.hazelcast.test.TestHazelcastInstanceFactory; import com.hazelcast.test.annotation.ParallelTest; import com.hazelcast.test.annotation.QuickTest; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -33,21 +33,22 @@ import org.junit.runner.RunWith; import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; +import static com.hazelcast.util.JsonUtil.getBoolean; +import static com.hazelcast.util.JsonUtil.getObject; import static com.hazelcast.util.JsonUtil.getString; +import static java.util.Collections.singleton; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; @RunWith(HazelcastParallelClassRunner.class) @Category({QuickTest.class, ParallelTest.class}) public class ExecuteScriptRequestTest extends HazelcastTestSupport { - private Cluster cluster; private ManagementCenterService managementCenterService; - private Map bindings = new HashMap(); + private String nodeAddressWithBrackets; + private String nodeAddressWithoutBrackets; /** * Zulu 6 and 7 doesn't have Rhino script engine, so this test should be excluded. @@ -61,71 +62,68 @@ public void setUp() { TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(2); HazelcastInstance[] instances = factory.newInstances(); - cluster = instances[0].getCluster(); - managementCenterService = getNode(instances[0]).getManagementCenterService(); - - bindings.put("key", "value"); + Node node = getNode(instances[0]); + nodeAddressWithBrackets = node.getThisAddress().toString(); + nodeAddressWithoutBrackets = node.getThisAddress().getHost() + ":" + node.getThisAddress().getPort(); + managementCenterService = node.getManagementCenterService(); } @Test public void testExecuteScriptRequest() throws Exception { - ExecuteScriptRequest request = new ExecuteScriptRequest("print('test');", "JavaScript", false, bindings); - - JsonObject jsonObject = new JsonObject(); - request.writeResponse(managementCenterService, jsonObject); - - JsonObject result = (JsonObject) jsonObject.get("result"); - String response = getString(result, "scriptResult"); - assertEquals("", response.trim()); - } - - @Test - public void testExecuteScriptRequest_whenTargetAllMembers() throws Exception { - ExecuteScriptRequest request = new ExecuteScriptRequest("print('test');", "JavaScript", true, null); + ExecuteScriptRequest request = new ExecuteScriptRequest("print('test');", "JavaScript", + singleton(nodeAddressWithoutBrackets)); JsonObject jsonObject = new JsonObject(); request.writeResponse(managementCenterService, jsonObject); JsonObject result = (JsonObject) jsonObject.get("result"); - String response = getString(result, "scriptResult"); - assertEquals("error\nerror\n", response); + JsonObject json = getObject(result, nodeAddressWithBrackets); + assertTrue(getBoolean(json, "success")); + assertEquals("error\n", getString(json, "result")); + assertEquals("error\n", getString(result, "scriptResult")); } @Test - public void testExecuteScriptRequest_whenTargetAllMembers_withTarget() throws Exception { - Address address = cluster.getLocalMember().getAddress(); - Set targets = Collections.singleton(address.getHost() + ":" + address.getPort()); - ExecuteScriptRequest request = new ExecuteScriptRequest("print('test');", "JavaScript", targets, bindings); + public void testExecuteScriptRequest_noTargets() throws Exception { + ExecuteScriptRequest request = new ExecuteScriptRequest("print('test');", "JavaScript", + Collections.emptySet()); JsonObject jsonObject = new JsonObject(); request.writeResponse(managementCenterService, jsonObject); JsonObject result = (JsonObject) jsonObject.get("result"); - String response = getString(result, "scriptResult"); - assertEquals("error", response.trim()); + assertEquals("", getString(result, "scriptResult")); } @Test public void testExecuteScriptRequest_withIllegalScriptEngine() throws Exception { - ExecuteScriptRequest request = new ExecuteScriptRequest("script", "engine", true, bindings); + ExecuteScriptRequest request = new ExecuteScriptRequest("script", "engine", + singleton(nodeAddressWithoutBrackets)); JsonObject jsonObject = new JsonObject(); request.writeResponse(managementCenterService, jsonObject); JsonObject result = (JsonObject) jsonObject.get("result"); - String response = getString(result, "scriptResult"); - assertContains(response, "IllegalArgumentException"); + JsonObject json = getObject(result, nodeAddressWithBrackets); + assertFalse(getBoolean(json, "success")); + assertContains(getString(json, "result"), "IllegalArgumentException"); + + assertContains(getString(result, "scriptResult"), "IllegalArgumentException"); } @Test public void testExecuteScriptRequest_withScriptException() throws Exception { - ExecuteScriptRequest request = new ExecuteScriptRequest("print(;", "JavaScript", true, bindings); + ExecuteScriptRequest request = new ExecuteScriptRequest("print(;", "JavaScript", + singleton(nodeAddressWithoutBrackets)); JsonObject jsonObject = new JsonObject(); request.writeResponse(managementCenterService, jsonObject); JsonObject result = (JsonObject) jsonObject.get("result"); - String response = getString(result, "scriptResult"); - assertNotNull(response); + JsonObject json = getObject(result, nodeAddressWithBrackets); + assertFalse(getBoolean(json, "success")); + assertContains(getString(json, "result"), "ScriptException"); + + assertContains(getString(result, "scriptResult"), "ScriptException"); } }