src/java.management.rest/share/classes/com/oracle/jmx/remote/rest/http/MBeanResource.java
author hb
Mon, 25 Dec 2017 20:42:05 +0530
branchjmx-rest-api
changeset 55994 9721e36abeb0
child 55995 a798bdd52997
permissions -rw-r--r--
Implementation of GET for new APIs Implemented paging and filtering for GET

package com.oracle.jmx.remote.rest.http;

import com.oracle.jmx.remote.rest.json.JSONArray;
import com.oracle.jmx.remote.rest.json.JSONElement;
import com.oracle.jmx.remote.rest.json.JSONObject;
import com.oracle.jmx.remote.rest.json.JSONPrimitive;
import com.oracle.jmx.remote.rest.mapper.JSONMapper;
import com.oracle.jmx.remote.rest.mapper.JSONMappingException;
import com.oracle.jmx.remote.rest.mapper.JSONMappingFactory;
import com.sun.net.httpserver.HttpExchange;

import javax.management.*;
import javax.management.remote.rest.PlatformRestAdapter;
import java.io.IOException;
import java.util.*;

import static javax.management.MBeanOperationInfo.*;

public class MBeanResource implements RestResource {

    private final ObjectName objectName;
    private final MBeanServer mBeanServer;

    public MBeanResource(MBeanServer mBeanServer, ObjectName objectName) {
        this.mBeanServer = mBeanServer;
        this.objectName = objectName;
    }

    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("description", mBeanInfo.getDescription());

        // Populate Attribute Info
        MBeanAttributeInfo[] attributes = mBeanInfo.getAttributes();
        JSONArray jarr = new JSONArray();
        for (MBeanAttributeInfo attr : attributes) {
            String access;
            if (attr.isReadable()) {
                if (attr.isWritable()) {
                    access = "read/write";
                } else {
                    access = "read-only";
                }
            } else if (attr.isWritable()) {
                access = "write-only";
            } else {
                access = "no-access";
            }
            JSONObject jobj1 = new JSONObject();
            jobj1.put("name", attr.getName());
            jobj1.put("type", attr.getType());
            jobj1.put("access", access);
            jobj1.put("description", attr.getDescription());
            jobj1.put("descriptor", getDescriptorJSON(attr.getDescriptor()));
            jarr.add(jobj1);
        }
        jobj.put("attributeInfo", jarr);

        // Add constructor Info
        MBeanConstructorInfo[] constructorInfo = mBeanInfo.getConstructors();
        jarr = new JSONArray();
        for (MBeanConstructorInfo constructor : constructorInfo) {
            JSONObject jobj1 = new JSONObject();
            jobj1.put("name", constructor.getName());
            JSONArray jarr1 = new JSONArray();
            for (MBeanParameterInfo paramInfo : constructor.getSignature()) {
                jarr1.add(getParamJSON(paramInfo));
            }
            jobj1.put("signature", jarr1);
            jobj1.put("description", constructor.getDescription());
            if (constructor.getDescriptor().getFieldNames().length > 1) {
                jobj1.put("descriptor", getDescriptorJSON(constructor.getDescriptor()));
            }
            jarr.add(jobj1);
        }
        jobj.put("constructorInfo", jarr);

        MBeanOperationInfo[] opInfo = mBeanInfo.getOperations();
        jarr = new JSONArray();

        for (MBeanOperationInfo op : opInfo) {
            String impactString;
            switch (op.getImpact()) {
                case ACTION:
                    impactString = "action";
                    break;
                case ACTION_INFO:
                    impactString = "action/info";
                    break;
                case INFO:
                    impactString = "info";
                    break;
                case UNKNOWN:
                    impactString = "unknown";
                    break;
                default:
                    impactString = "(" + op.getImpact() + ")";
            }

            JSONObject jobj1 = new JSONObject();

            jobj1.put("name", op.getName());
            JSONArray jarr1 = new JSONArray();
            for (MBeanParameterInfo paramInfo : op.getSignature()) {
                jarr1.add(getParamJSON(paramInfo));
            }
            jobj1.put("signature", jarr1);
            jobj1.put("returnType", op.getReturnType());
            jobj1.put("impact", impactString);
            jobj1.put("description", op.getDescription());
            if (op.getDescriptor().getFieldNames().length > 1) {
                jobj1.put("descriptor", getDescriptorJSON(op.getDescriptor()));
            }
            jarr.add(jobj1);
        }
        jobj.put("operationInfo", jarr);

        MBeanNotificationInfo[] notifications = mBeanInfo.getNotifications();
        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()) {
                jarr1.add(new JSONPrimitive(notifType));
            }
            jobj1.put("notifTypes", jarr1);
            jobj1.put("description", notification.getDescription());
            if (notification.getDescriptor().getFieldNames().length > 1) {
                jobj1.put("descriptor", getDescriptorJSON(notification.getDescriptor()));
            }
            jarr.add(jobj1);
        }
        jobj.put("notificationInfo", jarr);

        jobj.put("descriptor", getDescriptorJSON(mBeanInfo.getDescriptor()));
        return jobj;
    }

    private JSONObject getParamJSON(MBeanParameterInfo mParamInfo) {
        JSONObject jobj1 = new JSONObject();
        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()));
        }
        return jobj1;
    }

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

    private Map<String, Object> getAllAttributes() throws IntrospectionException,
            InstanceNotFoundException, ReflectionException, AttributeNotFoundException, MBeanException {
        Map<String, Object> 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<String> missingAttrs = new ArrayList<>(Arrays.asList(attrs));
            for (Attribute a : attrVals.asList()) {
                missingAttrs.remove(a.getName());
                result.put(a.getName(), a.getValue());
            }
            for (String attr : missingAttrs) {
                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 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("^\\/?jmx\\/servers\\/[a-zA-Z0-9\\-\\.]+\\/mbeans\\/[^\\/]+\\/?$") ||
                path.matches("^\\/?jmx\\/servers\\/[a-zA-Z0-9\\-\\.]+\\/mbeans\\/[^\\/]+\\/info$")) {
            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.getAuthority() + exchange.getRequestURI().getPath().replaceAll("\\/$", "");
        String info = path + "/info";

        try {
            Map<String, Object> allAttributes = getAllAttributes();
            Map<String, String> _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));
        }
    }

    @Override
    public HttpResponse doPut(HttpExchange exchange) {
        return null;
    }

    @Override
    public HttpResponse doPost(HttpExchange exchange) {
        return null;
    }

    @Override
    public HttpResponse doDelete(HttpExchange exchange) {
        return null;
    }

    @Override
    public HttpResponse doHead(HttpExchange exchange) {
        return null;
    }

}