55994
|
1 |
package com.oracle.jmx.remote.rest.http;
|
|
2 |
|
|
3 |
import com.oracle.jmx.remote.rest.json.JSONElement;
|
|
4 |
import com.oracle.jmx.remote.rest.json.JSONObject;
|
|
5 |
import com.oracle.jmx.remote.rest.mapper.JSONMapper;
|
|
6 |
import com.oracle.jmx.remote.rest.mapper.JSONMappingException;
|
|
7 |
import com.oracle.jmx.remote.rest.mapper.JSONMappingFactory;
|
|
8 |
import com.sun.net.httpserver.HttpExchange;
|
|
9 |
|
|
10 |
import javax.management.*;
|
|
11 |
import javax.management.remote.rest.PlatformRestAdapter;
|
|
12 |
import java.io.IOException;
|
|
13 |
import java.io.UnsupportedEncodingException;
|
|
14 |
import java.util.*;
|
|
15 |
import java.util.concurrent.ConcurrentHashMap;
|
|
16 |
import java.util.concurrent.CopyOnWriteArrayList;
|
|
17 |
import java.util.concurrent.CopyOnWriteArraySet;
|
|
18 |
import java.util.regex.Matcher;
|
|
19 |
import java.util.regex.Pattern;
|
|
20 |
|
|
21 |
public class MBeanCollectionResource implements RestResource, NotificationListener {
|
|
22 |
|
|
23 |
private List<ObjectName> allowedMbeans;
|
|
24 |
private final MBeanServer mBeanServer;
|
|
25 |
private final Map<String, MBeanResource> mBeanResourceMap = new ConcurrentHashMap<>();
|
|
26 |
private static final int pageSize = 10;
|
|
27 |
|
|
28 |
private boolean isMBeanAllowed(ObjectName objName) {
|
|
29 |
try {
|
|
30 |
MBeanInfo mInfo = mBeanServer.getMBeanInfo(objName);
|
|
31 |
MBeanAttributeInfo[] attrsInfo = mInfo.getAttributes();
|
|
32 |
for (MBeanAttributeInfo attrInfo : attrsInfo) {
|
|
33 |
String type = attrInfo.getType();
|
|
34 |
if (!JSONMappingFactory.INSTANCE.isTypeMapped(type)) {
|
|
35 |
return false;
|
|
36 |
}
|
|
37 |
}
|
|
38 |
MBeanOperationInfo[] operations = mInfo.getOperations();
|
|
39 |
for (MBeanOperationInfo opInfo : operations) {
|
|
40 |
MBeanParameterInfo[] signature = opInfo.getSignature();
|
|
41 |
for (MBeanParameterInfo sig : signature) {
|
|
42 |
if (!JSONMappingFactory.INSTANCE.isTypeMapped(sig.getType())) {
|
|
43 |
return false;
|
|
44 |
}
|
|
45 |
}
|
|
46 |
if (!JSONMappingFactory.INSTANCE.isTypeMapped(opInfo.getReturnType())) {
|
|
47 |
return false;
|
|
48 |
}
|
|
49 |
}
|
|
50 |
return true;
|
|
51 |
} catch (InstanceNotFoundException | IntrospectionException | ReflectionException | ClassNotFoundException ex) {
|
|
52 |
ex.printStackTrace();
|
|
53 |
return false;
|
|
54 |
}
|
|
55 |
}
|
|
56 |
|
|
57 |
private void introspectMBeanTypes(MBeanServer server) {
|
|
58 |
if (allowedMbeans.isEmpty()) {
|
|
59 |
Set<ObjectInstance> allMBeans = server.queryMBeans(null, null); // get all Mbeans
|
|
60 |
allMBeans.stream().filter((objIns) -> (isMBeanAllowed(objIns.getObjectName())))
|
|
61 |
.forEachOrdered(objIns -> allowedMbeans.add(objIns.getObjectName()));
|
|
62 |
}
|
|
63 |
}
|
|
64 |
|
|
65 |
@Override
|
|
66 |
public void handleNotification(Notification notification, Object handback) {
|
|
67 |
try {
|
|
68 |
MBeanServerNotification mbs = (MBeanServerNotification) notification;
|
|
69 |
if (MBeanServerNotification.REGISTRATION_NOTIFICATION.equals(mbs.getType())) {
|
|
70 |
ObjectName mBeanName = mbs.getMBeanName();
|
|
71 |
if (isMBeanAllowed(mBeanName)) {
|
|
72 |
allowedMbeans.add(mBeanName);
|
|
73 |
}
|
|
74 |
} else if (MBeanServerNotification.UNREGISTRATION_NOTIFICATION.equals(mbs.getType())) {
|
|
75 |
if (allowedMbeans.contains(mbs.getMBeanName().toString())) {
|
|
76 |
allowedMbeans.remove(mbs.getMBeanName().toString());
|
|
77 |
}
|
|
78 |
}
|
|
79 |
} catch (Exception e) {
|
|
80 |
}
|
|
81 |
}
|
|
82 |
|
|
83 |
public MBeanCollectionResource(MBeanServer mBeanServer) {
|
|
84 |
this.mBeanServer = mBeanServer;
|
|
85 |
allowedMbeans = new ArrayList<>();
|
|
86 |
introspectMBeanTypes(mBeanServer);
|
|
87 |
allowedMbeans = new CopyOnWriteArrayList<>(allowedMbeans);
|
|
88 |
allowedMbeans.forEach(objectName -> mBeanResourceMap.put(objectName.toString(),
|
|
89 |
new MBeanResource(mBeanServer, objectName)));
|
|
90 |
}
|
|
91 |
|
|
92 |
@Override
|
|
93 |
public void handle(HttpExchange exchange) throws IOException {
|
|
94 |
String path = exchange.getRequestURI().getPath();
|
55995
|
95 |
String pathPrefix = "^/?jmx/servers/[a-zA-Z0-9\\-\\.]+/mbeans";
|
|
96 |
if (path.matches(pathPrefix + "/?$")) {
|
55994
|
97 |
RestResource.super.handle(exchange);
|
55995
|
98 |
} else if (path.matches(pathPrefix + "/[^/]+/?.*")) {
|
55994
|
99 |
// Extract mbean name
|
|
100 |
// Forward the request to its corresponding rest resource
|
55995
|
101 |
Pattern mbeans = Pattern.compile(pathPrefix + "/");
|
55994
|
102 |
Matcher matcher = mbeans.matcher(path);
|
|
103 |
|
|
104 |
if (matcher.find()) {
|
|
105 |
String ss = path.substring(matcher.end());
|
|
106 |
String mBeanName = ss;
|
|
107 |
if (ss.indexOf('/') != -1) {
|
|
108 |
mBeanName = ss.substring(0, ss.indexOf('/'));
|
|
109 |
}
|
|
110 |
MBeanResource mBeanResource = mBeanResourceMap.get(mBeanName);
|
|
111 |
if (mBeanResource == null) {
|
|
112 |
HttpUtil.sendResponse(exchange, new HttpResponse(404, "Not found"));
|
|
113 |
return;
|
|
114 |
}
|
|
115 |
mBeanResource.handle(exchange);
|
|
116 |
}
|
|
117 |
}
|
|
118 |
}
|
|
119 |
|
|
120 |
@Override
|
|
121 |
public HttpResponse doGet(HttpExchange exchange) {
|
|
122 |
// add links
|
|
123 |
|
|
124 |
final String path = PlatformRestAdapter.getAuthority() + exchange.getRequestURI().getPath().replaceAll("\\/$", "");
|
|
125 |
try {
|
|
126 |
List<ObjectName> mbeans = allowedMbeans;
|
|
127 |
Map<String, String> queryMap = HttpUtil.getGetRequestQueryMap(exchange);
|
|
128 |
if(queryMap.containsKey("query")) {
|
|
129 |
Set<ObjectName> queryMBeans = mBeanServer.queryNames(new ObjectName(queryMap.get("query")),null);
|
|
130 |
queryMBeans.retainAll(allowedMbeans);
|
|
131 |
mbeans = new ArrayList<>(queryMBeans);
|
|
132 |
}
|
|
133 |
|
|
134 |
JSONObject _links = HttpUtil.getPaginationLinks(exchange, mbeans, pageSize);
|
|
135 |
List<ObjectName> filteredMBeans = HttpUtil.filterByPage(exchange, mbeans, pageSize);
|
|
136 |
List<Map<String, String>> items = new ArrayList<>(filteredMBeans.size());
|
|
137 |
filteredMBeans.forEach(objectName -> {
|
|
138 |
Map<String, String> item = new LinkedHashMap<>(2);
|
|
139 |
item.put("name", objectName.toString());
|
|
140 |
String href = path + "/" + objectName.toString();
|
|
141 |
href = HttpUtil.escapeUrl(href);
|
|
142 |
item.put("href", href);
|
|
143 |
items.add(item);
|
|
144 |
});
|
|
145 |
|
|
146 |
Map<String, String> properties = new HashMap<>();
|
|
147 |
|
|
148 |
properties.put("mbeanCount", Integer.toString(mbeans.size()));
|
|
149 |
|
|
150 |
JSONMapper typeMapper1 = JSONMappingFactory.INSTANCE.getTypeMapper(items);
|
|
151 |
JSONMapper typeMapper2 = JSONMappingFactory.INSTANCE.getTypeMapper(properties);
|
|
152 |
|
|
153 |
JSONElement linkElem = typeMapper1.toJsonValue(items);
|
|
154 |
JSONElement propElem = typeMapper2.toJsonValue(properties);
|
|
155 |
JSONObject jobj = new JSONObject();
|
|
156 |
if(_links != null && !_links.isEmpty()) {
|
|
157 |
jobj.put("_links",_links);
|
|
158 |
}
|
|
159 |
|
|
160 |
jobj.putAll((JSONObject) propElem);
|
|
161 |
jobj.put("items", linkElem);
|
|
162 |
|
|
163 |
return new HttpResponse(200, jobj.toJsonString());
|
|
164 |
} catch (JSONMappingException e) {
|
|
165 |
return new HttpResponse(500, "Internal server error");
|
|
166 |
} catch (UnsupportedEncodingException e) {
|
|
167 |
return HttpResponse.SERVER_ERROR;
|
|
168 |
} catch (MalformedObjectNameException e) {
|
|
169 |
return new HttpResponse(HttpResponse.BAD_REQUEST, "Invalid query string");
|
|
170 |
}
|
|
171 |
}
|
|
172 |
|
|
173 |
@Override
|
|
174 |
public HttpResponse doPut(HttpExchange exchange) {
|
|
175 |
return null;
|
|
176 |
}
|
|
177 |
|
|
178 |
@Override
|
|
179 |
public HttpResponse doPost(HttpExchange exchange) {
|
|
180 |
return null;
|
|
181 |
}
|
|
182 |
|
|
183 |
@Override
|
|
184 |
public HttpResponse doDelete(HttpExchange exchange) {
|
|
185 |
return null;
|
|
186 |
}
|
|
187 |
|
|
188 |
@Override
|
|
189 |
public HttpResponse doHead(HttpExchange exchange) {
|
|
190 |
return null;
|
|
191 |
}
|
|
192 |
}
|