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