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(); |
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 { |
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 } |