# HG changeset patch # User hb # Date 1515139973 -19800 # Node ID d6cbabcaf51858c4a34127f8c99de8c1e0af386f # Parent 352a4f213fc629b65ea3f0bfc5285debd1b8f0c5 1. MBeanCollection Bulk operation via objectname query 2. All MBeans identified by getCanonicalName instead of toString diff -r 352a4f213fc6 -r d6cbabcaf518 src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/http/HttpUtil.java --- a/src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/http/HttpUtil.java Thu Jan 04 14:39:04 2018 +0530 +++ b/src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/http/HttpUtil.java Fri Jan 05 13:42:53 2018 +0530 @@ -102,7 +102,7 @@ String[] params = query.trim().split("&"); for (String param : params) { int idx = param.indexOf('='); - if(idx != -1) { + if (idx != -1) { queryParams.put(param.substring(0, idx), param.substring(idx + 1)); } } @@ -116,8 +116,8 @@ int sp = auth.indexOf(' '); byte[] b = Base64.getDecoder().decode(auth.substring(sp + 1)); String authCredentials = new String(b); - int colon = authCredentials.indexOf (':'); - return authCredentials.substring(0,colon); + int colon = authCredentials.indexOf(':'); + return authCredentials.substring(0, colon); } return ""; } @@ -146,7 +146,7 @@ String msg = charset == null ? response.getBody() : URLEncoder.encode(response.getBody(), charset); byte[] bytes = msg.getBytes(); Headers resHeaders = exchange.getResponseHeaders(); - if(charset != null && !charset.isEmpty()) { + if (charset != null && !charset.isEmpty()) { resHeaders.add("Content-Type", "application/json; charset=" + charset); } else { resHeaders.add("Content-Type", "application/json;"); @@ -159,9 +159,7 @@ } public static List filterByPage(HttpExchange exchange, List input, int pageSize) throws UnsupportedEncodingException { - if (input.size() <= pageSize) { - return input; - } + Map queryParams = HttpUtil.getGetRequestQueryMap(exchange); int currPage = 1; if (queryParams != null && !queryParams.isEmpty()) { @@ -171,6 +169,11 @@ currPage = currPage > 1 ? currPage : 1; } } + + if (input.size() <= pageSize) { + return input; + } + int start = (currPage - 1) * pageSize; int end = Math.min(input.size(), start + pageSize); if (start < end) { diff -r 352a4f213fc6 -r d6cbabcaf518 src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/http/MBeanCollectionResource.java --- a/src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/http/MBeanCollectionResource.java Thu Jan 04 14:39:04 2018 +0530 +++ b/src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/http/MBeanCollectionResource.java Fri Jan 05 13:42:53 2018 +0530 @@ -47,12 +47,13 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; public class MBeanCollectionResource implements RestResource, NotificationListener { private List allowedMbeans; private final MBeanServer mBeanServer; - private final Map mBeanResourceMap = new ConcurrentHashMap<>(); + private final Map mBeanResourceMap = new ConcurrentHashMap<>(); private static final int pageSize = 10; private static final String pathPrefix = "^/?jmx/servers/[a-zA-Z0-9\\-\\.]+/mbeans"; @@ -116,8 +117,8 @@ allowedMbeans.add(mBeanName); } } else if (MBeanServerNotification.UNREGISTRATION_NOTIFICATION.equals(mbs.getType())) { - if (allowedMbeans.contains(mbs.getMBeanName().toString())) { - allowedMbeans.remove(mbs.getMBeanName().toString()); + if (allowedMbeans.contains(mbs.getMBeanName())) { + allowedMbeans.remove(mbs.getMBeanName()); } } } catch (Exception e) { @@ -131,13 +132,13 @@ allowedMbeans = new CopyOnWriteArrayList<>(allowedMbeans); // Create a REST handler for each MBean - allowedMbeans.forEach(objectName -> mBeanResourceMap.put(objectName.toString(), + allowedMbeans.forEach(objectName -> mBeanResourceMap.put(objectName, new MBeanResource(mBeanServer, objectName))); } @Override public void handle(HttpExchange exchange) throws IOException { - String path = URLDecoder.decode(exchange.getRequestURI().getPath(), StandardCharsets.UTF_8.displayName()); + String path = URLDecoder.decode(exchange.getRequestURI().getPath(), StandardCharsets.UTF_8.name()); if (path.matches(pathPrefix + "/?$")) { RestResource.super.handle(exchange); @@ -153,12 +154,17 @@ if (ss.indexOf('/') != -1) { mBeanName = ss.substring(0, ss.indexOf('/')); } - MBeanResource mBeanResource = mBeanResourceMap.get(mBeanName); - if (mBeanResource == null) { - HttpUtil.sendResponse(exchange, HttpResponse.REQUEST_NOT_FOUND); - return; + try { + MBeanResource mBeanResource = mBeanResourceMap.get(new ObjectName(mBeanName)); + if (mBeanResource == null) { + HttpUtil.sendResponse(exchange, HttpResponse.REQUEST_NOT_FOUND); + return; + } + mBeanResource.handle(exchange); + } catch (MalformedObjectNameException e) { + HttpUtil.sendResponse(exchange, HttpResponse.BAD_REQUEST); } - mBeanResource.handle(exchange); + } } } @@ -197,8 +203,8 @@ List> items = new ArrayList<>(filteredMBeans.size()); for (ObjectName objectName : mbeanPage) { Map item = new LinkedHashMap<>(); - item.put("name", objectName.toString()); - MBeanResource mBeanResource = mBeanResourceMap.get(objectName.toString()); + item.put("name", objectName.getCanonicalName()); + MBeanResource mBeanResource = mBeanResourceMap.get(objectName); try { JSONObject mBeanInfo = mBeanResource.getMBeanInfo(mBeanServer, objectName); JSONElement element = mBeanInfo.get("descriptor"); @@ -220,12 +226,12 @@ item.put("description", description); } element = mBeanInfo.get("attributeInfo"); - if(element != null) { - item.put("attributeCount", ((JSONArray)element).size() + ""); + if (element != null) { + item.put("attributeCount", ((JSONArray) element).size() + ""); } element = mBeanInfo.get("operationInfo"); - if(element != null) { - item.put("operationCount", ((JSONArray)element).size() + ""); + if (element != null) { + item.put("operationCount", ((JSONArray) element).size() + ""); } } catch (InstanceNotFoundException | IntrospectionException | ReflectionException e) { @@ -285,9 +291,48 @@ } JSONObject jsonObject = (JSONObject) jsonElement; + JSONObject normalizedObject = new JSONObject(jsonObject); + + // Normalize the input MBean names + for (String objectNameString : jsonObject.keySet()) { + if (!objectNameString.startsWith("?")) { // Ignore object name patterns + JSONElement element = jsonObject.get(objectNameString); + normalizedObject.remove(objectNameString); + normalizedObject.put(new ObjectName(objectNameString).getCanonicalName(), element); + } + } + + jsonObject.clear(); + jsonObject = normalizedObject; + + Set objectNamePatterns = jsonObject.keySet() + .stream() + .filter(a -> a.startsWith("?")) + .collect(Collectors.toSet()); + + if (!objectNamePatterns.isEmpty()) { + for (String pattern : objectNamePatterns) { + Set queryMBeans = mBeanServer + .queryNames(new ObjectName(pattern.substring(1)), null); + queryMBeans.retainAll(allowedMbeans); + JSONElement patternNode = jsonObject.get(pattern); + jsonObject.remove(pattern); + for (ObjectName queryMBean : queryMBeans) { + String name = queryMBean.getCanonicalName(); + if (jsonObject.containsKey(name)) { + JSONObject obj = new JSONObject(); + obj.put(name, patternNode); + deepMerge(jsonObject, obj); + } else { + jsonObject.put(name, patternNode); + } + } + } + } + JSONObject result = new JSONObject(); for (String mBeanName : jsonObject.keySet()) { - MBeanResource mBeanResource = mBeanResourceMap.get(mBeanName); + MBeanResource mBeanResource = mBeanResourceMap.get(new ObjectName(mBeanName)); if (mBeanResource != null) { JSONElement element = jsonObject.get(mBeanName); if (element instanceof JSONObject) { @@ -309,6 +354,36 @@ return new HttpResponse(HttpResponse.BAD_REQUEST, "Invalid JSON String for request body"); } catch (IOException e) { return HttpResponse.BAD_REQUEST; + } catch (MalformedObjectNameException e) { + return new HttpResponse(HttpResponse.BAD_REQUEST, "Invalid query string"); } } + + private JSONObject deepMerge(JSONObject jsonObject1, JSONObject jsonObject2) { + for (String key : jsonObject2.keySet()) { + if (jsonObject1.containsKey(key)) { + if (jsonObject2.get(key) instanceof JSONObject && jsonObject1.get(key) instanceof JSONObject) { + JSONObject jobj1 = (JSONObject) jsonObject1.get(key); + JSONObject jobj2 = (JSONObject) jsonObject2.get(key); + jsonObject1.put(key, deepMerge(jobj1, jobj2)); + } else if (jsonObject2.get(key) instanceof JSONArray && jsonObject1.get(key) instanceof JSONArray) { + JSONArray array1 = (JSONArray) jsonObject1.get(key); + JSONArray array2 = (JSONArray) jsonObject2.get(key); + for (JSONElement each : array2) { + if (!array1.contains(each)) { + array1.add(each); + } + } + } else { + JSONArray array = new JSONArray(); + array.add(jsonObject1.get(key)); + array.add(jsonObject2.get(key)); + jsonObject1.put(key, array); + } + } else { + jsonObject1.put(key, jsonObject2.get(key)); + } + } + return jsonObject1; + } } diff -r 352a4f213fc6 -r d6cbabcaf518 src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/http/MBeanResource.java --- a/src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/http/MBeanResource.java Thu Jan 04 14:39:04 2018 +0530 +++ b/src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/http/MBeanResource.java Fri Jan 05 13:42:53 2018 +0530 @@ -365,7 +365,7 @@ if (mBeanInfo == null) { return jobj; } - jobj.put("name", mbean.toString()); + jobj.put("name", mbean.getCanonicalName()); jobj.put("className", mBeanInfo.getClassName()); jobj.put("description", mBeanInfo.getDescription()); diff -r 352a4f213fc6 -r d6cbabcaf518 src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/json/JSONObject.java --- a/src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/json/JSONObject.java Thu Jan 04 14:39:04 2018 +0530 +++ b/src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/json/JSONObject.java Fri Jan 05 13:42:53 2018 +0530 @@ -34,6 +34,14 @@ private static final long serialVersionUID = -9148596129640441014L; + public JSONObject() { + super(); + } + + public JSONObject(JSONObject jsonObject) { + super(jsonObject); + } + public JSONElement put(String key, String value) { return super.put(key, new JSONPrimitive(value)); //To change body of generated methods, choose Tools | Templates. } diff -r 352a4f213fc6 -r d6cbabcaf518 test/jdk/javax/management/remote/rest/RestAdapterPerfTest.java --- a/test/jdk/javax/management/remote/rest/RestAdapterPerfTest.java Thu Jan 04 14:39:04 2018 +0530 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,60 +0,0 @@ - -/* @test - * @summary Performance test for rest adapter - * @library /test/lib - * @modules java.management.rest/com.oracle.jmx.remote.rest.http - * java.management.rest/com.oracle.jmx.remote.rest.json - * java.management.rest/com.oracle.jmx.remote.rest.json.parser - * java.management.rest/com.oracle.jmx.remote.rest.mapper - * @build RestAdapterPerfTest RestAdapterTest - * @run testng/othervm RestAdapterPerfTest - */ - -import java.lang.reflect.InvocationTargetException; -import jdk.test.lib.Utils; -import java.util.List; -import java.util.Random; -import java.util.concurrent.Executors; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.testng.annotations.Test; - -@Test -public class RestAdapterPerfTest { - - private static Random random = Utils.getRandomInstance(); - private static AtomicInteger count = new AtomicInteger(1); - - @Test - public void testMultipleClients() throws Exception { - RestAdapterTest test = new RestAdapterTest(); - List tasks = Stream.of(RestAdapterTest.class.getMethods()) - .filter(m -> m.getName().startsWith("test")).map(m -> (Runnable)() -> { - try { - m.invoke(test); - } catch (IllegalAccessException e) { - } catch (InvocationTargetException e) { - } - }).collect(Collectors.toList()); - - ThreadPoolExecutor es = (ThreadPoolExecutor) Executors.newFixedThreadPool(20); - es.setThreadFactory((Runnable R) -> new Thread(R, "perf-" + count.getAndIncrement())); - long current = System.currentTimeMillis(); - test.setupServers(); - for (int i = 0; i < 200; i++) { - Runnable task = tasks.get(random.nextInt(tasks.size())); - es.execute(task); - } - - System.out.println("Submitted 200 tasks"); - es.shutdown(); - es.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS); - float v = (float) (System.currentTimeMillis() - current) / (float) 1000; - System.out.println("Total time = " + v + "s"); - test.tearDownServers(); - } -} diff -r 352a4f213fc6 -r d6cbabcaf518 test/jdk/javax/management/remote/rest/RestAdapterPerformanceTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/javax/management/remote/rest/RestAdapterPerformanceTest.java Fri Jan 05 13:42:53 2018 +0530 @@ -0,0 +1,51 @@ +import org.testng.annotations.Test; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.Set; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Test +public class RestAdapterPerformanceTest { + + private static Random random = new Random(System.currentTimeMillis()); + private static AtomicInteger count = new AtomicInteger(1); + + @Test + public void testMultipleClients() throws Exception { + RestAdapterTest test = new RestAdapterTest(); + List tasks = Stream.of(RestAdapterTest.class.getMethods()) + .filter(m -> m.getName().startsWith("test")).map(m -> (Runnable)() -> { + try { + m.invoke(test); + } catch (IllegalAccessException e) { + } catch (InvocationTargetException e) { + } + }).collect(Collectors.toList()); + + ThreadPoolExecutor es = (ThreadPoolExecutor) Executors.newFixedThreadPool(20); + es.setThreadFactory((Runnable R) -> new Thread(R, "perf-" + count.getAndIncrement())); + long current = System.currentTimeMillis(); + test.setupServers(); + for (int i = 0; i < 200; i++) { + Runnable task = tasks.get(random.nextInt(tasks.size())); + es.execute(task); + } + + System.out.println("Submitted 200 tasks"); + es.shutdown(); + es.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS); + float v = (float) (System.currentTimeMillis() - current) / (float) 1000; + System.out.println("Total time = " + v + "s"); + test.tearDownServers(); + } +}