Mercurial > stress-tester
changeset 579:100d4bff9f82
QlikWebsocketClient - working version
author | Devel 1 |
---|---|
date | Fri, 29 Sep 2017 16:35:29 +0200 |
parents | fab4175d7fec |
children | a521874e90ea |
files | stress-tester-qlik/pom.xml stress-tester-qlik/src/main/java/com/passus/st/qlik/Protocol.java stress-tester-qlik/src/main/java/com/passus/st/qlik/QlikWebsocketClient.java stress-tester-qlik/src/test/java/com/passus/st/qlik/DevelEndpoint.java stress-tester-qlik/src/test/java/com/passus/st/qlik/ProtocolTest.java stress-tester-qlik/src/test/java/com/passus/st/qlik/QlikWebsocketClientIT.java |
diffstat | 6 files changed, 290 insertions(+), 9 deletions(-) [+] |
line wrap: on
line diff
--- a/stress-tester-qlik/pom.xml Fri Sep 29 16:04:03 2017 +0200 +++ b/stress-tester-qlik/pom.xml Fri Sep 29 16:35:29 2017 +0200 @@ -34,6 +34,18 @@ <artifactId>json-smart</artifactId> <version>1.3.1</version> </dependency> + + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-api</artifactId> + <version>2.7</version> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-core</artifactId> + <version>2.7</version> + </dependency> + <dependency> <groupId>org.testng</groupId> <artifactId>testng</artifactId>
--- a/stress-tester-qlik/src/main/java/com/passus/st/qlik/Protocol.java Fri Sep 29 16:04:03 2017 +0200 +++ b/stress-tester-qlik/src/main/java/com/passus/st/qlik/Protocol.java Fri Sep 29 16:35:29 2017 +0200 @@ -77,11 +77,11 @@ return (JSONArray) o; } - static Number number(Object o) { + static Integer integer(Object o) { if (!(o instanceof Number)) { throw new IllegalArgumentException(); } - return (Number) o; + return (Integer) o; } static Boolean bool(Object o) { @@ -95,16 +95,16 @@ return jsonObject(JSONValue.parse(json)); } - public static int getID(JSONObject root) { + public static Integer getID(JSONObject root) { Object id = root.get(ID); - return number(id).intValue(); + return id == null ? null : integer(id); } public static int getHandle(JSONObject root) { JSONObject result = jsonObject(root.get(RESULT)); JSONObject retValue = jsonObject(result.get("qReturn")); Object handle = retValue.get("qHandle"); - return number(handle).intValue(); + return integer(handle); } public static boolean getReturnedBoolean(JSONObject root) {
--- a/stress-tester-qlik/src/main/java/com/passus/st/qlik/QlikWebsocketClient.java Fri Sep 29 16:04:03 2017 +0200 +++ b/stress-tester-qlik/src/main/java/com/passus/st/qlik/QlikWebsocketClient.java Fri Sep 29 16:35:29 2017 +0200 @@ -12,7 +12,10 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import net.minidev.json.JSONObject; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.eclipse.jetty.websocket.api.Session; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; import org.eclipse.jetty.websocket.api.annotations.WebSocket; @@ -25,22 +28,31 @@ */ public class QlikWebsocketClient { + private static final Logger LOGGER = LogManager.getLogger(QlikWebsocketClient.class); + @WebSocket(maxTextMessageSize = 64 * 1024) - static class QlikEndpoint { + public static class QlikEndpoint { Map<Integer, CompletableFuture<JSONObject>> futures = new HashMap<>(); private Session session; + @OnWebSocketClose + public void onClose(int statusCode, String reason) { + LOGGER.debug("Connection closed: {} - {}", statusCode, reason); + session = null; + } + @OnWebSocketConnect public void onConnect(Session session) { - System.out.println("connected"); + LOGGER.debug("connected"); this.session = session; } @OnWebSocketMessage public void onMessage(String msg) { + LOGGER.debug(" IN: {}", msg); JSONObject root = Protocol.parseObject(msg); - int id = Protocol.getID(root); + Integer id = Protocol.getID(root); CompletableFuture<JSONObject> future = futures.remove(id); if (future != null) { future.complete(root); @@ -48,6 +60,7 @@ } public void sendMessage(String msg) throws IOException { + LOGGER.debug("OUT: {}", msg); session.getRemote().sendString(msg); } @@ -114,6 +127,18 @@ } public static Future<Boolean> asyncReload(String host, int port, String docName) { - throw new UnsupportedOperationException(); + final CompletableFuture<Boolean> future = new CompletableFuture<>(); + Thread task = new Thread() { + @Override + public void run() { + try { + future.complete(reload(host, port, docName)); + } catch (Exception ex) { + future.completeExceptionally(ex); + } + } + }; + task.start(); + return future; } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stress-tester-qlik/src/test/java/com/passus/st/qlik/DevelEndpoint.java Fri Sep 29 16:35:29 2017 +0200 @@ -0,0 +1,126 @@ +package com.passus.st.qlik; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URLEncoder; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import org.eclipse.jetty.websocket.api.Session; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; +import org.eclipse.jetty.websocket.api.annotations.WebSocket; +import org.eclipse.jetty.websocket.client.ClientUpgradeRequest; +import org.eclipse.jetty.websocket.client.WebSocketClient; + +@WebSocket(maxTextMessageSize = 64 * 1024) +public class DevelEndpoint { + + public static final int PORT = 9076;//4848; // 9076 + + public static final String url1 = "ws://localhost:" + PORT + "/app/" + + "%3Ftransient%3D/identity/8phkqd" + + "?" + + "reloadUri=http%3A%2F%2Flocalhost%3A4848%2Fdev-hub%2Fengine-api-explorer"; + + public static final String url2 = "ws://localhost:" + PORT + "/app/" + + "%3Ftransient%3D" + + "?reloadUri=http%3A%2F%2Flocalhost%3A4848%2Fdev-hub%2Fengine-api-explorer"; + + public static final String url3 = "ws://localhost:" + PORT + "/app/" + + "C%3A%5CUsers%5Cmikolaj.podbielski%5CDocuments%5CQlik%5CSense%5CApps%5CST_final_v1_LB.qvf" + + "?reloadUri=http%3A%2F%2Flocalhost%3A4848%2Fdev-hub%2Fengine-api-explorer"; + + public static final String msgProductVersion = "{\"method\":\"ProductVersion\",\"handle\":-1,\"params\":[],\"jsonrpc\":\"2.0\",\"id\":1}"; + public static final String msgIsPersonalMode = "{\"method\":\"IsPersonalMode\",\"handle\":-1,\"params\":[],\"jsonrpc\":\"2.0\",\"id\":2}"; + + public static final String msgGetDocList = "{\"method\":\"GetDocList\",\"handle\":-1,\"params\":[],\"jsonrpc\":\"2.0\",\"id\":1}"; + + public static final String msgOpenDoc1 = "{\"method\":\"OpenDoc\",\"handle\":-1,\"params\":[\"C:\\\\Users\\\\mikolaj.podbielski\\\\Documents\\\\Qlik\\\\Sense\\\\Apps\\\\ST_final_v1_LB.qvf\",\"\",\"\",\"\",false],\"delta\":false,\"jsonrpc\":\"2.0\",\"id\":1}"; + public static final String msgOpenDoc2 = "{\"method\":\"OpenDoc\",\"handle\":-1,\"params\":[\"C:\\\\Users\\\\mikolaj.podbielski\\\\Documents\\\\Qlik\\\\Sense\\\\Apps\\\\ST_final_v1_LB.qvf\"],\"jsonrpc\":\"2.0\",\"id\":2}"; + public static final String msgDoReload = "{\"method\":\"DoReload\",\"handle\":1,\"params\":{\"qMode\":0,\"qPartial\":false,\"qDebug\":false},\"jsonrpc\":\"2.0\",\"id\":4}"; + + private final Object lock = new Object(); + private Session session; + private String lastMessage; + + @OnWebSocketClose + public void onClose(int statusCode, String reason) { + System.out.printf("Connection closed: %d - %s%n", statusCode, reason); + session = null; + } + + @OnWebSocketConnect + public void onConnect(Session session) { + System.out.println("connected"); + this.session = session; + } + + @OnWebSocketMessage + public void onMessage(String msg) { + System.out.printf(" IN: %s%n", msg); + synchronized (lock) { + lastMessage = msg; + lock.notifyAll(); + } + } + + public String waitForMessage() throws InterruptedException { + synchronized (lock) { + lock.wait(); + return lastMessage; + } + } + + public static void sendMessage(Session session, String msg) throws IOException { + try { + Thread.sleep(300); + } catch (InterruptedException ignore) { + } + System.out.printf("OUT: %s%n", msg); + session.getRemote().sendString(msg); + } + + public void sendMessage(String msg) throws IOException { + try { + Thread.sleep(300); + } catch (InterruptedException ignore) { + } + System.out.printf("OUT: %s%n", msg); + session.getRemote().sendString(msg); + } + + private static Session openSession(WebSocketClient client, String url, Object websocket) + throws IOException, InterruptedException, ExecutionException, URISyntaxException { + ClientUpgradeRequest request = new ClientUpgradeRequest(); + Future<Session> future = client.connect(websocket, new URI(url), request); + Session session = future.get(); + return session; + } + + public static void main(String[] args) throws Exception { + String appId = "C:\\Users\\mikolaj.podbielski\\Documents\\Qlik\\Sense\\Apps\\ST_final_v1_LB.qvf"; + String url = "ws://localhost:" + PORT + "/app/"; + String appUrl = url + URLEncoder.encode(appId, "UTF-8"); + + WebSocketClient client = new WebSocketClient(); + client.start(); + + DevelEndpoint endpoint = new DevelEndpoint(); + + try (Session session2 = openSession(client, url, endpoint)) { + sendMessage(session2, msgGetDocList); + String docListString = endpoint.waitForMessage(); + } + try (Session session3 = openSession(client, appUrl, endpoint)) { + sendMessage(session3, msgOpenDoc2); + sendMessage(session3, msgDoReload); + + String reloadStatus = endpoint.waitForMessage(); +// endpoint.waitForMessage(); + } + + client.stop(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stress-tester-qlik/src/test/java/com/passus/st/qlik/ProtocolTest.java Fri Sep 29 16:35:29 2017 +0200 @@ -0,0 +1,92 @@ +package com.passus.st.qlik; + +import java.util.List; +import net.minidev.json.JSONObject; +import static org.testng.AssertJUnit.*; +import org.testng.annotations.Test; + +/** + * + * @author mikolaj.podbielski + */ +public class ProtocolTest { + + @Test + public void testParseGetDocListResponse() { + String json = "{\n" + + " \"jsonrpc\": \"2.0\",\n" + + " \"id\": 1,\n" + + " \"result\": {\n" + + " \"qDocList\": [\n" + + " {\n" + + " \"qDocName\": \"Sales Discovery.qvf\",\n" + + " \"qConnectedUsers\": 0,\n" + + " \"qFileTime\": 42990.50472222222,\n" + + " \"qFileSize\": 27262976,\n" + + " \"qDocId\": \"C:\\\\Users\\\\x\\\\Documents\\\\Qlik\\\\Sense\\\\Apps\\\\Sales Discovery.qvf\",\n" + + " \"qMeta\": {\n" + + " \"description\": \"This application\",\n" + + " \"dynamicColor\": \"hsla(210,18%,43%,1)\"\n" + + " },\n" + + " \"qLastReloadTime\": \"2016-06-14T14:40:01.222Z\",\n" + + " \"qTitle\": \"Sales Discovery\",\n" + + " \"qThumbnail\": {\n" + + " \"qUrl\": \"/media/4/thumb-sales_discovery.png\"\n" + + " },\n" + + " \"key\": \"C:\\\\Users\\\\x\\\\Documents\\\\Qlik\\\\Sense\\\\Apps\\\\Sales Discovery.qvf\",\n" + + " \"$$hashKey\": \"object:111\"\n" + + " },\n" + + " {\n" + + " \"qDocName\": \"ST_final_v1_LB.qvf\",\n" + + " \"qConnectedUsers\": 0,\n" + + " \"qFileTime\": 43007.4975,\n" + + " \"qFileSize\": 1179648,\n" + + " \"qDocId\": \"C:\\\\Users\\\\x\\\\Documents\\\\Qlik\\\\Sense\\\\Apps\\\\ST_final_v1_LB.qvf\",\n" + + " \"qMeta\": {\n" + + " \"description\": \"\",\n" + + " \"dynamicColor\": \"\"\n" + + " },\n" + + " \"qLastReloadTime\": \"2017-09-29T09:56:22.464Z\",\n" + + " \"qTitle\": \"Passus Stress Tester\",\n" + + " \"qThumbnail\": {\n" + + " \"qUrl\": \"/media/3/snmp.png\"\n" + + " },\n" + + " \"key\": \"C:\\\\Users\\\\x\\\\Documents\\\\Qlik\\\\Sense\\\\Apps\\\\ST_final_v1_LB.qvf\",\n" + + " \"$$hashKey\": \"object:110\"\n" + + " }\n" + + " ]\n" + + " }\n" + + "}"; + + List<Protocol.DocInfo> list = Protocol.parseGetDocListResponse(json); + assertEquals(2, list.size()); + + String expectedId = "C:\\Users\\x\\Documents\\Qlik\\Sense\\Apps\\Sales Discovery.qvf"; + Protocol.DocInfo info = list.get(0); + assertEquals("Sales Discovery.qvf", info.getDocName()); + assertEquals(expectedId, info.getDocId()); + assertEquals("Sales Discovery", info.getTitle()); + assertEquals(expectedId, info.getKey()); + } + + @Test + public void testGetId() { + JSONObject request = Protocol.parseObject("{\"method\":\"SomeRequest\",\"handle\":-1,\"params\":[],\"jsonrpc\":\"2.0\",\"id\":123}"); + assertEquals(123, Protocol.getID(request).intValue()); + + JSONObject response = Protocol.parseObject("{\"jsonrpc\":\"2.0\",\"id\":125,\"result\":{\"qReturn\":{\"qType\":\"Doc\",\"qHandle\":3,\"qGenericId\":\"qwerty\"}}}"); + assertEquals(125, Protocol.getID(response).intValue()); + } + + @Test + public void testGetHandle() { + JSONObject response = Protocol.parseObject("{\"jsonrpc\":\"2.0\",\"id\":125,\"result\":{\"qReturn\":{\"qType\":\"Doc\",\"qHandle\":3,\"qGenericId\":\"qwerty\"}}}"); + assertEquals(3, Protocol.getHandle(response)); + } + + @Test + public void testGetBooleanResult() { + JSONObject response = Protocol.parseObject("{\"jsonrpc\":\"2.0\",\"id\":4,\"result\":{\"qReturn\":true},\"change\":[1]}"); + assertEquals(true, Protocol.getReturnedBoolean(response)); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/stress-tester-qlik/src/test/java/com/passus/st/qlik/QlikWebsocketClientIT.java Fri Sep 29 16:35:29 2017 +0200 @@ -0,0 +1,26 @@ +package com.passus.st.qlik; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import static org.testng.Assert.*; +import org.testng.annotations.Test; + +/** + * + * @author mikolaj.podbielski + */ +public class QlikWebsocketClientIT { + + @Test + public void testReload() throws Exception { + boolean reload = QlikWebsocketClient.reload("localhost", 9076, "ST_final_v1_LB.qvf"); + assertTrue(reload); + } + + @Test + public void testAsyncReload() throws InterruptedException, ExecutionException { + Future<Boolean> asyncReload = QlikWebsocketClient.asyncReload("localhost", 9076, "ST_final_v1_LB.qvf"); + assertTrue(asyncReload.get()); + } + +}