Bulk operation at MBeanServer level jmx-rest-api
authorhb
Wed, 27 Dec 2017 16:05:53 +0530
branchjmx-rest-api
changeset 55996 e8d4ccaf6877
parent 55995 a798bdd52997
child 55997 f881344569d9
Bulk operation at MBeanServer level
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
--- a/src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/http/MBeanCollectionResource.java	Wed Dec 27 14:44:48 2017 +0530
+++ b/src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/http/MBeanCollectionResource.java	Wed Dec 27 16:05:53 2017 +0530
@@ -2,6 +2,8 @@
 
 import com.oracle.jmx.remote.rest.json.JSONElement;
 import com.oracle.jmx.remote.rest.json.JSONObject;
+import com.oracle.jmx.remote.rest.json.parser.JSONParser;
+import com.oracle.jmx.remote.rest.json.parser.ParseException;
 import com.oracle.jmx.remote.rest.mapper.JSONMapper;
 import com.oracle.jmx.remote.rest.mapper.JSONMappingException;
 import com.oracle.jmx.remote.rest.mapper.JSONMappingFactory;
@@ -11,10 +13,10 @@
 import javax.management.remote.rest.PlatformRestAdapter;
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
+import java.net.HttpURLConnection;
 import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.CopyOnWriteArraySet;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -24,6 +26,7 @@
     private final MBeanServer mBeanServer;
     private final Map<String, MBeanResource> mBeanResourceMap = new ConcurrentHashMap<>();
     private static final int pageSize = 10;
+    private static final String pathPrefix = "^/?jmx/servers/[a-zA-Z0-9\\-\\.]+/mbeans";
 
     private boolean isMBeanAllowed(ObjectName objName) {
         try {
@@ -92,7 +95,6 @@
     @Override
     public void handle(HttpExchange exchange) throws IOException {
         String path = exchange.getRequestURI().getPath();
-        String pathPrefix = "^/?jmx/servers/[a-zA-Z0-9\\-\\.]+/mbeans";
         if (path.matches(pathPrefix + "/?$")) {
             RestResource.super.handle(exchange);
         } else if (path.matches(pathPrefix + "/[^/]+/?.*")) {
@@ -125,8 +127,8 @@
         try {
             List<ObjectName> mbeans = allowedMbeans;
             Map<String, String> queryMap = HttpUtil.getGetRequestQueryMap(exchange);
-            if(queryMap.containsKey("query")) {
-                Set<ObjectName> queryMBeans = mBeanServer.queryNames(new ObjectName(queryMap.get("query")),null);
+            if (queryMap.containsKey("query")) {
+                Set<ObjectName> queryMBeans = mBeanServer.queryNames(new ObjectName(queryMap.get("query")), null);
                 queryMBeans.retainAll(allowedMbeans);
                 mbeans = new ArrayList<>(queryMBeans);
             }
@@ -153,8 +155,8 @@
             JSONElement linkElem = typeMapper1.toJsonValue(items);
             JSONElement propElem = typeMapper2.toJsonValue(properties);
             JSONObject jobj = new JSONObject();
-            if(_links != null && !_links.isEmpty()) {
-                jobj.put("_links",_links);
+            if (_links != null && !_links.isEmpty()) {
+                jobj.put("_links", _links);
             }
 
             jobj.putAll((JSONObject) propElem);
@@ -177,7 +179,50 @@
 
     @Override
     public HttpResponse doPost(HttpExchange exchange) {
-        return null;
+        String path = exchange.getRequestURI().getPath();
+        String reqBody = null;
+        try {
+            if (path.matches(pathPrefix + "/?$")) { // POST to current URL
+                reqBody = HttpUtil.readRequestBody(exchange);
+                if (reqBody == null || reqBody.isEmpty()) { // No Parameters
+                    return HttpResponse.BAD_REQUEST;
+                }
+
+                JSONParser parser = new JSONParser(reqBody);
+                JSONElement jsonElement = parser.parse();
+                if (!(jsonElement instanceof JSONObject)) {
+                    return new HttpResponse(HttpResponse.BAD_REQUEST,
+                            "Invalid parameters : [" + reqBody + "]");
+                }
+
+                JSONObject jsonObject = (JSONObject) jsonElement;
+                JSONObject result = new JSONObject();
+                for (String mBeanName : jsonObject.keySet()) {
+                    MBeanResource mBeanResource = mBeanResourceMap.get(mBeanName);
+                    try {
+                    if (mBeanResource == null) {
+                        result.put(mBeanName, "Invalid MBean");
+                    } else {
+                        JSONElement element = jsonObject.get(mBeanName);
+                        if (element instanceof JSONObject) {
+                            JSONElement res = mBeanResource.handleBulkRequest(exchange, (JSONObject) element);
+                            result.put(mBeanName, res);
+                        } else {
+                            result.put(mBeanName, "Invalid input");
+                        }
+                    }} catch (Throwable e) {
+                        e.printStackTrace();
+                    }
+                }
+                return new HttpResponse(HttpURLConnection.HTTP_OK, result.toJsonString());
+            } else {
+                return HttpResponse.METHOD_NOT_ALLOWED;
+            }
+        } catch (ParseException e) {
+            return new HttpResponse(HttpResponse.BAD_REQUEST, "Invalid JSON String for request body");
+        } catch (IOException e) {
+            return HttpResponse.BAD_REQUEST;
+        }
     }
 
     @Override
--- a/src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/http/MBeanResource.java	Wed Dec 27 14:44:48 2017 +0530
+++ b/src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/http/MBeanResource.java	Wed Dec 27 16:05:53 2017 +0530
@@ -195,26 +195,26 @@
         return jobj2;
     }
 
-    private Map<String, Object> getAttributes(String[] attrs) throws IntrospectionException,
-            InstanceNotFoundException, ReflectionException, AttributeNotFoundException, MBeanException {
+    private Map<String, Object> getAttributes(String[] attrs) throws InstanceNotFoundException,
+            ReflectionException {
         Map<String, Object> result = new LinkedHashMap<>();
-        if (attrs.length == 1) {
-            result.put(attrs[0], mBeanServer.getAttribute(objectName, attrs[0]));
+        if (attrs == null || attrs.length == 0) {
+            return result;
+        }
+        AttributeList attrVals = mBeanServer.getAttributes(objectName, attrs);
+        if (attrVals.size() != attrs.length) {
+            List<String> missingAttrs = new ArrayList<>(Arrays.asList(attrs));
+            for (Attribute a : attrVals.asList()) {
+                missingAttrs.remove(a.getName());
+                result.put(a.getName(), a.getValue());
+            }
+            for (String attr : missingAttrs) {
+                result.put(attr, "< Error: No such attribute >");
+            }
         } else {
-            AttributeList attrVals = mBeanServer.getAttributes(objectName, attrs);
-            if (attrVals.size() != attrs.length) {
-                List<String> missingAttrs = new ArrayList<>(Arrays.asList(attrs));
-                for (Attribute a : attrVals.asList()) {
-                    missingAttrs.remove(a.getName());
-                    result.put(a.getName(), a.getValue());
-                }
-                for (String attr : missingAttrs) {
-                    result.put(attr, "< Error: No such attribute >");
-                }
-            } else {
-                attrVals.asList().forEach((a) -> result.put(a.getName(), a.getValue()));
-            }
+            attrVals.asList().forEach((a) -> result.put(a.getName(), a.getValue()));
         }
+
         return result;
     }
 
@@ -253,8 +253,8 @@
         return result;
     }
 
-    private Map<String, Object> setAttributes(JSONObject attrMap) throws IntrospectionException,
-            InstanceNotFoundException, ReflectionException, JSONDataException, ClassNotFoundException, MBeanException {
+    private Map<String, Object> setAttributes(JSONObject attrMap) throws JSONDataException,
+            IntrospectionException, InstanceNotFoundException, ReflectionException {
         if (attrMap == null || attrMap.isEmpty()) {
             throw new JSONDataException("Null arguments for set attribute");
         }
@@ -291,6 +291,8 @@
                     result.put(attrName, "<Invalid value for the attribute>");
                 } catch (AttributeNotFoundException e) {
                     result.put(attrName, "<Attribute not found>");
+                } catch (ReflectionException | InstanceNotFoundException | MBeanException e) {
+                    result.put(attrName, "<ERROR: Unable to retrieve value>");
                 }
             }
         }
@@ -331,7 +333,7 @@
         if (jsonValues.size() != typeMap.size()) {
             throw new IllegalArgumentException("Invalid parameters : expected - " + typeMap.size() + " parameters, got - " + jsonValues.size());
         }
-        if(!jsonValues.keySet().equals(typeMap.keySet())) {
+        if (!jsonValues.keySet().equals(typeMap.keySet())) {
             throw new IllegalArgumentException("Invalid parameters - expected : " + Arrays.toString(typeMap.keySet().toArray()));
         }
         Map<String, Object> parameters = new LinkedHashMap<>();
@@ -531,7 +533,7 @@
                 JSONObject jsonObject = (JSONObject) jsonElement;
 
                 if (jsonObject.keySet().contains("attributes") | jsonObject.keySet().contains("operations")) {
-                    return handleBulkRequest(exchange, jsonObject);
+                    return new HttpResponse(HttpURLConnection.HTTP_OK, handleBulkRequest(exchange, jsonObject).toJsonString());
                 } else {
                     Map<String, Object> stringObjectMap = setAttributes(jsonObject);
                     JSONMapper typeMapper = JSONMappingFactory.INSTANCE.getTypeMapper(stringObjectMap);
@@ -570,7 +572,7 @@
             }
         } catch (InstanceNotFoundException e) {
             // Should never happen
-        } catch (ClassNotFoundException | JSONDataException | ParseException e) {
+        } catch (JSONDataException | ParseException e) {
             return new HttpResponse(HttpURLConnection.HTTP_BAD_REQUEST, "Invalid JSON : " + reqBody, e.getMessage());
         } catch (IntrospectionException | JSONMappingException | MBeanException | ReflectionException | IOException e) {
             return new HttpResponse(HttpResponse.SERVER_ERROR, HttpResponse.getErrorMessage(e));
@@ -582,96 +584,97 @@
         return HttpResponse.REQUEST_NOT_FOUND;
     }
 
-    private HttpResponse handleBulkRequest(HttpExchange exchange, JSONObject reqObject) {
-        try {
-            JSONObject result = new JSONObject();
+    public JSONElement handleBulkRequest(HttpExchange exchange, JSONObject reqObject) {
+
+        JSONObject result = new JSONObject();
 
-            do {
-                // Handle attributes
-                JSONElement element = reqObject.get("attributes");
-                if (element != null && !(element instanceof JSONObject))    // input validation
-                    break;
-                if (element != null && element instanceof JSONObject) {
-                    JSONObject attrInfo = (JSONObject) element;
-                    JSONObject attrNode = new JSONObject();
-                    // atleast one of get/set must be present
-                    if (attrInfo.get("get") == null && attrInfo.get("set") == null)
-                        break;
-                    // Read attributes
-                    JSONElement read = attrInfo.get("get");
-                    if (read != null && !(read instanceof JSONArray))
-                        break;
-                    if (read != null && read instanceof JSONArray) {
-                        JSONArray jattrs = (JSONArray) read;
-                        String[] attributes = getStrings(jattrs);
-                        Map<String, Object> attrRead = getAttributes(attributes);
-                        JSONMapper typeMapper = JSONMappingFactory.INSTANCE.getTypeMapper(attrRead);
-                        JSONElement jAttrRead = typeMapper.toJsonValue(attrRead);
-                        attrNode.put("get", jAttrRead);
-                    }
-
-                    // Write attributes
-                    JSONElement write = attrInfo.get("set");
-
-                    if (write != null && !(write instanceof JSONObject))
-                        break;
-
-                    if (write != null && write instanceof JSONObject) {
-                        JSONObject jattrs = (JSONObject) write;
-                        Map<String, Object> attrMap = setAttributes(jattrs);
-                        JSONMapper typeMapper = JSONMappingFactory.INSTANCE.getTypeMapper(attrMap);
-                        JSONElement jAttrRead = typeMapper.toJsonValue(attrMap);
-                        attrNode.put("set", jAttrRead);
-                    }
-                    result.put("attributes", attrNode);
+        // Handle attributes
+        JSONElement element = reqObject.get("attributes");
+        if (element != null && element instanceof JSONObject) {
+            JSONObject attrInfo = (JSONObject) element;
+            JSONObject attrNode = new JSONObject();
+            // Read attributes
+            JSONElement read = attrInfo.get("get");
+            if (read != null && read instanceof JSONArray) {
+                JSONArray jattrs = (JSONArray) read;
+                JSONElement jAttrRead;
+                Map<String, Object> attrRead = null;
+                try {
+                    String[] attributes = getStrings(jattrs);
+                    attrRead = getAttributes(attributes);
+                    JSONMapper typeMapper = JSONMappingFactory.INSTANCE.getTypeMapper(attrRead);
+                    jAttrRead = typeMapper.toJsonValue(attrRead);
+                } catch (InstanceNotFoundException | ReflectionException | JSONMappingException e) {
+                    jAttrRead = new JSONPrimitive("<ERROR: Unable to retrieve value>");
+                } catch (JSONDataException e) {
+                    jAttrRead = new JSONPrimitive("Invalid JSON : " + read.toJsonString());
                 }
 
-                // Execute operations
-                element = reqObject.get("operations");
-                if (element != null) {
-                    JSONArray operationList;
-                    if (element instanceof JSONPrimitive             // Single no-arg operation
-                            || element instanceof JSONObject) {     // single/mulitple operations
-                        operationList = new JSONArray();
-                        operationList.add(element);
-                    } else if (element instanceof JSONArray) {  // List of no-arg/with-arg operation
-                        operationList = (JSONArray) element;
-                    } else {
-                        // Should never happen
-                        return HttpResponse.BAD_REQUEST;
+                attrNode.put("get", jAttrRead);
+            }
+
+            // Write attributes
+            JSONElement write = attrInfo.get("set");
+            JSONElement jAttrRead;
+            if (write != null && write instanceof JSONObject) {
+                JSONObject jattrs = (JSONObject) write;
+                try {
+                    Map<String, Object> attrMap = setAttributes(jattrs);
+                    JSONMapper typeMapper = JSONMappingFactory.INSTANCE.getTypeMapper(attrMap);
+                    jAttrRead = typeMapper.toJsonValue(attrMap);
+                } catch (JSONDataException ex) {
+                    jAttrRead = new JSONPrimitive("Invalid JSON : " + write.toJsonString());
+                } catch (JSONMappingException | IntrospectionException | InstanceNotFoundException | ReflectionException e) {
+                    jAttrRead = new JSONPrimitive("<ERROR: Unable to retrieve value>");
+                }
+                attrNode.put("set", jAttrRead);
+            }
+            result.put("attributes", attrNode);
+        }
+
+        // Execute operations
+        element = reqObject.get("operations");
+        if (element != null) {
+            JSONArray operationList;
+            if (element instanceof JSONPrimitive             // Single no-arg operation
+                    || element instanceof JSONObject) {     // single/mulitple operations
+                operationList = new JSONArray();
+                operationList.add(element);
+            } else if (element instanceof JSONArray) {  // List of no-arg/with-arg operation
+                operationList = (JSONArray) element;
+            } else {
+                operationList = new JSONArray();
+            }
+            JSONObject opResult = new JSONObject();
+            for (JSONElement elem : operationList) {
+                if (elem instanceof JSONPrimitive
+                        && ((JSONPrimitive) elem).getValue() instanceof String) { // no-arg operation
+                    String opName = (String) ((JSONPrimitive) elem).getValue();
+                    try {
+                        JSONElement obj = execOperation(opName, null);
+                        opResult.put(opName, obj);
+                    } catch (IllegalArgumentException e) {
+                        opResult.put(opName, e.getMessage());
+                    } catch (IntrospectionException | InstanceNotFoundException | MBeanException | ReflectionException e) {
+                        opResult.put(opName, "<ERROR while executing operation>");
                     }
-                    JSONObject opResult = new JSONObject();
-                    for (JSONElement elem : operationList) {
-                        if (elem instanceof JSONPrimitive
-                                && ((JSONPrimitive) elem).getValue() instanceof String) { // no-arg operation
-                            String opName = (String) ((JSONPrimitive) elem).getValue();
-                            JSONElement obj = execOperation(opName, null);
+                } else if (elem instanceof JSONObject) {
+                    Set<String> opNames = ((JSONObject) element).keySet();
+                    for (String opName : opNames) {
+                        try {
+                            JSONElement obj = execOperation(opName, (JSONObject) ((JSONObject) element).get(opName));
                             opResult.put(opName, obj);
-                        } else if (elem instanceof JSONObject) {
-                            Set<String> opNames = ((JSONObject) element).keySet();
-                            for (String opName : opNames) {
-                                JSONElement obj = execOperation(opName, (JSONObject) ((JSONObject) element).get(opName));
-                                opResult.put(opName, obj);
-                            }
+                        } catch (IllegalArgumentException e) {
+                            opResult.put(opName, e.getMessage());
+                        } catch (IntrospectionException | InstanceNotFoundException | MBeanException | ReflectionException e) {
+                            opResult.put(opName, "<ERROR while executing operation>");
                         }
                     }
-                    result.put("operations", opResult);
                 }
-                return new HttpResponse(HttpURLConnection.HTTP_OK, result.toJsonString());
-            } while (false);
-            throw new JSONDataException("Invalid request body : " + reqObject.toJsonString());
-        } catch (InstanceNotFoundException e) {
-            // Should never happen
-        } catch (ClassNotFoundException | JSONDataException e) {
-            return new HttpResponse(HttpResponse.BAD_REQUEST, "Invalid JSON : " + e.getMessage());
-        } catch (IntrospectionException | JSONMappingException | MBeanException | ReflectionException e) {
-            return new HttpResponse(HttpResponse.SERVER_ERROR, HttpResponse.getErrorMessage(e));
-        } catch (IllegalArgumentException e) {
-            return new HttpResponse(HttpResponse.BAD_REQUEST, e.getMessage());
-        } catch (Exception e) {
-            return new HttpResponse(HttpResponse.SERVER_ERROR, HttpResponse.getErrorMessage(e));
+            }
+            result.put("operations", opResult);
         }
-        return HttpResponse.REQUEST_NOT_FOUND;
+        return result;
     }
 
     private String[] getStrings(JSONArray jsonArray) throws JSONDataException {