POST : Attribute update - working jmx-rest-api
authorhb
Wed, 27 Dec 2017 14:44:48 +0530
branchjmx-rest-api
changeset 55995 a798bdd52997
parent 55994 9721e36abeb0
child 55996 e8d4ccaf6877
POST : Attribute update - working POST : Bulk operation for MBean - working Exception to HTTP error Mapping
src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/http/HttpResponse.java
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/JmxRestAdapter.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/http/RestResource.java
src/java.management.rest/share/classes/javax/management/remote/rest/PlatformRestAdapter.java
test/jdk/javax/management/remote/rest/RunRestAdapter.java
--- a/src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/http/HttpResponse.java	Mon Dec 25 20:42:05 2017 +0530
+++ b/src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/http/HttpResponse.java	Wed Dec 27 14:44:48 2017 +0530
@@ -31,6 +31,7 @@
     private final int code;
     private final String message;
     private final String detail;
+    private final String body;
 
     public HttpResponse(int code, String message) {
         this(code, message, "");
@@ -40,29 +41,30 @@
         this.code = code;
         this.message = message;
         this.detail = detail;
+
+        if (code != HttpURLConnection.HTTP_OK) {
+            JSONObject jobj = new JSONObject();
+            jobj.put("status", new JSONPrimitive(code));
+            jobj.put("message", new JSONPrimitive(message));
+            if (detail != null && !detail.isEmpty()) {
+                jobj.put("details", new JSONPrimitive(detail));
+            }
+            this.body = jobj.toJsonString();
+        } else {
+            this.body = message;
+        }
     }
 
     public HttpResponse(HttpResponse response, String detail) {
-        this.code = response.code;
-        this.message = response.message;
-        this.detail = detail;
+        this(response.code, response.message, detail);
     }
 
     public int getCode() {
         return code;
     }
 
-    public String getResponse() {
-        if(code != HttpURLConnection.HTTP_OK) {
-            JSONObject jobj = new JSONObject();
-            jobj.put("status",new JSONPrimitive(code));
-            jobj.put("message",new JSONPrimitive(message));
-            if(detail != null && !detail.isEmpty()) {
-                jobj.put("details", new JSONPrimitive(detail));
-            }
-            return jobj.toJsonString();
-        }
-        return message;
+    public String getBody() {
+        return body;
     }
 
     static int getHttpErrorCode(Exception ex) {
--- a/src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/http/HttpUtil.java	Mon Dec 25 20:42:05 2017 +0530
+++ b/src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/http/HttpUtil.java	Wed Dec 27 14:44:48 2017 +0530
@@ -117,7 +117,7 @@
         }
 
         // Set response headers explicitly
-        String msg = charset == null ? response.getResponse() : URLEncoder.encode(response.getResponse(), charset);
+        String msg = charset == null ? response.getBody() : URLEncoder.encode(response.getBody(), charset);
         byte[] bytes = msg.getBytes();
         Headers resHeaders = exchange.getResponseHeaders();
         resHeaders.add("Content-Type", "application/json; charset=" + charset);
--- a/src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/http/JmxRestAdapter.java	Mon Dec 25 20:42:05 2017 +0530
+++ b/src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/http/JmxRestAdapter.java	Wed Dec 27 14:44:48 2017 +0530
@@ -31,9 +31,7 @@
 import java.security.AccessControlContext;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
-import java.util.Arrays;
-import java.util.LinkedHashMap;
-import java.util.Map;
+import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
 
 /**
@@ -165,13 +163,12 @@
         }
 
         String path = exchange.getRequestURI().getPath();
+
         // Route request to appropriate resource
-        if (path.matches("^\\/?jmx\\/servers\\/[a-zA-Z0-9\\\\-\\\\.]+\\/?$")) {
+        if (path.matches("^/?jmx/servers/[a-zA-Z0-9\\-\\.]+/?$")) {
             RestResource.super.handle(exchange);
-        } else if (path.matches("^\\/?jmx\\/servers\\/[a-zA-Z0-9\\-\\.]+\\/mbeans.*")) {
+        } else if (path.matches("^/?jmx/servers/[a-zA-Z0-9\\-\\.]+/mbeans.*")) {
             mbeansRes.handle(exchange);
-//        } else if (path.matches("^\\/?jmx\\/servers\\/[a-zA-Z0-9\\-\\.]+\\/mbeans\\/[^\\/]+\\/?$")) {
-//            mbeansRes.handle(exchange);
         } else {
             HttpUtil.sendResponse(exchange, new HttpResponse(404, "Not found"));
         }
--- a/src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/http/MBeanCollectionResource.java	Mon Dec 25 20:42:05 2017 +0530
+++ b/src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/http/MBeanCollectionResource.java	Wed Dec 27 14:44:48 2017 +0530
@@ -92,12 +92,13 @@
     @Override
     public void handle(HttpExchange exchange) throws IOException {
         String path = exchange.getRequestURI().getPath();
-        if (path.matches("^\\/?jmx\\/servers\\/[a-zA-Z0-9\\-\\.]+\\/mbeans\\/?$")) {
+        String pathPrefix = "^/?jmx/servers/[a-zA-Z0-9\\-\\.]+/mbeans";
+        if (path.matches(pathPrefix + "/?$")) {
             RestResource.super.handle(exchange);
-        } else if (path.matches("^\\/?jmx\\/servers\\/[a-zA-Z0-9\\-\\.]+\\/mbeans\\/[^\\/]+\\/?.*")) {
+        } else if (path.matches(pathPrefix + "/[^/]+/?.*")) {
             // Extract mbean name
             // Forward the request to its corresponding rest resource
-            Pattern mbeans = Pattern.compile("^\\/?jmx\\/servers\\/[a-zA-Z0-9\\-\\.]+\\/mbeans\\/");
+            Pattern mbeans = Pattern.compile(pathPrefix + "/");
             Matcher matcher = mbeans.matcher(path);
 
             if (matcher.find()) {
--- a/src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/http/MBeanResource.java	Mon Dec 25 20:42:05 2017 +0530
+++ b/src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/http/MBeanResource.java	Wed Dec 27 14:44:48 2017 +0530
@@ -4,15 +4,24 @@
 import com.oracle.jmx.remote.rest.json.JSONElement;
 import com.oracle.jmx.remote.rest.json.JSONObject;
 import com.oracle.jmx.remote.rest.json.JSONPrimitive;
+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.JSONDataException;
 import com.oracle.jmx.remote.rest.mapper.JSONMapper;
 import com.oracle.jmx.remote.rest.mapper.JSONMappingException;
 import com.oracle.jmx.remote.rest.mapper.JSONMappingFactory;
 import com.sun.net.httpserver.HttpExchange;
 
 import javax.management.*;
+import javax.management.openmbean.OpenMBeanAttributeInfo;
+import javax.management.openmbean.OpenMBeanParameterInfo;
+import javax.management.openmbean.OpenType;
 import javax.management.remote.rest.PlatformRestAdapter;
 import java.io.IOException;
+import java.net.HttpURLConnection;
 import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import static javax.management.MBeanOperationInfo.*;
 
@@ -20,6 +29,21 @@
 
     private final ObjectName objectName;
     private final MBeanServer mBeanServer;
+    private static final String pathPrefix = "^/?jmx/servers/[^/]+/mbeans/[^/]+";
+
+    private final static Map<String, Class<?>> primitiveToObject = new HashMap<>();
+
+    static {
+        primitiveToObject.put("int", Integer.TYPE);
+        primitiveToObject.put("long", Long.TYPE);
+        primitiveToObject.put("double", Double.TYPE);
+        primitiveToObject.put("float", Float.TYPE);
+        primitiveToObject.put("boolean", Boolean.TYPE);
+        primitiveToObject.put("char", Character.TYPE);
+        primitiveToObject.put("byte", Byte.TYPE);
+        primitiveToObject.put("void", Void.TYPE);
+        primitiveToObject.put("short", Short.TYPE);
+    }
 
     public MBeanResource(MBeanServer mBeanServer, ObjectName objectName) {
         this.mBeanServer = mBeanServer;
@@ -171,6 +195,29 @@
         return jobj2;
     }
 
+    private Map<String, Object> getAttributes(String[] attrs) throws IntrospectionException,
+            InstanceNotFoundException, ReflectionException, AttributeNotFoundException, MBeanException {
+        Map<String, Object> result = new LinkedHashMap<>();
+        if (attrs.length == 1) {
+            result.put(attrs[0], mBeanServer.getAttribute(objectName, attrs[0]));
+        } 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()));
+            }
+        }
+        return result;
+    }
+
     private Map<String, Object> getAllAttributes() throws IntrospectionException,
             InstanceNotFoundException, ReflectionException, AttributeNotFoundException, MBeanException {
         Map<String, Object> result = new HashMap<>();
@@ -206,6 +253,166 @@
         return result;
     }
 
+    private Map<String, Object> setAttributes(JSONObject attrMap) throws IntrospectionException,
+            InstanceNotFoundException, ReflectionException, JSONDataException, ClassNotFoundException, MBeanException {
+        if (attrMap == null || attrMap.isEmpty()) {
+            throw new JSONDataException("Null arguments for set attribute");
+        }
+        Map<String, Object> result = new HashMap<>();
+        for (String attrName : attrMap.keySet()) {
+            MBeanInfo mBeanInfo = mBeanServer.getMBeanInfo(objectName);
+            MBeanAttributeInfo attrInfo = Arrays.stream(mBeanInfo.getAttributes()).filter(a -> a.getName().equals(attrName)).findFirst().orElse(null);
+            if (attrInfo == null) {
+                result.put(attrName, "<Attribute not found>");
+            } else if (!attrInfo.isWritable()) {
+                result.put(attrName, "<Attribute is read-only>");
+            } else {
+                JSONMapper mapper;
+                if (attrInfo instanceof OpenMBeanAttributeInfo) {
+                    OpenType<?> type = ((OpenMBeanAttributeInfo) attrInfo).getOpenType();
+                    mapper = JSONMappingFactory.INSTANCE.getTypeMapper(type);
+                } else {
+                    Class<?> inputCls = primitiveToObject.get(attrInfo.getType());
+                    try {
+                        if (inputCls == null) {
+                            inputCls = Class.forName(attrInfo.getType());
+                        }
+                    } catch (ClassNotFoundException | ClassCastException ex) {
+                        throw new IllegalArgumentException("Invalid parameters : " + attrMap.get(attrName).toJsonString() + " cannot be mapped to : " + attrInfo.getType());
+                    }
+                    mapper = JSONMappingFactory.INSTANCE.getTypeMapper(inputCls);
+                }
+                try {
+                    Object attrValue = mapper.toJavaObject(attrMap.get(attrName));
+                    Attribute attrObj = new Attribute(attrName, attrValue);
+                    mBeanServer.setAttribute(objectName, attrObj);
+                    result.put(attrName, "success");
+                } catch (InvalidAttributeValueException | JSONDataException e) {
+                    result.put(attrName, "<Invalid value for the attribute>");
+                } catch (AttributeNotFoundException e) {
+                    result.put(attrName, "<Attribute not found>");
+                }
+            }
+        }
+        return result;
+    }
+
+    private Object mapJsonToType(JSONElement jsonElement, MBeanParameterInfo type) {
+        if (type instanceof OpenMBeanParameterInfo) {
+            OpenType<?> openType = ((OpenMBeanParameterInfo) type).getOpenType();
+            JSONMapper typeMapper = JSONMappingFactory.INSTANCE.getTypeMapper(openType);
+            try {
+                return typeMapper.toJavaObject(jsonElement);
+            } catch (JSONDataException ex) {
+                throw new IllegalArgumentException("Invalid JSON String : " + jsonElement.toJsonString() + " for arguments");
+            }
+        } else {
+            Class<?> inputCls = primitiveToObject.get(type.getType());
+            try {
+                if (inputCls == null) {
+                    inputCls = Class.forName(type.getType());
+                }
+            } catch (ClassNotFoundException | ClassCastException ex) {
+                throw new IllegalArgumentException("Invalid parameters : " + jsonElement.toJsonString() + " cannot be mapped to : " + type.getType());
+            }
+            JSONMapper typeMapper = JSONMappingFactory.INSTANCE.getTypeMapper(inputCls);
+            if (typeMapper == null) {
+                throw new IllegalArgumentException("Invalid parameters : " + jsonElement.toJsonString() + " cannot be mapped to : " + type.getType());
+            }
+            try {
+                return typeMapper.toJavaObject(jsonElement);
+            } catch (JSONDataException ex) {
+                throw new IllegalArgumentException("Invalid JSON String : " + jsonElement.toJsonString() + " for arguments");
+            }
+        }
+    }
+
+    private Map<String, Object> getParameters(Map<String, JSONElement> jsonValues, Map<String, MBeanParameterInfo> typeMap) {
+        if (jsonValues.size() != typeMap.size()) {
+            throw new IllegalArgumentException("Invalid parameters : expected - " + typeMap.size() + " parameters, got - " + jsonValues.size());
+        }
+        if(!jsonValues.keySet().equals(typeMap.keySet())) {
+            throw new IllegalArgumentException("Invalid parameters - expected : " + Arrays.toString(typeMap.keySet().toArray()));
+        }
+        Map<String, Object> parameters = new LinkedHashMap<>();
+        if (typeMap.size() == 0 && jsonValues.isEmpty()) {
+            return parameters;
+        }
+        for (String name : typeMap.keySet()) {
+            MBeanParameterInfo type = typeMap.get(name);
+            JSONElement jsonVal = jsonValues.get(name);
+            Object obj = mapJsonToType(jsonVal, type);
+            parameters.put(name, obj);
+        }
+        return parameters;
+    }
+
+    private JSONElement execOperation(String opstr, JSONObject params)
+            throws MBeanException, IntrospectionException, ReflectionException, InstanceNotFoundException {
+        if (params == null) {
+            params = new JSONObject();
+        }
+        MBeanInfo mBeanInfo;
+        try {
+            mBeanInfo = mBeanServer.getMBeanInfo(objectName);
+        } catch (InstanceNotFoundException ex) {
+            throw new IllegalArgumentException("MBean does not exist");
+        }
+
+        MBeanOperationInfo[] opinfos = Arrays.stream(mBeanInfo.getOperations()).
+                filter(a -> a.getName().equals(opstr)).toArray(MBeanOperationInfo[]::new);
+
+        if (opinfos.length == 0) {
+            throw new IllegalArgumentException("Invalid Operation String");
+        }
+
+        String[] signature = null;
+        Object[] parameters = null;
+
+        if (opinfos.length == 1) {
+            MBeanParameterInfo[] sig = opinfos[0].getSignature();
+            Map<String, MBeanParameterInfo> typeMap = new LinkedHashMap<>();
+            Arrays.stream(sig).forEach(e -> typeMap.put(e.getName(), e));
+            parameters = getParameters(params, typeMap).values().toArray();
+            signature = Arrays.asList(sig).stream().map(a -> a.getType()).toArray(a -> new String[a]);
+        } else if (opinfos.length > 1) {
+            IllegalArgumentException exception = null;
+            for (MBeanOperationInfo opInfo : opinfos) {
+                MBeanParameterInfo[] sig = opInfo.getSignature();
+                try {
+                    Map<String, MBeanParameterInfo> typeMap = new LinkedHashMap<>();
+                    Arrays.stream(sig).forEach(e -> typeMap.put(e.getName(), e));
+                    parameters = getParameters(params, typeMap).values().toArray();
+                    signature = Arrays.asList(sig).stream().map(a -> a.getType()).toArray(a -> new String[a]);
+                    exception = null;
+                    break;
+                } catch (IllegalArgumentException ex) {
+                    exception = ex;
+                }
+            }
+            if (exception != null) {
+                throw exception;
+            }
+        }
+
+        Object invoke = null;
+        try {
+            invoke = mBeanServer.invoke(objectName, opstr, parameters, signature);
+            if (invoke != null) {
+                JSONMapper typeMapper = JSONMappingFactory.INSTANCE.getTypeMapper(invoke);
+                if (typeMapper != null) {
+                    return typeMapper.toJsonValue(invoke);
+                } else {
+                    return new JSONPrimitive("<Unable to map result to JSON>");
+                }
+            } else {
+                return new JSONPrimitive("void");
+            }
+        } catch (JSONMappingException e) {
+            return new JSONPrimitive("<Unable to map result to JSON>");
+        }
+    }
+
     private HttpResponse doMBeanInfo(HttpExchange exchange) {
         try {
             JSONObject mBeanInfo = getMBeanInfo(mBeanServer, objectName);
@@ -222,8 +429,11 @@
     @Override
     public void handle(HttpExchange exchange) throws IOException {
         String path = exchange.getRequestURI().getPath();
-        if (path.matches("^\\/?jmx\\/servers\\/[a-zA-Z0-9\\-\\.]+\\/mbeans\\/[^\\/]+\\/?$") ||
-                path.matches("^\\/?jmx\\/servers\\/[a-zA-Z0-9\\-\\.]+\\/mbeans\\/[^\\/]+\\/info$")) {
+        if (path.matches(pathPrefix + "/?$")) {
+            RestResource.super.handle(exchange);
+        } else if (path.matches(pathPrefix + "/info$") && exchange.getRequestMethod().equalsIgnoreCase("GET")) {
+            RestResource.super.handle(exchange);
+        } else if (path.matches(pathPrefix + "/[^/]+/?$") && exchange.getRequestMethod().equalsIgnoreCase("POST")) {
             RestResource.super.handle(exchange);
         } else {
             HttpUtil.sendResponse(exchange, new HttpResponse(404, "Not found"));
@@ -232,7 +442,7 @@
 
     @Override
     public HttpResponse doGet(HttpExchange exchange) {
-        if(exchange.getRequestURI().getPath().endsWith("info")) {
+        if (exchange.getRequestURI().getPath().endsWith("info")) {
             return doMBeanInfo(exchange);
         }
         String path = PlatformRestAdapter.getAuthority() + exchange.getRequestURI().getPath().replaceAll("\\/$", "");
@@ -241,7 +451,7 @@
         try {
             Map<String, Object> allAttributes = getAllAttributes();
             Map<String, String> _links = new LinkedHashMap<>();
-            _links.put("info",HttpUtil.escapeUrl(info));
+            _links.put("info", HttpUtil.escapeUrl(info));
 
             MBeanOperationInfo[] opInfo = mBeanServer.getMBeanInfo(objectName).getOperations();
             JSONArray jarr = new JSONArray();
@@ -263,14 +473,14 @@
             }
 
             JSONMapper typeMapper = JSONMappingFactory.INSTANCE.getTypeMapper(allAttributes);
-            if(typeMapper != null) {
+            if (typeMapper != null) {
                 JSONElement jsonElement1 = typeMapper.toJsonValue(allAttributes);
                 JSONElement jsonElement2 = typeMapper.toJsonValue(_links);
 
                 JSONObject jobj = new JSONObject();
-                jobj.put("attributes",jsonElement1);
-                jobj.put("operations",jarr);
-                jobj.put("_links",jsonElement2);
+                jobj.put("attributes", jsonElement1);
+                jobj.put("operations", jarr);
+                jobj.put("_links", jsonElement2);
                 return new HttpResponse(200, jobj.toJsonString());
             } else {
                 return new HttpResponse(HttpResponse.SERVER_ERROR, "Unable to find JSONMapper");
@@ -289,13 +499,194 @@
         }
     }
 
+    /*
+    HTTP POST for this MBean's URL allows setting of attributes and execution of operations.
+    POST request body can follow one of the below formats
+    1. { name : value}
+    Set a single attribute
+    2. { name1 : value1, name2 : value2 }
+    Sets multiple attributes
+    3. {attributes : {read : [name]} , {write : {name : value}}, operations : {op_name : {param_name:name, param_value:value}}}
+    This bulk operation request sets multiple attributes and executes multiple
+    operations on the MBean.
+     */
     @Override
-    public HttpResponse doPut(HttpExchange exchange) {
-        return null;
+    public HttpResponse doPost(HttpExchange exchange) {
+        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;
+
+                if (jsonObject.keySet().contains("attributes") | jsonObject.keySet().contains("operations")) {
+                    return handleBulkRequest(exchange, jsonObject);
+                } else {
+                    Map<String, Object> stringObjectMap = setAttributes(jsonObject);
+                    JSONMapper typeMapper = JSONMappingFactory.INSTANCE.getTypeMapper(stringObjectMap);
+                    if (typeMapper != null) {
+                        return new HttpResponse(HttpURLConnection.HTTP_OK, typeMapper.toJsonValue(stringObjectMap).toJsonString());
+                    } else {
+                        return new HttpResponse(HttpResponse.SERVER_ERROR, "Unable to find JSON Mapper");
+                    }
+                }
+            } else if (path.matches(pathPrefix + "/[^/]+/?$")) {  // POST to MBeanOperation
+                Matcher matcher = Pattern.compile(pathPrefix + "/").matcher(path);
+                String operation;
+                if (matcher.find()) {
+                    String ss = path.substring(matcher.end());
+                    operation = ss;
+                } else {
+                    return HttpResponse.BAD_REQUEST;
+                }
+
+                reqBody = HttpUtil.readRequestBody(exchange);
+                JSONElement result;
+                if (reqBody == null || reqBody.isEmpty()) { // No Parameters
+                    result = execOperation(operation, null);
+                } else {
+                    JSONParser parser = new JSONParser(reqBody);
+                    JSONElement jsonElement = parser.parse();
+                    if (!(jsonElement instanceof JSONObject)) {
+                        return new HttpResponse(HttpResponse.BAD_REQUEST,
+                                "Invalid parameters : [" + reqBody + "] for operation - " + operation);
+                    }
+                    result = execOperation(operation, (JSONObject) jsonElement);
+                }
+                return new HttpResponse(HttpURLConnection.HTTP_OK, result.toJsonString());
+            } else {
+                return HttpResponse.REQUEST_NOT_FOUND;
+            }
+        } catch (InstanceNotFoundException e) {
+            // Should never happen
+        } catch (ClassNotFoundException | 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));
+        } catch (IllegalArgumentException e) {
+            return new HttpResponse(HttpResponse.BAD_REQUEST, e.getMessage());
+        } catch (Exception e) {
+            return new HttpResponse(HttpResponse.SERVER_ERROR, HttpResponse.getErrorMessage(e));
+        }
+        return HttpResponse.REQUEST_NOT_FOUND;
+    }
+
+    private HttpResponse handleBulkRequest(HttpExchange exchange, JSONObject reqObject) {
+        try {
+            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);
+                }
+
+                // 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;
+                    }
+                    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);
+                            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);
+                            }
+                        }
+                    }
+                    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));
+        }
+        return HttpResponse.REQUEST_NOT_FOUND;
+    }
+
+    private String[] getStrings(JSONArray jsonArray) throws JSONDataException {
+        List<String> attributes = new ArrayList<>();
+        for (JSONElement element : jsonArray) {
+            if (element instanceof JSONPrimitive && ((JSONPrimitive) element).getValue() instanceof String) {
+                JSONPrimitive val = (JSONPrimitive) element;
+                attributes.add((String) val.getValue());
+            } else throw new JSONDataException("Expecting String, got " + element.toJsonString());
+        }
+        return attributes.toArray(new String[0]);
     }
 
     @Override
-    public HttpResponse doPost(HttpExchange exchange) {
+    public HttpResponse doPut(HttpExchange exchange) {
         return null;
     }
 
--- a/src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/http/RestResource.java	Mon Dec 25 20:42:05 2017 +0530
+++ b/src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/http/RestResource.java	Wed Dec 27 14:44:48 2017 +0530
@@ -7,6 +7,7 @@
 
 import com.sun.net.httpserver.HttpExchange;
 import com.sun.net.httpserver.HttpHandler;
+
 import java.io.IOException;
 
 /**
@@ -36,7 +37,7 @@
         }
         HttpUtil.sendResponse(exchange,httpResponse);
     }
-    
+
     public HttpResponse doGet(HttpExchange exchange);
     public HttpResponse doPut(HttpExchange exchange);
     public HttpResponse doPost(HttpExchange exchange);
--- a/src/java.management.rest/share/classes/javax/management/remote/rest/PlatformRestAdapter.java	Mon Dec 25 20:42:05 2017 +0530
+++ b/src/java.management.rest/share/classes/javax/management/remote/rest/PlatformRestAdapter.java	Wed Dec 27 14:44:48 2017 +0530
@@ -226,14 +226,14 @@
     /**
      * Default values for JMX configuration properties.
      */
-    public static interface DefaultValues {
+    static interface DefaultValues {
 
         public static final String PORT = "0";
         public static final String CONFIG_FILE_NAME = "management.properties";
-        public static final String USE_SSL = "true";
+        public static final String USE_SSL = "false";
         public static final String USE_LOCAL_ONLY = "true";
         public static final String USE_REGISTRY_SSL = "false";
-        public static final String USE_AUTHENTICATION = "true";
+        public static final String USE_AUTHENTICATION = "false";
         public static final String PASSWORD_FILE_NAME = "jmxremote.password";
         public static final String ACCESS_FILE_NAME = "jmxremote.access";
         public static final String SSL_NEED_CLIENT_AUTH = "false";
--- a/test/jdk/javax/management/remote/rest/RunRestAdapter.java	Mon Dec 25 20:42:05 2017 +0530
+++ b/test/jdk/javax/management/remote/rest/RunRestAdapter.java	Wed Dec 27 14:44:48 2017 +0530
@@ -68,7 +68,7 @@
         Properties props = new Properties();
         props.setProperty("com.sun.management.jmxremote.ssl", "true");
         props.setProperty("com.sun.management.jmxremote.ssl.config.file", sslAgentConfig);
-        props.setProperty("com.sun.management.jmxremote.authenticate", "false");
+        props.setProperty("com.sun.management.jmxremote.authenticate", "true");
         props.setProperty("com.sun.management.jmxremote.rest.port", "8686");
 
         try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName))) {