src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/http/MBeanResource.java
branchjmx-rest-api
changeset 56000 054866e5113c
parent 55997 f881344569d9
child 56001 95c0323f0c1a
equal deleted inserted replaced
55999:2db04c2274fd 56000:054866e5113c
    45 import java.io.IOException;
    45 import java.io.IOException;
    46 import java.net.HttpURLConnection;
    46 import java.net.HttpURLConnection;
    47 import java.util.*;
    47 import java.util.*;
    48 import java.util.regex.Matcher;
    48 import java.util.regex.Matcher;
    49 import java.util.regex.Pattern;
    49 import java.util.regex.Pattern;
       
    50 import java.util.stream.Collectors;
       
    51 import java.util.stream.Stream;
    50 
    52 
    51 import static javax.management.MBeanOperationInfo.*;
    53 import static javax.management.MBeanOperationInfo.*;
    52 
    54 
    53 public class MBeanResource implements RestResource {
    55 public class MBeanResource implements RestResource {
    54 
    56 
    68         primitiveToObject.put("byte", Byte.TYPE);
    70         primitiveToObject.put("byte", Byte.TYPE);
    69         primitiveToObject.put("void", Void.TYPE);
    71         primitiveToObject.put("void", Void.TYPE);
    70         primitiveToObject.put("short", Short.TYPE);
    72         primitiveToObject.put("short", Short.TYPE);
    71     }
    73     }
    72 
    74 
    73     public MBeanResource(MBeanServer mBeanServer, ObjectName objectName) {
    75     MBeanResource(MBeanServer mBeanServer, ObjectName objectName) {
    74         this.mBeanServer = mBeanServer;
    76         this.mBeanServer = mBeanServer;
    75         this.objectName = objectName;
    77         this.objectName = objectName;
    76     }
    78     }
    77 
    79 
    78     private JSONObject getMBeanInfo(MBeanServer mbeanServer, ObjectName mbean) throws InstanceNotFoundException, IntrospectionException, ReflectionException {
    80     @Override
       
    81     public void handle(HttpExchange exchange) throws IOException {
       
    82         String path = exchange.getRequestURI().getPath();
       
    83         if (path.matches(pathPrefix + "/?$")) {
       
    84             RestResource.super.handle(exchange);
       
    85         } else if (path.matches(pathPrefix + "/info$")
       
    86                 && exchange.getRequestMethod().equalsIgnoreCase("GET")) {
       
    87             RestResource.super.handle(exchange);
       
    88         } else if (path.matches(pathPrefix + "/[^/]+/?$")
       
    89                 && exchange.getRequestMethod().equalsIgnoreCase("POST")) {
       
    90             RestResource.super.handle(exchange);
       
    91         } else {
       
    92             HttpUtil.sendResponse(exchange, HttpResponse.REQUEST_NOT_FOUND);
       
    93         }
       
    94     }
       
    95 
       
    96     @Override
       
    97     public HttpResponse doGet(HttpExchange exchange) {
       
    98         String path = PlatformRestAdapter.getDomain() +
       
    99                 exchange.getRequestURI().getPath().replaceAll("/$", "");
       
   100 
       
   101         if (path.endsWith("info")) {
       
   102             return doMBeanInfo();
       
   103         }
       
   104 
       
   105         String infoPath = path + "/info";
       
   106 
       
   107         try {
       
   108             Map<String, Object> allAttributes = getAllAttributes();
       
   109             Map<String, String> _links = new LinkedHashMap<>();
       
   110             _links.put("info", HttpUtil.escapeUrl(infoPath));
       
   111 
       
   112             MBeanOperationInfo[] opInfo = mBeanServer.getMBeanInfo(objectName).getOperations();
       
   113             JSONArray jarr = new JSONArray();
       
   114             for (MBeanOperationInfo op : opInfo) {
       
   115                 JSONObject jobj1 = new JSONObject();
       
   116                 JSONArray jarr1 = new JSONArray();
       
   117                 jobj1.put("name", op.getName());
       
   118                 jobj1.put("href", HttpUtil.escapeUrl(path + "/" + op.getName()));
       
   119                 jobj1.put("method", "POST");
       
   120                 for (MBeanParameterInfo paramInfo : op.getSignature()) {
       
   121                     JSONObject jobj = new JSONObject();
       
   122                     jobj.put("name", paramInfo.getName());
       
   123                     jobj.put("type", paramInfo.getType());
       
   124                     jarr1.add(jobj);
       
   125                 }
       
   126                 jobj1.put("arguments", jarr1);
       
   127                 jobj1.put("returnType", op.getReturnType());
       
   128                 jarr.add(jobj1);
       
   129             }
       
   130 
       
   131             JSONMapper typeMapper = JSONMappingFactory.INSTANCE.getTypeMapper(allAttributes);
       
   132             if (typeMapper != null) {
       
   133                 JSONElement jsonElement1 = typeMapper.toJsonValue(allAttributes);
       
   134                 JSONElement jsonElement2 = typeMapper.toJsonValue(_links);
       
   135 
       
   136                 JSONObject jobj = new JSONObject();
       
   137                 jobj.put("attributes", jsonElement1);
       
   138                 jobj.put("operations", jarr);
       
   139                 jobj.put("_links", jsonElement2);
       
   140                 return new HttpResponse(jobj.toJsonString());
       
   141             } else {
       
   142                 return HttpResponse.SERVER_ERROR;
       
   143             }
       
   144         } catch (RuntimeOperationsException | IntrospectionException | ReflectionException
       
   145                 | JSONMappingException | MBeanException e) {
       
   146             return HttpResponse.SERVER_ERROR;
       
   147         } catch (InstanceNotFoundException e) {
       
   148             return new HttpResponse(HttpResponse.BAD_REQUEST, "Specified MBean does not exist");
       
   149         } catch (Exception e) {
       
   150             return HttpResponse.SERVER_ERROR;
       
   151         }
       
   152     }
       
   153 
       
   154     /*
       
   155     HTTP POST for this MBean's URL allows setting of attributes and execution of operations.
       
   156     POST request body can follow one of the below formats
       
   157     1. { name : value}
       
   158     Set a single attribute
       
   159     2. { name1 : value1, name2 : value2 }
       
   160     Sets multiple attributes
       
   161     3. {attributes : {read : [name]} , {write : {name : value}}, operations : {op_name : {param_name:name, param_value:value}}}
       
   162     This bulk operation request sets multiple attributes and executes multiple
       
   163     operations on the MBean.
       
   164  */
       
   165     @Override
       
   166     public HttpResponse doPost(HttpExchange exchange) {
       
   167         String path = exchange.getRequestURI().getPath();
       
   168         String reqBody = null;
       
   169         try {
       
   170             if (path.matches(pathPrefix + "/?$")) { // POST to current URL
       
   171                 reqBody = HttpUtil.readRequestBody(exchange);
       
   172                 if (reqBody.isEmpty()) {                // No Parameters
       
   173                     return HttpResponse.BAD_REQUEST;
       
   174                 }
       
   175 
       
   176                 JSONParser parser = new JSONParser(reqBody);
       
   177                 JSONElement jsonElement = parser.parse();
       
   178                 if (!(jsonElement instanceof JSONObject)) {
       
   179                     return new HttpResponse(HttpResponse.BAD_REQUEST,
       
   180                             "Invalid parameters : [" + reqBody + "]");
       
   181                 }
       
   182 
       
   183                 JSONObject jsonObject = (JSONObject) jsonElement;
       
   184 
       
   185                 // Handle bulk operation
       
   186                 if (jsonObject.keySet().contains("attributes") | jsonObject.keySet().contains("operations")) {
       
   187                     return new HttpResponse(handleBulkRequest(jsonObject).toJsonString());
       
   188                 } else {    // Handle attribute update
       
   189                     Map<String, Object> stringObjectMap = setAttributes(jsonObject);
       
   190                     JSONMapper typeMapper = JSONMappingFactory.INSTANCE.getTypeMapper(stringObjectMap);
       
   191                     if (typeMapper != null) {
       
   192                         return new HttpResponse(HttpURLConnection.HTTP_OK, typeMapper.toJsonValue(stringObjectMap).toJsonString());
       
   193                     } else {
       
   194                         return new HttpResponse(HttpResponse.SERVER_ERROR, "Unable to find JSON Mapper");
       
   195                     }
       
   196                 }
       
   197             } else if (path.matches(pathPrefix + "/[^/]+/?$")) {  // POST to MBeanOperation
       
   198                 Matcher matcher = Pattern.compile(pathPrefix + "/").matcher(path);
       
   199                 String operation;
       
   200                 if (matcher.find()) {
       
   201                     operation = path.substring(matcher.end());
       
   202                 } else {
       
   203                     return HttpResponse.BAD_REQUEST;
       
   204                 }
       
   205 
       
   206                 reqBody = HttpUtil.readRequestBody(exchange);
       
   207                 JSONElement result;
       
   208                 if (reqBody.isEmpty()) { // No Parameters
       
   209                     result = execOperation(operation, null);
       
   210                 } else {
       
   211                     JSONParser parser = new JSONParser(reqBody);
       
   212                     JSONElement jsonElement = parser.parse();
       
   213                     if (!(jsonElement instanceof JSONObject)) {
       
   214                         return new HttpResponse(HttpResponse.BAD_REQUEST,
       
   215                                 "Invalid parameters : [" + reqBody + "] for operation - " + operation);
       
   216                     }
       
   217                     result = execOperation(operation, (JSONObject) jsonElement);
       
   218                 }
       
   219                 return new HttpResponse(HttpURLConnection.HTTP_OK, result.toJsonString());
       
   220             } else {
       
   221                 return HttpResponse.REQUEST_NOT_FOUND;
       
   222             }
       
   223         } catch (InstanceNotFoundException e) {
       
   224             // Should never happen
       
   225         } catch (JSONDataException | ParseException e) {
       
   226             return new HttpResponse(HttpURLConnection.HTTP_BAD_REQUEST, "Invalid JSON : " + reqBody, e.getMessage());
       
   227         } catch (IntrospectionException | JSONMappingException | MBeanException | ReflectionException | IOException e) {
       
   228             return new HttpResponse(HttpResponse.SERVER_ERROR, HttpResponse.getErrorMessage(e));
       
   229         } catch (IllegalArgumentException e) {
       
   230             return new HttpResponse(HttpResponse.BAD_REQUEST, e.getMessage());
       
   231         } catch (Exception e) {
       
   232             return new HttpResponse(HttpResponse.SERVER_ERROR, HttpResponse.getErrorMessage(e));
       
   233         }
       
   234         return HttpResponse.REQUEST_NOT_FOUND;
       
   235     }
       
   236 
       
   237     private HttpResponse doMBeanInfo() {
       
   238         try {
       
   239             JSONObject mBeanInfo = getMBeanInfo(mBeanServer, objectName);
       
   240             return new HttpResponse(mBeanInfo.toJsonString());
       
   241         } catch (RuntimeOperationsException | IntrospectionException | ReflectionException e) {
       
   242             return HttpResponse.SERVER_ERROR;
       
   243         } catch (InstanceNotFoundException e) {
       
   244             return new HttpResponse(HttpResponse.BAD_REQUEST, "Specified MBean does not exist");
       
   245         } catch (Exception e) {
       
   246             return HttpResponse.SERVER_ERROR;
       
   247         }
       
   248     }
       
   249 
       
   250     JSONElement handleBulkRequest(JSONObject reqObject) {
       
   251         JSONObject result = new JSONObject();
       
   252 
       
   253         // Handle attributes
       
   254         JSONElement element = reqObject.get("attributes");
       
   255         if (element != null && element instanceof JSONObject) {
       
   256             JSONObject attrInfo = (JSONObject) element;
       
   257             JSONObject attrNode = new JSONObject();
       
   258 
       
   259             // Read attributes
       
   260             JSONElement read = attrInfo.get("get");
       
   261             if (read != null) {
       
   262                 if (read instanceof JSONArray) {
       
   263                     JSONArray jsonAttrMap = (JSONArray) read;
       
   264                     JSONElement resultJson;
       
   265                     Map<String, Object> attrRead;
       
   266                     try {
       
   267                         String[] attributes = getStrings(jsonAttrMap);
       
   268                         attrRead = getAttributes(attributes);
       
   269                         JSONMapper typeMapper = JSONMappingFactory.INSTANCE.getTypeMapper(attrRead);
       
   270                         resultJson = typeMapper.toJsonValue(attrRead);
       
   271                     } catch (InstanceNotFoundException | ReflectionException | JSONMappingException | MBeanException e) {
       
   272                         resultJson = new JSONPrimitive("<ERROR: Unable to retrieve value>");
       
   273                     } catch (JSONDataException e) {
       
   274                         resultJson = new JSONPrimitive("Invalid JSON : " + e.getMessage());
       
   275                     }
       
   276                     attrNode.put("get", resultJson);
       
   277                 } else {
       
   278                     attrInfo.put("get", new JSONPrimitive("Invalid JSON : " + read.toJsonString()));
       
   279                 }
       
   280             }
       
   281 
       
   282             // Write attributes
       
   283             JSONElement write = attrInfo.get("set");
       
   284 
       
   285             if (write != null) {
       
   286                 if (write instanceof JSONObject) {
       
   287                     JSONElement resultJson;
       
   288                     JSONObject jsonAttrMap = (JSONObject) write;
       
   289                     try {
       
   290                         Map<String, Object> attrMap = setAttributes(jsonAttrMap);
       
   291                         JSONMapper typeMapper = JSONMappingFactory.INSTANCE.getTypeMapper(attrMap);
       
   292                         resultJson = typeMapper.toJsonValue(attrMap);
       
   293                     } catch (JSONDataException ex) {
       
   294                         resultJson = new JSONPrimitive("Invalid JSON : " + write.toJsonString());
       
   295                     } catch (JSONMappingException | IntrospectionException | InstanceNotFoundException | ReflectionException e) {
       
   296                         resultJson = new JSONPrimitive("<ERROR: Unable to retrieve value>");
       
   297                     }
       
   298                     attrNode.put("set", resultJson);
       
   299                 } else {
       
   300                     attrNode.put("set", new JSONPrimitive("Invalid JSON : " + write.toJsonString()));
       
   301                 }
       
   302             }
       
   303             result.put("attributes", attrNode);
       
   304         }
       
   305 
       
   306         // Execute operations
       
   307         element = reqObject.get("operations");
       
   308         if (element != null) {
       
   309             JSONArray operationList;
       
   310             if (element instanceof JSONPrimitive             // Single no-arg operation
       
   311                     || element instanceof JSONObject) {     // single/mulitple operations
       
   312                 operationList = new JSONArray();
       
   313                 operationList.add(element);
       
   314             } else if (element instanceof JSONArray) {  // List of no-arg/with-arg operation
       
   315                 operationList = (JSONArray) element;
       
   316             } else {
       
   317                 operationList = new JSONArray();
       
   318             }
       
   319             JSONObject opResult = new JSONObject();
       
   320             operationList.forEach((elem) -> {
       
   321                 if (elem instanceof JSONPrimitive
       
   322                         && ((JSONPrimitive) elem).getValue() instanceof String) { // no-arg operation
       
   323                     String opName = (String) ((JSONPrimitive) elem).getValue();
       
   324                     try {
       
   325                         JSONElement obj = execOperation(opName, null);
       
   326                         opResult.put(opName, obj);
       
   327                     } catch (IllegalArgumentException e) {
       
   328                         opResult.put(opName, e.getMessage());
       
   329                     } catch (IntrospectionException | InstanceNotFoundException
       
   330                             | MBeanException | ReflectionException e) {
       
   331                         opResult.put(opName, "<ERROR while executing operation>");
       
   332                     }
       
   333                 } else if (elem instanceof JSONObject) {
       
   334                     ((JSONObject) elem).keySet().forEach((opName) -> {
       
   335                         try {
       
   336                             JSONElement jsonElement = ((JSONObject) elem).get(opName);
       
   337                             if (jsonElement instanceof JSONObject) {
       
   338                                 JSONElement obj = execOperation(opName, (JSONObject) jsonElement);
       
   339                                 opResult.put(opName, obj);
       
   340                             } else {
       
   341                                 opResult.put(opName, new JSONPrimitive("Invalid parameter JSON"));
       
   342                             }
       
   343                         } catch (IllegalArgumentException e) {
       
   344                             opResult.put(opName, e.getMessage());
       
   345                         } catch (IntrospectionException | InstanceNotFoundException
       
   346                                 | MBeanException | ReflectionException e) {
       
   347                             opResult.put(opName, "<ERROR while executing operation>");
       
   348                         }
       
   349                     });
       
   350                 }
       
   351             });
       
   352             result.put("operations", opResult);
       
   353         }
       
   354         return result;
       
   355     }
       
   356 
       
   357     private JSONObject getMBeanInfo(MBeanServer mbeanServer, ObjectName mbean)
       
   358             throws InstanceNotFoundException, IntrospectionException, ReflectionException {
       
   359 
    79         JSONObject jobj = new JSONObject();
   360         JSONObject jobj = new JSONObject();
    80         MBeanInfo mBeanInfo = mbeanServer.getMBeanInfo(mbean);
   361         MBeanInfo mBeanInfo = mbeanServer.getMBeanInfo(mbean);
    81         if (mBeanInfo == null) {
   362         if (mBeanInfo == null) {
    82             return jobj;
   363             return jobj;
    83         }
   364         }
       
   365         jobj.put("name", mbean.toString());
       
   366         jobj.put("className", mBeanInfo.getClassName());
    84         jobj.put("description", mBeanInfo.getDescription());
   367         jobj.put("description", mBeanInfo.getDescription());
    85 
   368 
    86         // Populate Attribute Info
   369         // Populate Attribute Info
    87         MBeanAttributeInfo[] attributes = mBeanInfo.getAttributes();
   370         MBeanAttributeInfo[] attributes = mBeanInfo.getAttributes();
    88         JSONArray jarr = new JSONArray();
   371         JSONArray jarr = new JSONArray();
   106             jobj1.put("description", attr.getDescription());
   389             jobj1.put("description", attr.getDescription());
   107             jobj1.put("descriptor", getDescriptorJSON(attr.getDescriptor()));
   390             jobj1.put("descriptor", getDescriptorJSON(attr.getDescriptor()));
   108             jarr.add(jobj1);
   391             jarr.add(jobj1);
   109         }
   392         }
   110         jobj.put("attributeInfo", jarr);
   393         jobj.put("attributeInfo", jarr);
       
   394 
   111 
   395 
   112         // Add constructor Info
   396         // Add constructor Info
   113         MBeanConstructorInfo[] constructorInfo = mBeanInfo.getConstructors();
   397         MBeanConstructorInfo[] constructorInfo = mBeanInfo.getConstructors();
   114         jarr = new JSONArray();
   398         jarr = new JSONArray();
   115         for (MBeanConstructorInfo constructor : constructorInfo) {
   399         for (MBeanConstructorInfo constructor : constructorInfo) {
   170 
   454 
   171         MBeanNotificationInfo[] notifications = mBeanInfo.getNotifications();
   455         MBeanNotificationInfo[] notifications = mBeanInfo.getNotifications();
   172         jarr = new JSONArray();
   456         jarr = new JSONArray();
   173 
   457 
   174         for (MBeanNotificationInfo notification : notifications) {
   458         for (MBeanNotificationInfo notification : notifications) {
   175 
       
   176             JSONObject jobj1 = new JSONObject();
   459             JSONObject jobj1 = new JSONObject();
   177 
       
   178             jobj1.put("name", notification.getName());
   460             jobj1.put("name", notification.getName());
   179             JSONArray jarr1 = new JSONArray();
   461             JSONArray jarr1 = new JSONArray();
   180             for (String notifType : notification.getNotifTypes()) {
   462             for (String notifType : notification.getNotifTypes()) {
   181                 jarr1.add(new JSONPrimitive(notifType));
   463                 jarr1.add(new JSONPrimitive(notifType));
   182             }
   464             }
   193         return jobj;
   475         return jobj;
   194     }
   476     }
   195 
   477 
   196     private JSONObject getParamJSON(MBeanParameterInfo mParamInfo) {
   478     private JSONObject getParamJSON(MBeanParameterInfo mParamInfo) {
   197         JSONObject jobj1 = new JSONObject();
   479         JSONObject jobj1 = new JSONObject();
       
   480         jobj1.put("name", mParamInfo.getName());
       
   481         jobj1.put("type", mParamInfo.getType());
   198         if (mParamInfo.getDescription() != null && !mParamInfo.getDescription().isEmpty()) {
   482         if (mParamInfo.getDescription() != null && !mParamInfo.getDescription().isEmpty()) {
   199             jobj1.put("description", mParamInfo.getDescription());
   483             jobj1.put("description", mParamInfo.getDescription());
   200         }
   484         }
   201         jobj1.put("name", mParamInfo.getName());
       
   202         jobj1.put("type", mParamInfo.getType());
       
   203         if (mParamInfo.getDescriptor() != null && mParamInfo.getDescriptor().getFieldNames().length > 1) {
   485         if (mParamInfo.getDescriptor() != null && mParamInfo.getDescriptor().getFieldNames().length > 1) {
   204             jobj1.put("descriptor", getDescriptorJSON(mParamInfo.getDescriptor()));
   486             jobj1.put("descriptor", getDescriptorJSON(mParamInfo.getDescriptor()));
   205         }
   487         }
   206         return jobj1;
   488         return jobj1;
   207     }
   489     }
   208 
   490 
   209     private JSONObject getDescriptorJSON(Descriptor descriptor) {
   491     private JSONObject getDescriptorJSON(Descriptor descriptor) {
   210         JSONObject jobj2 = new JSONObject();
   492         JSONObject jobj2 = new JSONObject();
   211         try {
   493         String[] descNames = descriptor.getFieldNames();
   212             String[] descNames = descriptor.getFieldNames();
   494         for (String descName : descNames) {
   213             for (String descName : descNames) {
   495             Object fieldValue = descriptor.getFieldValue(descName);
   214                 Object fieldValue = descriptor.getFieldValue(descName);
   496             jobj2.put(descName, fieldValue != null ? fieldValue.toString() : null);
   215                 jobj2.put(descName, fieldValue != null ? fieldValue.toString() : null);
       
   216             }
       
   217         } catch (Throwable t) {
       
   218             t.printStackTrace();
       
   219         }
   497         }
   220         return jobj2;
   498         return jobj2;
   221     }
   499     }
   222 
   500 
   223     private Map<String, Object> getAttributes(String[] attrs) throws InstanceNotFoundException,
   501     private Map<String, Object> getAttributes(String[] attrs) throws
   224             ReflectionException {
   502             InstanceNotFoundException, ReflectionException, MBeanException {
       
   503 
   225         Map<String, Object> result = new LinkedHashMap<>();
   504         Map<String, Object> result = new LinkedHashMap<>();
       
   505 
   226         if (attrs == null || attrs.length == 0) {
   506         if (attrs == null || attrs.length == 0) {
   227             return result;
   507             return result;
   228         }
   508         }
       
   509 
   229         AttributeList attrVals = mBeanServer.getAttributes(objectName, attrs);
   510         AttributeList attrVals = mBeanServer.getAttributes(objectName, attrs);
   230         if (attrVals.size() != attrs.length) {
   511         List<String> missingAttrs = Arrays.asList(attrs);
   231             List<String> missingAttrs = new ArrayList<>(Arrays.asList(attrs));
   512         attrVals.asList().forEach(a -> {
   232             for (Attribute a : attrVals.asList()) {
   513             missingAttrs.remove(a.getName());
   233                 missingAttrs.remove(a.getName());
   514             result.put(a.getName(), a.getValue());
   234                 result.put(a.getName(), a.getValue());
   515         });
   235             }
   516 
   236             for (String attr : missingAttrs) {
   517         for (String attr : missingAttrs) {
       
   518             try {
       
   519                 mBeanServer.getAttribute(objectName, attr);
   237                 result.put(attr, "< Error: No such attribute >");
   520                 result.put(attr, "< Error: No such attribute >");
   238             }
   521             } catch (RuntimeException ex) {
   239         } else {
   522                 if (ex.getCause() instanceof UnsupportedOperationException) {
   240             attrVals.asList().forEach((a) -> result.put(a.getName(), a.getValue()));
   523                     result.put(attr, "< Attribute not supported >");
   241         }
   524                 } else if (ex.getCause() instanceof IllegalArgumentException) {
   242 
   525                     result.put(attr, "< Invalid attributes >");
       
   526                 }
       
   527             } catch (AttributeNotFoundException e) {
       
   528                 result.put(attr, "< Attribute not found >");
       
   529             }
       
   530         }
   243         return result;
   531         return result;
   244     }
   532     }
   245 
   533 
   246     private Map<String, Object> getAllAttributes() throws IntrospectionException,
   534     private Map<String, Object> getAllAttributes() throws InstanceNotFoundException,
   247             InstanceNotFoundException, ReflectionException, AttributeNotFoundException, MBeanException {
   535             ReflectionException, MBeanException, IntrospectionException {
   248         Map<String, Object> result = new HashMap<>();
   536 
   249         MBeanInfo mInfo = mBeanServer.getMBeanInfo(objectName);
   537         MBeanInfo mInfo = mBeanServer.getMBeanInfo(objectName);
   250         String[] attrs = Arrays.stream(mInfo.getAttributes())
   538         String[] attrs = Stream.of(mInfo.getAttributes())
   251                 .map(MBeanAttributeInfo::getName)
   539                 .map(MBeanAttributeInfo::getName)
   252                 .toArray(String[]::new);
   540                 .toArray(String[]::new);
   253         AttributeList attrVals = mBeanServer.getAttributes(objectName, attrs);
   541 
   254         if (attrVals.size() != attrs.length) {
   542         return getAttributes(attrs);
   255             List<String> missingAttrs = new ArrayList<>(Arrays.asList(attrs));
       
   256             for (Attribute a : attrVals.asList()) {
       
   257                 missingAttrs.remove(a.getName());
       
   258                 result.put(a.getName(), a.getValue());
       
   259             }
       
   260             for (String attr : missingAttrs) {
       
   261                 try {
       
   262                     Object attribute = mBeanServer.getAttribute(objectName, attr);
       
   263                 } catch (RuntimeException ex) {
       
   264                     if (ex.getCause() instanceof UnsupportedOperationException) {
       
   265                         result.put(attr, "< Attribute not supported >");
       
   266                     } else if (ex.getCause() instanceof IllegalArgumentException) {
       
   267                         result.put(attr, "< Invalid attributes >");
       
   268                     }
       
   269                     continue;
       
   270                 }
       
   271                 result.put(attr, "< Error: No such attribute >");
       
   272             }
       
   273         } else {
       
   274             attrVals.asList().forEach((a) -> {
       
   275                 result.put(a.getName(), a.getValue());
       
   276             });
       
   277         }
       
   278         return result;
       
   279     }
   543     }
   280 
   544 
   281     private Map<String, Object> setAttributes(JSONObject attrMap) throws JSONDataException,
   545     private Map<String, Object> setAttributes(JSONObject attrMap) throws JSONDataException,
   282             IntrospectionException, InstanceNotFoundException, ReflectionException {
   546             IntrospectionException, InstanceNotFoundException, ReflectionException {
       
   547 
   283         if (attrMap == null || attrMap.isEmpty()) {
   548         if (attrMap == null || attrMap.isEmpty()) {
   284             throw new JSONDataException("Null arguments for set attribute");
   549             throw new JSONDataException("Null arguments for set attribute");
   285         }
   550         }
       
   551 
       
   552         MBeanInfo mBeanInfo = mBeanServer.getMBeanInfo(objectName);
   286         Map<String, Object> result = new HashMap<>();
   553         Map<String, Object> result = new HashMap<>();
       
   554 
   287         for (String attrName : attrMap.keySet()) {
   555         for (String attrName : attrMap.keySet()) {
   288             MBeanInfo mBeanInfo = mBeanServer.getMBeanInfo(objectName);
   556             MBeanAttributeInfo attrInfo = Arrays.stream(mBeanInfo.getAttributes()).
   289             MBeanAttributeInfo attrInfo = Arrays.stream(mBeanInfo.getAttributes()).filter(a -> a.getName().equals(attrName)).findFirst().orElse(null);
   557                     filter(a -> a.getName().equals(attrName)).findFirst().orElse(null);
   290             if (attrInfo == null) {
   558             if (attrInfo == null) {
   291                 result.put(attrName, "<Attribute not found>");
   559                 result.put(attrName, "<Attribute not found>");
   292             } else if (!attrInfo.isWritable()) {
   560             } else if (!attrInfo.isWritable()) {
   293                 result.put(attrName, "<Attribute is read-only>");
   561                 result.put(attrName, "<Attribute is read-only>");
   294             } else {
   562             } else {
   301                     try {
   569                     try {
   302                         if (inputCls == null) {
   570                         if (inputCls == null) {
   303                             inputCls = Class.forName(attrInfo.getType());
   571                             inputCls = Class.forName(attrInfo.getType());
   304                         }
   572                         }
   305                     } catch (ClassNotFoundException | ClassCastException ex) {
   573                     } catch (ClassNotFoundException | ClassCastException ex) {
   306                         throw new IllegalArgumentException("Invalid parameters : " + attrMap.get(attrName).toJsonString() + " cannot be mapped to : " + attrInfo.getType());
   574                         throw new IllegalArgumentException("Invalid parameters : "
       
   575                                 + attrMap.get(attrName).toJsonString() + " cannot be mapped to : " + attrInfo.getType());
   307                     }
   576                     }
   308                     mapper = JSONMappingFactory.INSTANCE.getTypeMapper(inputCls);
   577                     mapper = JSONMappingFactory.INSTANCE.getTypeMapper(inputCls);
   309                 }
   578                 }
   310                 try {
   579                 try {
   311                     Object attrValue = mapper.toJavaObject(attrMap.get(attrName));
   580                     Object attrValue = mapper.toJavaObject(attrMap.get(attrName));
   322             }
   591             }
   323         }
   592         }
   324         return result;
   593         return result;
   325     }
   594     }
   326 
   595 
   327     private Object mapJsonToType(JSONElement jsonElement, MBeanParameterInfo type) {
   596     private Object mapJsonElemToType(JSONElement jsonElement, MBeanParameterInfo type) {
   328         if (type instanceof OpenMBeanParameterInfo) {
   597         if (type instanceof OpenMBeanParameterInfo) {
   329             OpenType<?> openType = ((OpenMBeanParameterInfo) type).getOpenType();
   598             OpenType<?> openType = ((OpenMBeanParameterInfo) type).getOpenType();
   330             JSONMapper typeMapper = JSONMappingFactory.INSTANCE.getTypeMapper(openType);
   599             JSONMapper typeMapper = JSONMappingFactory.INSTANCE.getTypeMapper(openType);
   331             try {
   600             try {
   332                 return typeMapper.toJavaObject(jsonElement);
   601                 return typeMapper.toJavaObject(jsonElement);
   352                 throw new IllegalArgumentException("Invalid JSON String : " + jsonElement.toJsonString() + " for arguments");
   621                 throw new IllegalArgumentException("Invalid JSON String : " + jsonElement.toJsonString() + " for arguments");
   353             }
   622             }
   354         }
   623         }
   355     }
   624     }
   356 
   625 
   357     private Map<String, Object> getParameters(Map<String, JSONElement> jsonValues, Map<String, MBeanParameterInfo> typeMap) {
   626     private Map<String, Object> getOperationParameters(Map<String, JSONElement> jsonValues, Map<String, MBeanParameterInfo> typeMap) {
   358         if (jsonValues.size() != typeMap.size()) {
   627         if (jsonValues.size() != typeMap.size()) {
   359             throw new IllegalArgumentException("Invalid parameters : expected - " + typeMap.size() + " parameters, got - " + jsonValues.size());
   628             throw new IllegalArgumentException("Invalid parameters : expected - " + typeMap.size() + " parameters, got - " + jsonValues.size());
   360         }
   629         }
   361         if (!jsonValues.keySet().equals(typeMap.keySet())) {
   630         if (!jsonValues.keySet().equals(typeMap.keySet())) {
   362             throw new IllegalArgumentException("Invalid parameters - expected : " + Arrays.toString(typeMap.keySet().toArray()));
   631             throw new IllegalArgumentException("Invalid parameters - expected : " + Arrays.toString(typeMap.keySet().toArray()));
   363         }
   632         }
   364         Map<String, Object> parameters = new LinkedHashMap<>();
   633         Map<String, Object> parameters = new LinkedHashMap<>(); // Order of parameters should be same as typeMap
   365         if (typeMap.size() == 0 && jsonValues.isEmpty()) {
   634         if (typeMap.isEmpty() && jsonValues.isEmpty()) {
   366             return parameters;
   635             return parameters;
   367         }
   636         }
   368         for (String name : typeMap.keySet()) {
   637         typeMap.keySet().forEach((name) -> {
   369             MBeanParameterInfo type = typeMap.get(name);
   638             MBeanParameterInfo type = typeMap.get(name);
   370             JSONElement jsonVal = jsonValues.get(name);
   639             JSONElement jsonVal = jsonValues.get(name);
   371             Object obj = mapJsonToType(jsonVal, type);
   640             Object obj = mapJsonElemToType(jsonVal, type);
   372             parameters.put(name, obj);
   641             parameters.put(name, obj);
   373         }
   642         });
   374         return parameters;
   643         return parameters;
   375     }
   644     }
   376 
   645 
   377     private JSONElement execOperation(String opstr, JSONObject params)
   646     private JSONElement execOperation(String opstr, JSONObject params)
   378             throws MBeanException, IntrospectionException, ReflectionException, InstanceNotFoundException {
   647             throws MBeanException, IntrospectionException, ReflectionException, InstanceNotFoundException {
   384             mBeanInfo = mBeanServer.getMBeanInfo(objectName);
   653             mBeanInfo = mBeanServer.getMBeanInfo(objectName);
   385         } catch (InstanceNotFoundException ex) {
   654         } catch (InstanceNotFoundException ex) {
   386             throw new IllegalArgumentException("MBean does not exist");
   655             throw new IllegalArgumentException("MBean does not exist");
   387         }
   656         }
   388 
   657 
   389         MBeanOperationInfo[] opinfos = Arrays.stream(mBeanInfo.getOperations()).
   658         List<MBeanOperationInfo> mBeanOperationInfos = Arrays.stream(mBeanInfo.getOperations()).
   390                 filter(a -> a.getName().equals(opstr)).toArray(MBeanOperationInfo[]::new);
   659                 filter(a -> a.getName().equals(opstr)).collect(Collectors.toList());
   391 
   660 
   392         if (opinfos.length == 0) {
   661         if (mBeanOperationInfos.isEmpty()) {
   393             throw new IllegalArgumentException("Invalid Operation String");
   662             throw new IllegalArgumentException("Invalid Operation String");
   394         }
   663         }
   395 
   664 
   396         String[] signature = null;
   665         String[] signature = null;
   397         Object[] parameters = null;
   666         Object[] parameters = null;
   398 
   667 
   399         if (opinfos.length == 1) {
   668         IllegalArgumentException exception = null;
   400             MBeanParameterInfo[] sig = opinfos[0].getSignature();
   669         for (MBeanOperationInfo mBeanOperationInfo : mBeanOperationInfos) {
   401             Map<String, MBeanParameterInfo> typeMap = new LinkedHashMap<>();
   670             MBeanParameterInfo[] sig = mBeanOperationInfo.getSignature();
   402             Arrays.stream(sig).forEach(e -> typeMap.put(e.getName(), e));
   671             try {
   403             parameters = getParameters(params, typeMap).values().toArray();
   672                 Map<String, MBeanParameterInfo> typeMap = new LinkedHashMap<>();    // Order of parameters is important
   404             signature = Arrays.asList(sig).stream().map(a -> a.getType()).toArray(a -> new String[a]);
   673                 Arrays.stream(sig).forEach(e -> typeMap.put(e.getName(), e));
   405         } else if (opinfos.length > 1) {
   674                 parameters = getOperationParameters(params, typeMap).values().toArray();
   406             IllegalArgumentException exception = null;
   675                 signature = Stream.of(sig).map(MBeanParameterInfo::getType).toArray(String[]::new);
   407             for (MBeanOperationInfo opInfo : opinfos) {
   676                 exception = null;
   408                 MBeanParameterInfo[] sig = opInfo.getSignature();
   677                 break;
   409                 try {
   678             } catch (IllegalArgumentException ex) {
   410                     Map<String, MBeanParameterInfo> typeMap = new LinkedHashMap<>();
   679                 exception = ex;
   411                     Arrays.stream(sig).forEach(e -> typeMap.put(e.getName(), e));
   680             }
   412                     parameters = getParameters(params, typeMap).values().toArray();
   681         }
   413                     signature = Arrays.asList(sig).stream().map(a -> a.getType()).toArray(a -> new String[a]);
   682         if (exception != null) {
   414                     exception = null;
   683             throw exception;
   415                     break;
   684         }
   416                 } catch (IllegalArgumentException ex) {
   685 
   417                     exception = ex;
   686         Object invoke;
   418                 }
       
   419             }
       
   420             if (exception != null) {
       
   421                 throw exception;
       
   422             }
       
   423         }
       
   424 
       
   425         Object invoke = null;
       
   426         try {
   687         try {
   427             invoke = mBeanServer.invoke(objectName, opstr, parameters, signature);
   688             invoke = mBeanServer.invoke(objectName, opstr, parameters, signature);
   428             if (invoke != null) {
   689             if (invoke != null) {
   429                 JSONMapper typeMapper = JSONMappingFactory.INSTANCE.getTypeMapper(invoke);
   690                 JSONMapper typeMapper = JSONMappingFactory.INSTANCE.getTypeMapper(invoke);
   430                 if (typeMapper != null) {
   691                 if (typeMapper != null) {
   438         } catch (JSONMappingException e) {
   699         } catch (JSONMappingException e) {
   439             return new JSONPrimitive("<Unable to map result to JSON>");
   700             return new JSONPrimitive("<Unable to map result to JSON>");
   440         }
   701         }
   441     }
   702     }
   442 
   703 
   443     private HttpResponse doMBeanInfo(HttpExchange exchange) {
       
   444         try {
       
   445             JSONObject mBeanInfo = getMBeanInfo(mBeanServer, objectName);
       
   446             return new HttpResponse(200, mBeanInfo.toJsonString());
       
   447         } catch (RuntimeOperationsException | IntrospectionException | ReflectionException e) {
       
   448             return new HttpResponse(HttpResponse.SERVER_ERROR, HttpResponse.getErrorMessage(e));
       
   449         } catch (InstanceNotFoundException e) {
       
   450             return new HttpResponse(HttpResponse.BAD_REQUEST, "Specified MBean does not exist");
       
   451         } catch (Exception e) {
       
   452             return new HttpResponse(HttpResponse.SERVER_ERROR, HttpResponse.getErrorMessage(e));
       
   453         }
       
   454     }
       
   455 
       
   456     @Override
       
   457     public void handle(HttpExchange exchange) throws IOException {
       
   458         String path = exchange.getRequestURI().getPath();
       
   459         if (path.matches(pathPrefix + "/?$")) {
       
   460             RestResource.super.handle(exchange);
       
   461         } else if (path.matches(pathPrefix + "/info$") && exchange.getRequestMethod().equalsIgnoreCase("GET")) {
       
   462             RestResource.super.handle(exchange);
       
   463         } else if (path.matches(pathPrefix + "/[^/]+/?$") && exchange.getRequestMethod().equalsIgnoreCase("POST")) {
       
   464             RestResource.super.handle(exchange);
       
   465         } else {
       
   466             HttpUtil.sendResponse(exchange, new HttpResponse(404, "Not found"));
       
   467         }
       
   468     }
       
   469 
       
   470     @Override
       
   471     public HttpResponse doGet(HttpExchange exchange) {
       
   472         if (exchange.getRequestURI().getPath().endsWith("info")) {
       
   473             return doMBeanInfo(exchange);
       
   474         }
       
   475         String path = PlatformRestAdapter.getDomain() + exchange.getRequestURI().getPath().replaceAll("\\/$", "");
       
   476         String info = path + "/info";
       
   477 
       
   478         try {
       
   479             Map<String, Object> allAttributes = getAllAttributes();
       
   480             Map<String, String> _links = new LinkedHashMap<>();
       
   481             _links.put("info", HttpUtil.escapeUrl(info));
       
   482 
       
   483             MBeanOperationInfo[] opInfo = mBeanServer.getMBeanInfo(objectName).getOperations();
       
   484             JSONArray jarr = new JSONArray();
       
   485             for (MBeanOperationInfo op : opInfo) {
       
   486                 JSONObject jobj1 = new JSONObject();
       
   487                 JSONArray jarr1 = new JSONArray();
       
   488                 jobj1.put("name", op.getName());
       
   489                 jobj1.put("href", HttpUtil.escapeUrl(path + "/" + op.getName()));
       
   490                 jobj1.put("method", "POST");
       
   491                 for (MBeanParameterInfo paramInfo : op.getSignature()) {
       
   492                     JSONObject jobj = new JSONObject();
       
   493                     jobj.put("name", paramInfo.getName());
       
   494                     jobj.put("type", paramInfo.getType());
       
   495                     jarr1.add(jobj);
       
   496                 }
       
   497                 jobj1.put("arguments", jarr1);
       
   498                 jobj1.put("returnType", op.getReturnType());
       
   499                 jarr.add(jobj1);
       
   500             }
       
   501 
       
   502             JSONMapper typeMapper = JSONMappingFactory.INSTANCE.getTypeMapper(allAttributes);
       
   503             if (typeMapper != null) {
       
   504                 JSONElement jsonElement1 = typeMapper.toJsonValue(allAttributes);
       
   505                 JSONElement jsonElement2 = typeMapper.toJsonValue(_links);
       
   506 
       
   507                 JSONObject jobj = new JSONObject();
       
   508                 jobj.put("attributes", jsonElement1);
       
   509                 jobj.put("operations", jarr);
       
   510                 jobj.put("_links", jsonElement2);
       
   511                 return new HttpResponse(200, jobj.toJsonString());
       
   512             } else {
       
   513                 return new HttpResponse(HttpResponse.SERVER_ERROR, "Unable to find JSONMapper");
       
   514             }
       
   515         } catch (RuntimeOperationsException | IntrospectionException | ReflectionException | JSONMappingException e) {
       
   516             return new HttpResponse(HttpResponse.SERVER_ERROR, HttpResponse.getErrorMessage(e));
       
   517         } catch (InstanceNotFoundException e) {
       
   518             return new HttpResponse(HttpResponse.BAD_REQUEST, "Specified MBean does not exist");
       
   519         } catch (AttributeNotFoundException e) {
       
   520             return new HttpResponse(HttpResponse.BAD_REQUEST, "Specified Attribute does not exist");
       
   521         } catch (MBeanException e) {
       
   522             Throwable cause = e.getCause();
       
   523             return new HttpResponse(HttpResponse.SERVER_ERROR, HttpResponse.getErrorMessage(e));
       
   524         } catch (Exception e) {
       
   525             return new HttpResponse(HttpResponse.SERVER_ERROR, HttpResponse.getErrorMessage(e));
       
   526         }
       
   527     }
       
   528 
       
   529     /*
       
   530     HTTP POST for this MBean's URL allows setting of attributes and execution of operations.
       
   531     POST request body can follow one of the below formats
       
   532     1. { name : value}
       
   533     Set a single attribute
       
   534     2. { name1 : value1, name2 : value2 }
       
   535     Sets multiple attributes
       
   536     3. {attributes : {read : [name]} , {write : {name : value}}, operations : {op_name : {param_name:name, param_value:value}}}
       
   537     This bulk operation request sets multiple attributes and executes multiple
       
   538     operations on the MBean.
       
   539      */
       
   540     @Override
       
   541     public HttpResponse doPost(HttpExchange exchange) {
       
   542         String path = exchange.getRequestURI().getPath();
       
   543         String reqBody = null;
       
   544         try {
       
   545             if (path.matches(pathPrefix + "/?$")) { // POST to current URL
       
   546                 reqBody = HttpUtil.readRequestBody(exchange);
       
   547                 if (reqBody == null || reqBody.isEmpty()) { // No Parameters
       
   548                     return HttpResponse.BAD_REQUEST;
       
   549                 }
       
   550 
       
   551                 JSONParser parser = new JSONParser(reqBody);
       
   552                 JSONElement jsonElement = parser.parse();
       
   553                 if (!(jsonElement instanceof JSONObject)) {
       
   554                     return new HttpResponse(HttpResponse.BAD_REQUEST,
       
   555                             "Invalid parameters : [" + reqBody + "]");
       
   556                 }
       
   557 
       
   558                 JSONObject jsonObject = (JSONObject) jsonElement;
       
   559 
       
   560                 if (jsonObject.keySet().contains("attributes") | jsonObject.keySet().contains("operations")) {
       
   561                     return new HttpResponse(HttpURLConnection.HTTP_OK, handleBulkRequest(exchange, jsonObject).toJsonString());
       
   562                 } else {
       
   563                     Map<String, Object> stringObjectMap = setAttributes(jsonObject);
       
   564                     JSONMapper typeMapper = JSONMappingFactory.INSTANCE.getTypeMapper(stringObjectMap);
       
   565                     if (typeMapper != null) {
       
   566                         return new HttpResponse(HttpURLConnection.HTTP_OK, typeMapper.toJsonValue(stringObjectMap).toJsonString());
       
   567                     } else {
       
   568                         return new HttpResponse(HttpResponse.SERVER_ERROR, "Unable to find JSON Mapper");
       
   569                     }
       
   570                 }
       
   571             } else if (path.matches(pathPrefix + "/[^/]+/?$")) {  // POST to MBeanOperation
       
   572                 Matcher matcher = Pattern.compile(pathPrefix + "/").matcher(path);
       
   573                 String operation;
       
   574                 if (matcher.find()) {
       
   575                     String ss = path.substring(matcher.end());
       
   576                     operation = ss;
       
   577                 } else {
       
   578                     return HttpResponse.BAD_REQUEST;
       
   579                 }
       
   580 
       
   581                 reqBody = HttpUtil.readRequestBody(exchange);
       
   582                 JSONElement result;
       
   583                 if (reqBody == null || reqBody.isEmpty()) { // No Parameters
       
   584                     result = execOperation(operation, null);
       
   585                 } else {
       
   586                     JSONParser parser = new JSONParser(reqBody);
       
   587                     JSONElement jsonElement = parser.parse();
       
   588                     if (!(jsonElement instanceof JSONObject)) {
       
   589                         return new HttpResponse(HttpResponse.BAD_REQUEST,
       
   590                                 "Invalid parameters : [" + reqBody + "] for operation - " + operation);
       
   591                     }
       
   592                     result = execOperation(operation, (JSONObject) jsonElement);
       
   593                 }
       
   594                 return new HttpResponse(HttpURLConnection.HTTP_OK, result.toJsonString());
       
   595             } else {
       
   596                 return HttpResponse.REQUEST_NOT_FOUND;
       
   597             }
       
   598         } catch (InstanceNotFoundException e) {
       
   599             // Should never happen
       
   600         } catch (JSONDataException | ParseException e) {
       
   601             return new HttpResponse(HttpURLConnection.HTTP_BAD_REQUEST, "Invalid JSON : " + reqBody, e.getMessage());
       
   602         } catch (IntrospectionException | JSONMappingException | MBeanException | ReflectionException | IOException e) {
       
   603             return new HttpResponse(HttpResponse.SERVER_ERROR, HttpResponse.getErrorMessage(e));
       
   604         } catch (IllegalArgumentException e) {
       
   605             return new HttpResponse(HttpResponse.BAD_REQUEST, e.getMessage());
       
   606         } catch (Exception e) {
       
   607             return new HttpResponse(HttpResponse.SERVER_ERROR, HttpResponse.getErrorMessage(e));
       
   608         }
       
   609         return HttpResponse.REQUEST_NOT_FOUND;
       
   610     }
       
   611 
       
   612     public JSONElement handleBulkRequest(HttpExchange exchange, JSONObject reqObject) {
       
   613 
       
   614         JSONObject result = new JSONObject();
       
   615 
       
   616         // Handle attributes
       
   617         JSONElement element = reqObject.get("attributes");
       
   618         if (element != null && element instanceof JSONObject) {
       
   619             JSONObject attrInfo = (JSONObject) element;
       
   620             JSONObject attrNode = new JSONObject();
       
   621             // Read attributes
       
   622             JSONElement read = attrInfo.get("get");
       
   623             if (read != null && read instanceof JSONArray) {
       
   624                 JSONArray jattrs = (JSONArray) read;
       
   625                 JSONElement jAttrRead;
       
   626                 Map<String, Object> attrRead = null;
       
   627                 try {
       
   628                     String[] attributes = getStrings(jattrs);
       
   629                     attrRead = getAttributes(attributes);
       
   630                     JSONMapper typeMapper = JSONMappingFactory.INSTANCE.getTypeMapper(attrRead);
       
   631                     jAttrRead = typeMapper.toJsonValue(attrRead);
       
   632                 } catch (InstanceNotFoundException | ReflectionException | JSONMappingException e) {
       
   633                     jAttrRead = new JSONPrimitive("<ERROR: Unable to retrieve value>");
       
   634                 } catch (JSONDataException e) {
       
   635                     jAttrRead = new JSONPrimitive("Invalid JSON : " + read.toJsonString());
       
   636                 }
       
   637 
       
   638                 attrNode.put("get", jAttrRead);
       
   639             }
       
   640 
       
   641             // Write attributes
       
   642             JSONElement write = attrInfo.get("set");
       
   643             JSONElement jAttrRead;
       
   644             if (write != null && write instanceof JSONObject) {
       
   645                 JSONObject jattrs = (JSONObject) write;
       
   646                 try {
       
   647                     Map<String, Object> attrMap = setAttributes(jattrs);
       
   648                     JSONMapper typeMapper = JSONMappingFactory.INSTANCE.getTypeMapper(attrMap);
       
   649                     jAttrRead = typeMapper.toJsonValue(attrMap);
       
   650                 } catch (JSONDataException ex) {
       
   651                     jAttrRead = new JSONPrimitive("Invalid JSON : " + write.toJsonString());
       
   652                 } catch (JSONMappingException | IntrospectionException | InstanceNotFoundException | ReflectionException e) {
       
   653                     jAttrRead = new JSONPrimitive("<ERROR: Unable to retrieve value>");
       
   654                 }
       
   655                 attrNode.put("set", jAttrRead);
       
   656             }
       
   657             result.put("attributes", attrNode);
       
   658         }
       
   659 
       
   660         // Execute operations
       
   661         element = reqObject.get("operations");
       
   662         if (element != null) {
       
   663             JSONArray operationList;
       
   664             if (element instanceof JSONPrimitive             // Single no-arg operation
       
   665                     || element instanceof JSONObject) {     // single/mulitple operations
       
   666                 operationList = new JSONArray();
       
   667                 operationList.add(element);
       
   668             } else if (element instanceof JSONArray) {  // List of no-arg/with-arg operation
       
   669                 operationList = (JSONArray) element;
       
   670             } else {
       
   671                 operationList = new JSONArray();
       
   672             }
       
   673             JSONObject opResult = new JSONObject();
       
   674             for (JSONElement elem : operationList) {
       
   675                 if (elem instanceof JSONPrimitive
       
   676                         && ((JSONPrimitive) elem).getValue() instanceof String) { // no-arg operation
       
   677                     String opName = (String) ((JSONPrimitive) elem).getValue();
       
   678                     try {
       
   679                         JSONElement obj = execOperation(opName, null);
       
   680                         opResult.put(opName, obj);
       
   681                     } catch (IllegalArgumentException e) {
       
   682                         opResult.put(opName, e.getMessage());
       
   683                     } catch (IntrospectionException | InstanceNotFoundException | MBeanException | ReflectionException e) {
       
   684                         opResult.put(opName, "<ERROR while executing operation>");
       
   685                     }
       
   686                 } else if (elem instanceof JSONObject) {
       
   687                     Set<String> opNames = ((JSONObject) element).keySet();
       
   688                     for (String opName : opNames) {
       
   689                         try {
       
   690                             JSONElement obj = execOperation(opName, (JSONObject) ((JSONObject) element).get(opName));
       
   691                             opResult.put(opName, obj);
       
   692                         } catch (IllegalArgumentException e) {
       
   693                             opResult.put(opName, e.getMessage());
       
   694                         } catch (IntrospectionException | InstanceNotFoundException | MBeanException | ReflectionException e) {
       
   695                             opResult.put(opName, "<ERROR while executing operation>");
       
   696                         }
       
   697                     }
       
   698                 }
       
   699             }
       
   700             result.put("operations", opResult);
       
   701         }
       
   702         return result;
       
   703     }
       
   704 
       
   705     private String[] getStrings(JSONArray jsonArray) throws JSONDataException {
   704     private String[] getStrings(JSONArray jsonArray) throws JSONDataException {
   706         List<String> attributes = new ArrayList<>();
   705         List<String> attributes = new ArrayList<>();
   707         for (JSONElement element : jsonArray) {
   706         for (JSONElement element : jsonArray) {
   708             if (element instanceof JSONPrimitive && ((JSONPrimitive) element).getValue() instanceof String) {
   707             if (element instanceof JSONPrimitive && ((JSONPrimitive) element).getValue() instanceof String) {
   709                 JSONPrimitive val = (JSONPrimitive) element;
   708                 JSONPrimitive val = (JSONPrimitive) element;
   710                 attributes.add((String) val.getValue());
   709                 attributes.add((String) val.getValue());
   711             } else throw new JSONDataException("Expecting String, got " + element.toJsonString());
   710             } else throw new JSONDataException("Expecting String, got " + element.toJsonString());
   712         }
   711         }
   713         return attributes.toArray(new String[0]);
   712         return attributes.toArray(new String[0]);
   714     }
   713     }
   715 
       
   716     @Override
       
   717     public HttpResponse doPut(HttpExchange exchange) {
       
   718         return null;
       
   719     }
       
   720 
       
   721     @Override
       
   722     public HttpResponse doDelete(HttpExchange exchange) {
       
   723         return null;
       
   724     }
       
   725 
       
   726     @Override
       
   727     public HttpResponse doHead(HttpExchange exchange) {
       
   728         return null;
       
   729     }
       
   730 
       
   731 }
   714 }