# HG changeset patch # User hb # Date 1514471703 -19800 # Node ID 054866e5113c7e3965eea50faa66b46cdb342171 # Parent 2db04c2274fd791fc1231a947053ab364d08661c Code cleanup - MBeanColl*Resource, MBeanResource diff -r 2db04c2274fd -r 054866e5113c src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/http/MBeanCollectionResource.java --- a/src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/http/MBeanCollectionResource.java Thu Dec 28 14:43:34 2017 +0530 +++ b/src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/http/MBeanCollectionResource.java Thu Dec 28 20:05:03 2017 +0530 @@ -53,9 +53,19 @@ private static final int pageSize = 10; private static final String pathPrefix = "^/?jmx/servers/[a-zA-Z0-9\\-\\.]+/mbeans"; + // Only MXBean or any other MBean that uses types + // that have a valid mapper functions private boolean isMBeanAllowed(ObjectName objName) { try { MBeanInfo mInfo = mBeanServer.getMBeanInfo(objName); + + // Return true for MXbean + Descriptor desc = mInfo.getDescriptor(); + String isMxBean = (String) desc.getFieldValue("mxbean"); + if (isMxBean.equalsIgnoreCase("true")) + return true; + + // Check attribute types MBeanAttributeInfo[] attrsInfo = mInfo.getAttributes(); for (MBeanAttributeInfo attrInfo : attrsInfo) { String type = attrInfo.getType(); @@ -63,6 +73,8 @@ return false; } } + + // Check operation parameters and return types MBeanOperationInfo[] operations = mInfo.getOperations(); for (MBeanOperationInfo opInfo : operations) { MBeanParameterInfo[] signature = opInfo.getSignature(); @@ -76,7 +88,8 @@ } } return true; - } catch (InstanceNotFoundException | IntrospectionException | ReflectionException | ClassNotFoundException ex) { + } catch (InstanceNotFoundException | IntrospectionException | + ReflectionException | ClassNotFoundException ex) { ex.printStackTrace(); return false; } @@ -113,6 +126,8 @@ allowedMbeans = new ArrayList<>(); introspectMBeanTypes(mBeanServer); allowedMbeans = new CopyOnWriteArrayList<>(allowedMbeans); + + // Create a REST handler for each MBean allowedMbeans.forEach(objectName -> mBeanResourceMap.put(objectName.toString(), new MBeanResource(mBeanServer, objectName))); } @@ -136,7 +151,7 @@ } MBeanResource mBeanResource = mBeanResourceMap.get(mBeanName); if (mBeanResource == null) { - HttpUtil.sendResponse(exchange, new HttpResponse(404, "Not found")); + HttpUtil.sendResponse(exchange, HttpResponse.REQUEST_NOT_FOUND); return; } mBeanResource.handle(exchange); @@ -146,20 +161,21 @@ @Override public HttpResponse doGet(HttpExchange exchange) { - // add links - - final String path = PlatformRestAdapter.getDomain() + exchange.getRequestURI().getPath().replaceAll("\\/$", ""); + final String path = PlatformRestAdapter.getDomain() + + exchange.getRequestURI().getPath().replaceAll("/$", ""); try { - List mbeans = allowedMbeans; + List filteredMBeans = allowedMbeans; Map queryMap = HttpUtil.getGetRequestQueryMap(exchange); - if (queryMap.containsKey("query")) { - Set queryMBeans = mBeanServer.queryNames(new ObjectName(queryMap.get("query")), null); - queryMBeans.retainAll(allowedMbeans); - mbeans = new ArrayList<>(queryMBeans); + if (queryMap.containsKey("query")) { // Filter based on ObjectName query + Set queryMBeans = mBeanServer + .queryNames(new ObjectName(queryMap.get("query")), null); + queryMBeans.retainAll(allowedMbeans); // Intersection of two lists + filteredMBeans = new ArrayList<>(queryMBeans); } - JSONObject _links = HttpUtil.getPaginationLinks(exchange, mbeans, pageSize); - List filteredMBeans = HttpUtil.filterByPage(exchange, mbeans, pageSize); + JSONObject _links = HttpUtil.getPaginationLinks(exchange, filteredMBeans, pageSize); + filteredMBeans = HttpUtil.filterByPage(exchange, filteredMBeans, pageSize); + List> items = new ArrayList<>(filteredMBeans.size()); filteredMBeans.forEach(objectName -> { Map item = new LinkedHashMap<>(2); @@ -172,7 +188,7 @@ Map properties = new HashMap<>(); - properties.put("mbeanCount", Integer.toString(mbeans.size())); + properties.put("mbeanCount", Integer.toString(filteredMBeans.size())); JSONMapper typeMapper1 = JSONMappingFactory.INSTANCE.getTypeMapper(items); JSONMapper typeMapper2 = JSONMappingFactory.INSTANCE.getTypeMapper(properties); @@ -180,29 +196,24 @@ JSONElement linkElem = typeMapper1.toJsonValue(items); JSONElement propElem = typeMapper2.toJsonValue(properties); JSONObject jobj = new JSONObject(); + + jobj.putAll((JSONObject) propElem); + jobj.put("mbeans", linkElem); + if (_links != null && !_links.isEmpty()) { jobj.put("_links", _links); } - - jobj.putAll((JSONObject) propElem); - jobj.put("items", linkElem); - - return new HttpResponse(200, jobj.toJsonString()); + return new HttpResponse(jobj.toJsonString()); } catch (JSONMappingException e) { - return new HttpResponse(500, "Internal server error"); + return HttpResponse.SERVER_ERROR; } catch (UnsupportedEncodingException e) { - return HttpResponse.SERVER_ERROR; + return HttpResponse.BAD_REQUEST; } catch (MalformedObjectNameException e) { return new HttpResponse(HttpResponse.BAD_REQUEST, "Invalid query string"); } } @Override - public HttpResponse doPut(HttpExchange exchange) { - return null; - } - - @Override public HttpResponse doPost(HttpExchange exchange) { String path = exchange.getRequestURI().getPath(); String reqBody = null; @@ -224,22 +235,20 @@ JSONObject result = new JSONObject(); for (String mBeanName : jsonObject.keySet()) { MBeanResource mBeanResource = mBeanResourceMap.get(mBeanName); - try { - if (mBeanResource == null) { - result.put(mBeanName, "Invalid MBean"); - } else { + if (mBeanResource != null) { JSONElement element = jsonObject.get(mBeanName); if (element instanceof JSONObject) { - JSONElement res = mBeanResource.handleBulkRequest(exchange, (JSONObject) element); + JSONElement res = mBeanResource.handleBulkRequest + ((JSONObject) element); result.put(mBeanName, res); } else { result.put(mBeanName, "Invalid input"); } - }} catch (Throwable e) { - e.printStackTrace(); + } else { + result.put(mBeanName, "Invalid MBean"); } } - return new HttpResponse(HttpURLConnection.HTTP_OK, result.toJsonString()); + return new HttpResponse(result.toJsonString()); } else { return HttpResponse.METHOD_NOT_ALLOWED; } @@ -249,14 +258,4 @@ return HttpResponse.BAD_REQUEST; } } - - @Override - public HttpResponse doDelete(HttpExchange exchange) { - return null; - } - - @Override - public HttpResponse doHead(HttpExchange exchange) { - return null; - } } diff -r 2db04c2274fd -r 054866e5113c src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/http/MBeanResource.java --- a/src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/http/MBeanResource.java Thu Dec 28 14:43:34 2017 +0530 +++ b/src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/http/MBeanResource.java Thu Dec 28 20:05:03 2017 +0530 @@ -47,6 +47,8 @@ import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; import static javax.management.MBeanOperationInfo.*; @@ -70,17 +72,298 @@ primitiveToObject.put("short", Short.TYPE); } - public MBeanResource(MBeanServer mBeanServer, ObjectName objectName) { + MBeanResource(MBeanServer mBeanServer, ObjectName objectName) { this.mBeanServer = mBeanServer; this.objectName = objectName; } - private JSONObject getMBeanInfo(MBeanServer mbeanServer, ObjectName mbean) throws InstanceNotFoundException, IntrospectionException, ReflectionException { + @Override + public void handle(HttpExchange exchange) throws IOException { + String path = exchange.getRequestURI().getPath(); + if (path.matches(pathPrefix + "/?$")) { + RestResource.super.handle(exchange); + } else if (path.matches(pathPrefix + "/info$") + && exchange.getRequestMethod().equalsIgnoreCase("GET")) { + RestResource.super.handle(exchange); + } else if (path.matches(pathPrefix + "/[^/]+/?$") + && exchange.getRequestMethod().equalsIgnoreCase("POST")) { + RestResource.super.handle(exchange); + } else { + HttpUtil.sendResponse(exchange, HttpResponse.REQUEST_NOT_FOUND); + } + } + + @Override + public HttpResponse doGet(HttpExchange exchange) { + String path = PlatformRestAdapter.getDomain() + + exchange.getRequestURI().getPath().replaceAll("/$", ""); + + if (path.endsWith("info")) { + return doMBeanInfo(); + } + + String infoPath = path + "/info"; + + try { + Map allAttributes = getAllAttributes(); + Map _links = new LinkedHashMap<>(); + _links.put("info", HttpUtil.escapeUrl(infoPath)); + + MBeanOperationInfo[] opInfo = mBeanServer.getMBeanInfo(objectName).getOperations(); + JSONArray jarr = new JSONArray(); + for (MBeanOperationInfo op : opInfo) { + JSONObject jobj1 = new JSONObject(); + JSONArray jarr1 = new JSONArray(); + jobj1.put("name", op.getName()); + jobj1.put("href", HttpUtil.escapeUrl(path + "/" + op.getName())); + jobj1.put("method", "POST"); + for (MBeanParameterInfo paramInfo : op.getSignature()) { + JSONObject jobj = new JSONObject(); + jobj.put("name", paramInfo.getName()); + jobj.put("type", paramInfo.getType()); + jarr1.add(jobj); + } + jobj1.put("arguments", jarr1); + jobj1.put("returnType", op.getReturnType()); + jarr.add(jobj1); + } + + JSONMapper typeMapper = JSONMappingFactory.INSTANCE.getTypeMapper(allAttributes); + if (typeMapper != null) { + JSONElement jsonElement1 = typeMapper.toJsonValue(allAttributes); + JSONElement jsonElement2 = typeMapper.toJsonValue(_links); + + JSONObject jobj = new JSONObject(); + jobj.put("attributes", jsonElement1); + jobj.put("operations", jarr); + jobj.put("_links", jsonElement2); + return new HttpResponse(jobj.toJsonString()); + } else { + return HttpResponse.SERVER_ERROR; + } + } catch (RuntimeOperationsException | IntrospectionException | ReflectionException + | JSONMappingException | MBeanException e) { + return HttpResponse.SERVER_ERROR; + } catch (InstanceNotFoundException e) { + return new HttpResponse(HttpResponse.BAD_REQUEST, "Specified MBean does not exist"); + } catch (Exception e) { + return HttpResponse.SERVER_ERROR; + } + } + + /* + HTTP POST for this MBean's URL allows setting of attributes and execution of operations. + POST request body can follow one of the below formats + 1. { name : value} + Set a single attribute + 2. { name1 : value1, name2 : value2 } + Sets multiple attributes + 3. {attributes : {read : [name]} , {write : {name : value}}, operations : {op_name : {param_name:name, param_value:value}}} + This bulk operation request sets multiple attributes and executes multiple + operations on the MBean. + */ + @Override + public HttpResponse doPost(HttpExchange exchange) { + String path = exchange.getRequestURI().getPath(); + String reqBody = null; + try { + if (path.matches(pathPrefix + "/?$")) { // POST to current URL + reqBody = HttpUtil.readRequestBody(exchange); + if (reqBody.isEmpty()) { // No Parameters + return HttpResponse.BAD_REQUEST; + } + + JSONParser parser = new JSONParser(reqBody); + JSONElement jsonElement = parser.parse(); + if (!(jsonElement instanceof JSONObject)) { + return new HttpResponse(HttpResponse.BAD_REQUEST, + "Invalid parameters : [" + reqBody + "]"); + } + + JSONObject jsonObject = (JSONObject) jsonElement; + + // Handle bulk operation + if (jsonObject.keySet().contains("attributes") | jsonObject.keySet().contains("operations")) { + return new HttpResponse(handleBulkRequest(jsonObject).toJsonString()); + } else { // Handle attribute update + Map stringObjectMap = setAttributes(jsonObject); + JSONMapper typeMapper = JSONMappingFactory.INSTANCE.getTypeMapper(stringObjectMap); + if (typeMapper != null) { + return new HttpResponse(HttpURLConnection.HTTP_OK, typeMapper.toJsonValue(stringObjectMap).toJsonString()); + } else { + return new HttpResponse(HttpResponse.SERVER_ERROR, "Unable to find JSON Mapper"); + } + } + } else if (path.matches(pathPrefix + "/[^/]+/?$")) { // POST to MBeanOperation + Matcher matcher = Pattern.compile(pathPrefix + "/").matcher(path); + String operation; + if (matcher.find()) { + operation = path.substring(matcher.end()); + } else { + return HttpResponse.BAD_REQUEST; + } + + reqBody = HttpUtil.readRequestBody(exchange); + JSONElement result; + if (reqBody.isEmpty()) { // No Parameters + result = execOperation(operation, null); + } else { + JSONParser parser = new JSONParser(reqBody); + JSONElement jsonElement = parser.parse(); + if (!(jsonElement instanceof JSONObject)) { + return new HttpResponse(HttpResponse.BAD_REQUEST, + "Invalid parameters : [" + reqBody + "] for operation - " + operation); + } + result = execOperation(operation, (JSONObject) jsonElement); + } + return new HttpResponse(HttpURLConnection.HTTP_OK, result.toJsonString()); + } else { + return HttpResponse.REQUEST_NOT_FOUND; + } + } catch (InstanceNotFoundException e) { + // Should never happen + } catch (JSONDataException | ParseException e) { + return new HttpResponse(HttpURLConnection.HTTP_BAD_REQUEST, "Invalid JSON : " + reqBody, e.getMessage()); + } catch (IntrospectionException | JSONMappingException | MBeanException | ReflectionException | IOException e) { + return new HttpResponse(HttpResponse.SERVER_ERROR, HttpResponse.getErrorMessage(e)); + } catch (IllegalArgumentException e) { + return new HttpResponse(HttpResponse.BAD_REQUEST, e.getMessage()); + } catch (Exception e) { + return new HttpResponse(HttpResponse.SERVER_ERROR, HttpResponse.getErrorMessage(e)); + } + return HttpResponse.REQUEST_NOT_FOUND; + } + + private HttpResponse doMBeanInfo() { + try { + JSONObject mBeanInfo = getMBeanInfo(mBeanServer, objectName); + return new HttpResponse(mBeanInfo.toJsonString()); + } catch (RuntimeOperationsException | IntrospectionException | ReflectionException e) { + return HttpResponse.SERVER_ERROR; + } catch (InstanceNotFoundException e) { + return new HttpResponse(HttpResponse.BAD_REQUEST, "Specified MBean does not exist"); + } catch (Exception e) { + return HttpResponse.SERVER_ERROR; + } + } + + JSONElement handleBulkRequest(JSONObject reqObject) { + JSONObject result = new JSONObject(); + + // Handle attributes + JSONElement element = reqObject.get("attributes"); + if (element != null && element instanceof JSONObject) { + JSONObject attrInfo = (JSONObject) element; + JSONObject attrNode = new JSONObject(); + + // Read attributes + JSONElement read = attrInfo.get("get"); + if (read != null) { + if (read instanceof JSONArray) { + JSONArray jsonAttrMap = (JSONArray) read; + JSONElement resultJson; + Map attrRead; + try { + String[] attributes = getStrings(jsonAttrMap); + attrRead = getAttributes(attributes); + JSONMapper typeMapper = JSONMappingFactory.INSTANCE.getTypeMapper(attrRead); + resultJson = typeMapper.toJsonValue(attrRead); + } catch (InstanceNotFoundException | ReflectionException | JSONMappingException | MBeanException e) { + resultJson = new JSONPrimitive(""); + } catch (JSONDataException e) { + resultJson = new JSONPrimitive("Invalid JSON : " + e.getMessage()); + } + attrNode.put("get", resultJson); + } else { + attrInfo.put("get", new JSONPrimitive("Invalid JSON : " + read.toJsonString())); + } + } + + // Write attributes + JSONElement write = attrInfo.get("set"); + + if (write != null) { + if (write instanceof JSONObject) { + JSONElement resultJson; + JSONObject jsonAttrMap = (JSONObject) write; + try { + Map attrMap = setAttributes(jsonAttrMap); + JSONMapper typeMapper = JSONMappingFactory.INSTANCE.getTypeMapper(attrMap); + resultJson = typeMapper.toJsonValue(attrMap); + } catch (JSONDataException ex) { + resultJson = new JSONPrimitive("Invalid JSON : " + write.toJsonString()); + } catch (JSONMappingException | IntrospectionException | InstanceNotFoundException | ReflectionException e) { + resultJson = new JSONPrimitive(""); + } + attrNode.put("set", resultJson); + } else { + attrNode.put("set", new JSONPrimitive("Invalid JSON : " + write.toJsonString())); + } + } + result.put("attributes", attrNode); + } + + // Execute operations + element = reqObject.get("operations"); + if (element != null) { + JSONArray operationList; + if (element instanceof JSONPrimitive // Single no-arg operation + || element instanceof JSONObject) { // single/mulitple operations + operationList = new JSONArray(); + operationList.add(element); + } else if (element instanceof JSONArray) { // List of no-arg/with-arg operation + operationList = (JSONArray) element; + } else { + operationList = new JSONArray(); + } + JSONObject opResult = new JSONObject(); + operationList.forEach((elem) -> { + if (elem instanceof JSONPrimitive + && ((JSONPrimitive) elem).getValue() instanceof String) { // no-arg operation + String opName = (String) ((JSONPrimitive) elem).getValue(); + try { + JSONElement obj = execOperation(opName, null); + opResult.put(opName, obj); + } catch (IllegalArgumentException e) { + opResult.put(opName, e.getMessage()); + } catch (IntrospectionException | InstanceNotFoundException + | MBeanException | ReflectionException e) { + opResult.put(opName, ""); + } + } else if (elem instanceof JSONObject) { + ((JSONObject) elem).keySet().forEach((opName) -> { + try { + JSONElement jsonElement = ((JSONObject) elem).get(opName); + if (jsonElement instanceof JSONObject) { + JSONElement obj = execOperation(opName, (JSONObject) jsonElement); + opResult.put(opName, obj); + } else { + opResult.put(opName, new JSONPrimitive("Invalid parameter JSON")); + } + } catch (IllegalArgumentException e) { + opResult.put(opName, e.getMessage()); + } catch (IntrospectionException | InstanceNotFoundException + | MBeanException | ReflectionException e) { + opResult.put(opName, ""); + } + }); + } + }); + result.put("operations", opResult); + } + return result; + } + + private JSONObject getMBeanInfo(MBeanServer mbeanServer, ObjectName mbean) + throws InstanceNotFoundException, IntrospectionException, ReflectionException { + JSONObject jobj = new JSONObject(); MBeanInfo mBeanInfo = mbeanServer.getMBeanInfo(mbean); if (mBeanInfo == null) { return jobj; } + jobj.put("name", mbean.toString()); + jobj.put("className", mBeanInfo.getClassName()); jobj.put("description", mBeanInfo.getDescription()); // Populate Attribute Info @@ -109,6 +392,7 @@ } jobj.put("attributeInfo", jarr); + // Add constructor Info MBeanConstructorInfo[] constructorInfo = mBeanInfo.getConstructors(); jarr = new JSONArray(); @@ -172,9 +456,7 @@ jarr = new JSONArray(); for (MBeanNotificationInfo notification : notifications) { - JSONObject jobj1 = new JSONObject(); - jobj1.put("name", notification.getName()); JSONArray jarr1 = new JSONArray(); for (String notifType : notification.getNotifTypes()) { @@ -195,11 +477,11 @@ private JSONObject getParamJSON(MBeanParameterInfo mParamInfo) { JSONObject jobj1 = new JSONObject(); + jobj1.put("name", mParamInfo.getName()); + jobj1.put("type", mParamInfo.getType()); if (mParamInfo.getDescription() != null && !mParamInfo.getDescription().isEmpty()) { jobj1.put("description", mParamInfo.getDescription()); } - jobj1.put("name", mParamInfo.getName()); - jobj1.put("type", mParamInfo.getType()); if (mParamInfo.getDescriptor() != null && mParamInfo.getDescriptor().getFieldNames().length > 1) { jobj1.put("descriptor", getDescriptorJSON(mParamInfo.getDescriptor())); } @@ -208,85 +490,71 @@ private JSONObject getDescriptorJSON(Descriptor descriptor) { JSONObject jobj2 = new JSONObject(); - try { - String[] descNames = descriptor.getFieldNames(); - for (String descName : descNames) { - Object fieldValue = descriptor.getFieldValue(descName); - jobj2.put(descName, fieldValue != null ? fieldValue.toString() : null); - } - } catch (Throwable t) { - t.printStackTrace(); + String[] descNames = descriptor.getFieldNames(); + for (String descName : descNames) { + Object fieldValue = descriptor.getFieldValue(descName); + jobj2.put(descName, fieldValue != null ? fieldValue.toString() : null); } return jobj2; } - private Map getAttributes(String[] attrs) throws InstanceNotFoundException, - ReflectionException { + private Map getAttributes(String[] attrs) throws + InstanceNotFoundException, ReflectionException, MBeanException { + Map result = new LinkedHashMap<>(); + if (attrs == null || attrs.length == 0) { return result; } + AttributeList attrVals = mBeanServer.getAttributes(objectName, attrs); - if (attrVals.size() != attrs.length) { - List missingAttrs = new ArrayList<>(Arrays.asList(attrs)); - for (Attribute a : attrVals.asList()) { - missingAttrs.remove(a.getName()); - result.put(a.getName(), a.getValue()); - } - for (String attr : missingAttrs) { - result.put(attr, "< Error: No such attribute >"); - } - } else { - attrVals.asList().forEach((a) -> result.put(a.getName(), a.getValue())); - } - - return result; - } + List missingAttrs = Arrays.asList(attrs); + attrVals.asList().forEach(a -> { + missingAttrs.remove(a.getName()); + result.put(a.getName(), a.getValue()); + }); - private Map getAllAttributes() throws IntrospectionException, - InstanceNotFoundException, ReflectionException, AttributeNotFoundException, MBeanException { - Map result = new HashMap<>(); - MBeanInfo mInfo = mBeanServer.getMBeanInfo(objectName); - String[] attrs = Arrays.stream(mInfo.getAttributes()) - .map(MBeanAttributeInfo::getName) - .toArray(String[]::new); - AttributeList attrVals = mBeanServer.getAttributes(objectName, attrs); - if (attrVals.size() != attrs.length) { - List missingAttrs = new ArrayList<>(Arrays.asList(attrs)); - for (Attribute a : attrVals.asList()) { - missingAttrs.remove(a.getName()); - result.put(a.getName(), a.getValue()); + for (String attr : missingAttrs) { + try { + mBeanServer.getAttribute(objectName, attr); + result.put(attr, "< Error: No such attribute >"); + } catch (RuntimeException ex) { + if (ex.getCause() instanceof UnsupportedOperationException) { + result.put(attr, "< Attribute not supported >"); + } else if (ex.getCause() instanceof IllegalArgumentException) { + result.put(attr, "< Invalid attributes >"); + } + } catch (AttributeNotFoundException e) { + result.put(attr, "< Attribute not found >"); } - for (String attr : missingAttrs) { - try { - Object attribute = mBeanServer.getAttribute(objectName, attr); - } catch (RuntimeException ex) { - if (ex.getCause() instanceof UnsupportedOperationException) { - result.put(attr, "< Attribute not supported >"); - } else if (ex.getCause() instanceof IllegalArgumentException) { - result.put(attr, "< Invalid attributes >"); - } - continue; - } - result.put(attr, "< Error: No such attribute >"); - } - } else { - attrVals.asList().forEach((a) -> { - result.put(a.getName(), a.getValue()); - }); } return result; } + private Map getAllAttributes() throws InstanceNotFoundException, + ReflectionException, MBeanException, IntrospectionException { + + MBeanInfo mInfo = mBeanServer.getMBeanInfo(objectName); + String[] attrs = Stream.of(mInfo.getAttributes()) + .map(MBeanAttributeInfo::getName) + .toArray(String[]::new); + + return getAttributes(attrs); + } + private Map setAttributes(JSONObject attrMap) throws JSONDataException, IntrospectionException, InstanceNotFoundException, ReflectionException { + if (attrMap == null || attrMap.isEmpty()) { throw new JSONDataException("Null arguments for set attribute"); } + + MBeanInfo mBeanInfo = mBeanServer.getMBeanInfo(objectName); Map result = new HashMap<>(); + for (String attrName : attrMap.keySet()) { - MBeanInfo mBeanInfo = mBeanServer.getMBeanInfo(objectName); - MBeanAttributeInfo attrInfo = Arrays.stream(mBeanInfo.getAttributes()).filter(a -> a.getName().equals(attrName)).findFirst().orElse(null); + MBeanAttributeInfo attrInfo = Arrays.stream(mBeanInfo.getAttributes()). + filter(a -> a.getName().equals(attrName)).findFirst().orElse(null); if (attrInfo == null) { result.put(attrName, ""); } else if (!attrInfo.isWritable()) { @@ -303,7 +571,8 @@ inputCls = Class.forName(attrInfo.getType()); } } catch (ClassNotFoundException | ClassCastException ex) { - throw new IllegalArgumentException("Invalid parameters : " + attrMap.get(attrName).toJsonString() + " cannot be mapped to : " + attrInfo.getType()); + throw new IllegalArgumentException("Invalid parameters : " + + attrMap.get(attrName).toJsonString() + " cannot be mapped to : " + attrInfo.getType()); } mapper = JSONMappingFactory.INSTANCE.getTypeMapper(inputCls); } @@ -324,7 +593,7 @@ return result; } - private Object mapJsonToType(JSONElement jsonElement, MBeanParameterInfo type) { + private Object mapJsonElemToType(JSONElement jsonElement, MBeanParameterInfo type) { if (type instanceof OpenMBeanParameterInfo) { OpenType openType = ((OpenMBeanParameterInfo) type).getOpenType(); JSONMapper typeMapper = JSONMappingFactory.INSTANCE.getTypeMapper(openType); @@ -354,23 +623,23 @@ } } - private Map getParameters(Map jsonValues, Map typeMap) { + private Map getOperationParameters(Map jsonValues, Map typeMap) { if (jsonValues.size() != typeMap.size()) { throw new IllegalArgumentException("Invalid parameters : expected - " + typeMap.size() + " parameters, got - " + jsonValues.size()); } if (!jsonValues.keySet().equals(typeMap.keySet())) { throw new IllegalArgumentException("Invalid parameters - expected : " + Arrays.toString(typeMap.keySet().toArray())); } - Map parameters = new LinkedHashMap<>(); - if (typeMap.size() == 0 && jsonValues.isEmpty()) { + Map parameters = new LinkedHashMap<>(); // Order of parameters should be same as typeMap + if (typeMap.isEmpty() && jsonValues.isEmpty()) { return parameters; } - for (String name : typeMap.keySet()) { + typeMap.keySet().forEach((name) -> { MBeanParameterInfo type = typeMap.get(name); JSONElement jsonVal = jsonValues.get(name); - Object obj = mapJsonToType(jsonVal, type); + Object obj = mapJsonElemToType(jsonVal, type); parameters.put(name, obj); - } + }); return parameters; } @@ -386,43 +655,35 @@ throw new IllegalArgumentException("MBean does not exist"); } - MBeanOperationInfo[] opinfos = Arrays.stream(mBeanInfo.getOperations()). - filter(a -> a.getName().equals(opstr)).toArray(MBeanOperationInfo[]::new); + List mBeanOperationInfos = Arrays.stream(mBeanInfo.getOperations()). + filter(a -> a.getName().equals(opstr)).collect(Collectors.toList()); - if (opinfos.length == 0) { + if (mBeanOperationInfos.isEmpty()) { throw new IllegalArgumentException("Invalid Operation String"); } String[] signature = null; Object[] parameters = null; - if (opinfos.length == 1) { - MBeanParameterInfo[] sig = opinfos[0].getSignature(); - Map typeMap = new LinkedHashMap<>(); - Arrays.stream(sig).forEach(e -> typeMap.put(e.getName(), e)); - parameters = getParameters(params, typeMap).values().toArray(); - signature = Arrays.asList(sig).stream().map(a -> a.getType()).toArray(a -> new String[a]); - } else if (opinfos.length > 1) { - IllegalArgumentException exception = null; - for (MBeanOperationInfo opInfo : opinfos) { - MBeanParameterInfo[] sig = opInfo.getSignature(); - try { - Map typeMap = new LinkedHashMap<>(); - Arrays.stream(sig).forEach(e -> typeMap.put(e.getName(), e)); - parameters = getParameters(params, typeMap).values().toArray(); - signature = Arrays.asList(sig).stream().map(a -> a.getType()).toArray(a -> new String[a]); - exception = null; - break; - } catch (IllegalArgumentException ex) { - exception = ex; - } - } - if (exception != null) { - throw exception; + IllegalArgumentException exception = null; + for (MBeanOperationInfo mBeanOperationInfo : mBeanOperationInfos) { + MBeanParameterInfo[] sig = mBeanOperationInfo.getSignature(); + try { + Map typeMap = new LinkedHashMap<>(); // Order of parameters is important + Arrays.stream(sig).forEach(e -> typeMap.put(e.getName(), e)); + parameters = getOperationParameters(params, typeMap).values().toArray(); + signature = Stream.of(sig).map(MBeanParameterInfo::getType).toArray(String[]::new); + exception = null; + break; + } catch (IllegalArgumentException ex) { + exception = ex; } } + if (exception != null) { + throw exception; + } - Object invoke = null; + Object invoke; try { invoke = mBeanServer.invoke(objectName, opstr, parameters, signature); if (invoke != null) { @@ -440,268 +701,6 @@ } } - private HttpResponse doMBeanInfo(HttpExchange exchange) { - try { - JSONObject mBeanInfo = getMBeanInfo(mBeanServer, objectName); - return new HttpResponse(200, mBeanInfo.toJsonString()); - } catch (RuntimeOperationsException | IntrospectionException | ReflectionException e) { - return new HttpResponse(HttpResponse.SERVER_ERROR, HttpResponse.getErrorMessage(e)); - } catch (InstanceNotFoundException e) { - return new HttpResponse(HttpResponse.BAD_REQUEST, "Specified MBean does not exist"); - } catch (Exception e) { - return new HttpResponse(HttpResponse.SERVER_ERROR, HttpResponse.getErrorMessage(e)); - } - } - - @Override - public void handle(HttpExchange exchange) throws IOException { - String path = exchange.getRequestURI().getPath(); - if (path.matches(pathPrefix + "/?$")) { - RestResource.super.handle(exchange); - } else if (path.matches(pathPrefix + "/info$") && exchange.getRequestMethod().equalsIgnoreCase("GET")) { - RestResource.super.handle(exchange); - } else if (path.matches(pathPrefix + "/[^/]+/?$") && exchange.getRequestMethod().equalsIgnoreCase("POST")) { - RestResource.super.handle(exchange); - } else { - HttpUtil.sendResponse(exchange, new HttpResponse(404, "Not found")); - } - } - - @Override - public HttpResponse doGet(HttpExchange exchange) { - if (exchange.getRequestURI().getPath().endsWith("info")) { - return doMBeanInfo(exchange); - } - String path = PlatformRestAdapter.getDomain() + exchange.getRequestURI().getPath().replaceAll("\\/$", ""); - String info = path + "/info"; - - try { - Map allAttributes = getAllAttributes(); - Map _links = new LinkedHashMap<>(); - _links.put("info", HttpUtil.escapeUrl(info)); - - MBeanOperationInfo[] opInfo = mBeanServer.getMBeanInfo(objectName).getOperations(); - JSONArray jarr = new JSONArray(); - for (MBeanOperationInfo op : opInfo) { - JSONObject jobj1 = new JSONObject(); - JSONArray jarr1 = new JSONArray(); - jobj1.put("name", op.getName()); - jobj1.put("href", HttpUtil.escapeUrl(path + "/" + op.getName())); - jobj1.put("method", "POST"); - for (MBeanParameterInfo paramInfo : op.getSignature()) { - JSONObject jobj = new JSONObject(); - jobj.put("name", paramInfo.getName()); - jobj.put("type", paramInfo.getType()); - jarr1.add(jobj); - } - jobj1.put("arguments", jarr1); - jobj1.put("returnType", op.getReturnType()); - jarr.add(jobj1); - } - - JSONMapper typeMapper = JSONMappingFactory.INSTANCE.getTypeMapper(allAttributes); - if (typeMapper != null) { - JSONElement jsonElement1 = typeMapper.toJsonValue(allAttributes); - JSONElement jsonElement2 = typeMapper.toJsonValue(_links); - - JSONObject jobj = new JSONObject(); - jobj.put("attributes", jsonElement1); - jobj.put("operations", jarr); - jobj.put("_links", jsonElement2); - return new HttpResponse(200, jobj.toJsonString()); - } else { - return new HttpResponse(HttpResponse.SERVER_ERROR, "Unable to find JSONMapper"); - } - } catch (RuntimeOperationsException | IntrospectionException | ReflectionException | JSONMappingException e) { - return new HttpResponse(HttpResponse.SERVER_ERROR, HttpResponse.getErrorMessage(e)); - } catch (InstanceNotFoundException e) { - return new HttpResponse(HttpResponse.BAD_REQUEST, "Specified MBean does not exist"); - } catch (AttributeNotFoundException e) { - return new HttpResponse(HttpResponse.BAD_REQUEST, "Specified Attribute does not exist"); - } catch (MBeanException e) { - Throwable cause = e.getCause(); - return new HttpResponse(HttpResponse.SERVER_ERROR, HttpResponse.getErrorMessage(e)); - } catch (Exception e) { - return new HttpResponse(HttpResponse.SERVER_ERROR, HttpResponse.getErrorMessage(e)); - } - } - - /* - HTTP POST for this MBean's URL allows setting of attributes and execution of operations. - POST request body can follow one of the below formats - 1. { name : value} - Set a single attribute - 2. { name1 : value1, name2 : value2 } - Sets multiple attributes - 3. {attributes : {read : [name]} , {write : {name : value}}, operations : {op_name : {param_name:name, param_value:value}}} - This bulk operation request sets multiple attributes and executes multiple - operations on the MBean. - */ - @Override - public HttpResponse doPost(HttpExchange exchange) { - String path = exchange.getRequestURI().getPath(); - String reqBody = null; - try { - if (path.matches(pathPrefix + "/?$")) { // POST to current URL - reqBody = HttpUtil.readRequestBody(exchange); - if (reqBody == null || reqBody.isEmpty()) { // No Parameters - return HttpResponse.BAD_REQUEST; - } - - JSONParser parser = new JSONParser(reqBody); - JSONElement jsonElement = parser.parse(); - if (!(jsonElement instanceof JSONObject)) { - return new HttpResponse(HttpResponse.BAD_REQUEST, - "Invalid parameters : [" + reqBody + "]"); - } - - JSONObject jsonObject = (JSONObject) jsonElement; - - if (jsonObject.keySet().contains("attributes") | jsonObject.keySet().contains("operations")) { - return new HttpResponse(HttpURLConnection.HTTP_OK, handleBulkRequest(exchange, jsonObject).toJsonString()); - } else { - Map stringObjectMap = setAttributes(jsonObject); - JSONMapper typeMapper = JSONMappingFactory.INSTANCE.getTypeMapper(stringObjectMap); - if (typeMapper != null) { - return new HttpResponse(HttpURLConnection.HTTP_OK, typeMapper.toJsonValue(stringObjectMap).toJsonString()); - } else { - return new HttpResponse(HttpResponse.SERVER_ERROR, "Unable to find JSON Mapper"); - } - } - } else if (path.matches(pathPrefix + "/[^/]+/?$")) { // POST to MBeanOperation - Matcher matcher = Pattern.compile(pathPrefix + "/").matcher(path); - String operation; - if (matcher.find()) { - String ss = path.substring(matcher.end()); - operation = ss; - } else { - return HttpResponse.BAD_REQUEST; - } - - reqBody = HttpUtil.readRequestBody(exchange); - JSONElement result; - if (reqBody == null || reqBody.isEmpty()) { // No Parameters - result = execOperation(operation, null); - } else { - JSONParser parser = new JSONParser(reqBody); - JSONElement jsonElement = parser.parse(); - if (!(jsonElement instanceof JSONObject)) { - return new HttpResponse(HttpResponse.BAD_REQUEST, - "Invalid parameters : [" + reqBody + "] for operation - " + operation); - } - result = execOperation(operation, (JSONObject) jsonElement); - } - return new HttpResponse(HttpURLConnection.HTTP_OK, result.toJsonString()); - } else { - return HttpResponse.REQUEST_NOT_FOUND; - } - } catch (InstanceNotFoundException e) { - // Should never happen - } catch (JSONDataException | ParseException e) { - return new HttpResponse(HttpURLConnection.HTTP_BAD_REQUEST, "Invalid JSON : " + reqBody, e.getMessage()); - } catch (IntrospectionException | JSONMappingException | MBeanException | ReflectionException | IOException e) { - return new HttpResponse(HttpResponse.SERVER_ERROR, HttpResponse.getErrorMessage(e)); - } catch (IllegalArgumentException e) { - return new HttpResponse(HttpResponse.BAD_REQUEST, e.getMessage()); - } catch (Exception e) { - return new HttpResponse(HttpResponse.SERVER_ERROR, HttpResponse.getErrorMessage(e)); - } - return HttpResponse.REQUEST_NOT_FOUND; - } - - public JSONElement handleBulkRequest(HttpExchange exchange, JSONObject reqObject) { - - JSONObject result = new JSONObject(); - - // Handle attributes - JSONElement element = reqObject.get("attributes"); - if (element != null && element instanceof JSONObject) { - JSONObject attrInfo = (JSONObject) element; - JSONObject attrNode = new JSONObject(); - // Read attributes - JSONElement read = attrInfo.get("get"); - if (read != null && read instanceof JSONArray) { - JSONArray jattrs = (JSONArray) read; - JSONElement jAttrRead; - Map attrRead = null; - try { - String[] attributes = getStrings(jattrs); - attrRead = getAttributes(attributes); - JSONMapper typeMapper = JSONMappingFactory.INSTANCE.getTypeMapper(attrRead); - jAttrRead = typeMapper.toJsonValue(attrRead); - } catch (InstanceNotFoundException | ReflectionException | JSONMappingException e) { - jAttrRead = new JSONPrimitive(""); - } catch (JSONDataException e) { - jAttrRead = new JSONPrimitive("Invalid JSON : " + read.toJsonString()); - } - - attrNode.put("get", jAttrRead); - } - - // Write attributes - JSONElement write = attrInfo.get("set"); - JSONElement jAttrRead; - if (write != null && write instanceof JSONObject) { - JSONObject jattrs = (JSONObject) write; - try { - Map attrMap = setAttributes(jattrs); - JSONMapper typeMapper = JSONMappingFactory.INSTANCE.getTypeMapper(attrMap); - jAttrRead = typeMapper.toJsonValue(attrMap); - } catch (JSONDataException ex) { - jAttrRead = new JSONPrimitive("Invalid JSON : " + write.toJsonString()); - } catch (JSONMappingException | IntrospectionException | InstanceNotFoundException | ReflectionException e) { - jAttrRead = new JSONPrimitive(""); - } - attrNode.put("set", jAttrRead); - } - result.put("attributes", attrNode); - } - - // Execute operations - element = reqObject.get("operations"); - if (element != null) { - JSONArray operationList; - if (element instanceof JSONPrimitive // Single no-arg operation - || element instanceof JSONObject) { // single/mulitple operations - operationList = new JSONArray(); - operationList.add(element); - } else if (element instanceof JSONArray) { // List of no-arg/with-arg operation - operationList = (JSONArray) element; - } else { - operationList = new JSONArray(); - } - JSONObject opResult = new JSONObject(); - for (JSONElement elem : operationList) { - if (elem instanceof JSONPrimitive - && ((JSONPrimitive) elem).getValue() instanceof String) { // no-arg operation - String opName = (String) ((JSONPrimitive) elem).getValue(); - try { - JSONElement obj = execOperation(opName, null); - opResult.put(opName, obj); - } catch (IllegalArgumentException e) { - opResult.put(opName, e.getMessage()); - } catch (IntrospectionException | InstanceNotFoundException | MBeanException | ReflectionException e) { - opResult.put(opName, ""); - } - } else if (elem instanceof JSONObject) { - Set opNames = ((JSONObject) element).keySet(); - for (String opName : opNames) { - try { - JSONElement obj = execOperation(opName, (JSONObject) ((JSONObject) element).get(opName)); - opResult.put(opName, obj); - } catch (IllegalArgumentException e) { - opResult.put(opName, e.getMessage()); - } catch (IntrospectionException | InstanceNotFoundException | MBeanException | ReflectionException e) { - opResult.put(opName, ""); - } - } - } - } - result.put("operations", opResult); - } - return result; - } - private String[] getStrings(JSONArray jsonArray) throws JSONDataException { List attributes = new ArrayList<>(); for (JSONElement element : jsonArray) { @@ -712,20 +711,4 @@ } return attributes.toArray(new String[0]); } - - @Override - public HttpResponse doPut(HttpExchange exchange) { - return null; - } - - @Override - public HttpResponse doDelete(HttpExchange exchange) { - return null; - } - - @Override - public HttpResponse doHead(HttpExchange exchange) { - return null; - } - }