1. MBeanCollection Bulk operation via objectname query jmx-rest-api
authorhb
Fri, 05 Jan 2018 13:42:53 +0530
branchjmx-rest-api
changeset 56007 d6cbabcaf518
parent 56006 352a4f213fc6
child 56026 bd531f08d7c7
1. MBeanCollection Bulk operation via objectname query 2. All MBeans identified by getCanonicalName instead of toString
src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/http/HttpUtil.java
src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/http/MBeanCollectionResource.java
src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/http/MBeanResource.java
src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/json/JSONObject.java
test/jdk/javax/management/remote/rest/RestAdapterPerfTest.java
test/jdk/javax/management/remote/rest/RestAdapterPerformanceTest.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 <T> List<T> filterByPage(HttpExchange exchange, List<T> input, int pageSize) throws UnsupportedEncodingException {
-        if (input.size() <= pageSize) {
-            return input;
-        }
+
         Map<String, String> 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) {
--- 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<ObjectName> allowedMbeans;
     private final MBeanServer mBeanServer;
-    private final Map<String, MBeanResource> mBeanResourceMap = new ConcurrentHashMap<>();
+    private final Map<ObjectName, MBeanResource> 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<Map<String, String>> items = new ArrayList<>(filteredMBeans.size());
             for (ObjectName objectName : mbeanPage) {
                 Map<String, String> 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<String> objectNamePatterns = jsonObject.keySet()
+                        .stream()
+                        .filter(a -> a.startsWith("?"))
+                        .collect(Collectors.toSet());
+
+                if (!objectNamePatterns.isEmpty()) {
+                    for (String pattern : objectNamePatterns) {
+                        Set<ObjectName> 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;
+    }
 }
--- 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());
 
--- 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.
     }
--- 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<Runnable> 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();
-    }
-}
--- /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<Runnable> 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();
+    }
+}