1. MBeanCollection Bulk operation via objectname query
2. All MBeans identified by getCanonicalName instead of toString
--- 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();
+ }
+}