55985
|
1 |
/*
|
|
2 |
* To change this license header, choose License Headers in Project Properties.
|
|
3 |
* To change this template file, choose Tools | Templates
|
|
4 |
* and open the template in the editor.
|
|
5 |
*/
|
|
6 |
package com.oracle.jmx.remote.rest.http;
|
|
7 |
|
|
8 |
import javax.management.*;
|
|
9 |
import javax.management.openmbean.OpenMBeanAttributeInfo;
|
|
10 |
import javax.management.openmbean.OpenMBeanParameterInfo;
|
|
11 |
import javax.management.openmbean.OpenType;
|
|
12 |
import com.oracle.jmx.remote.rest.json.JSONArray;
|
|
13 |
import com.oracle.jmx.remote.rest.json.JSONElement;
|
|
14 |
import com.oracle.jmx.remote.rest.json.JSONObject;
|
|
15 |
import com.oracle.jmx.remote.rest.json.JSONPrimitive;
|
|
16 |
import com.oracle.jmx.remote.rest.mapper.JSONDataException;
|
|
17 |
import com.oracle.jmx.remote.rest.mapper.JSONMapper;
|
|
18 |
import com.oracle.jmx.remote.rest.mapper.JSONMappingException;
|
|
19 |
import com.oracle.jmx.remote.rest.mapper.JSONMappingFactory;
|
|
20 |
import com.oracle.jmx.remote.rest.json.parser.JSONParser;
|
|
21 |
import com.oracle.jmx.remote.rest.json.parser.ParseException;
|
|
22 |
import java.net.HttpURLConnection;
|
|
23 |
import java.util.*;
|
|
24 |
|
|
25 |
/**
|
|
26 |
* @author harsha
|
|
27 |
*/
|
|
28 |
public class PostRequestHandler {
|
|
29 |
|
|
30 |
private static final String MBEAN_NAME = "name";
|
|
31 |
private static final String MBEAN_ARGUMENTS = "arguments";
|
|
32 |
private final static Map<String, Class<?>> primitiveToObject = new HashMap<>();
|
|
33 |
|
|
34 |
static {
|
|
35 |
primitiveToObject.put("int", Integer.TYPE);
|
|
36 |
primitiveToObject.put("long", Long.TYPE);
|
|
37 |
primitiveToObject.put("double", Double.TYPE);
|
|
38 |
primitiveToObject.put("float", Float.TYPE);
|
|
39 |
primitiveToObject.put("boolean", Boolean.TYPE);
|
|
40 |
primitiveToObject.put("char", Character.TYPE);
|
|
41 |
primitiveToObject.put("byte", Byte.TYPE);
|
|
42 |
primitiveToObject.put("void", Void.TYPE);
|
|
43 |
primitiveToObject.put("short", Short.TYPE);
|
|
44 |
}
|
|
45 |
|
|
46 |
private final MBeanServer mbeanServer;
|
|
47 |
private final List<String> allowedMbeans;
|
|
48 |
|
|
49 |
public PostRequestHandler(MBeanServer mServer, List<String> allowedMbeans) {
|
|
50 |
this.mbeanServer = mServer;
|
|
51 |
this.allowedMbeans = allowedMbeans;
|
|
52 |
}
|
|
53 |
|
|
54 |
public synchronized List<JSONObject> handle(String jsonStr) {
|
|
55 |
|
|
56 |
JSONElement object;
|
|
57 |
List<JSONObject> postResponse = new ArrayList<>();
|
|
58 |
try {
|
|
59 |
object = new JSONParser(jsonStr).parse();
|
|
60 |
} catch (ParseException ex) {
|
|
61 |
postResponse.add(HttpResponse.getJsonObject(HttpResponse.getHttpErrorCode(ex), new JSONPrimitive(jsonStr), new JSONPrimitive("Invalid JSON String")));
|
|
62 |
return postResponse;
|
|
63 |
}
|
|
64 |
|
|
65 |
if (object instanceof JSONObject) {
|
|
66 |
postResponse.add(handleIndividualRequest((JSONObject) object));
|
|
67 |
return postResponse;
|
|
68 |
} else if (object instanceof JSONArray) {
|
|
69 |
JSONArray jarr = (JSONArray) object;
|
|
70 |
|
|
71 |
for (JSONElement jval : jarr) {
|
|
72 |
if (jval instanceof JSONObject) {
|
|
73 |
JSONObject resp = handleIndividualRequest((JSONObject) jval);
|
|
74 |
postResponse.add(resp);
|
|
75 |
}
|
|
76 |
}
|
|
77 |
return postResponse;
|
|
78 |
} else {
|
|
79 |
postResponse.add(HttpResponse.getJsonObject(HttpURLConnection.HTTP_BAD_REQUEST, new JSONPrimitive(jsonStr), new JSONPrimitive("Invalid request")));
|
|
80 |
return postResponse;
|
|
81 |
}
|
|
82 |
}
|
|
83 |
|
|
84 |
private JSONObject handleIndividualRequest(JSONObject jobject) {
|
|
85 |
|
|
86 |
try {
|
|
87 |
JSONObject map = jobject;
|
|
88 |
ObjectName name = getObjectName(map);
|
|
89 |
MBeanOps operation = null;
|
|
90 |
for (MBeanOps op : MBeanOps.values()) {
|
|
91 |
if (map.containsKey(op.toString())) {
|
|
92 |
operation = op;
|
|
93 |
break;
|
|
94 |
}
|
|
95 |
}
|
|
96 |
|
|
97 |
if (operation == null) {
|
|
98 |
throw new JSONDataException("Invalid operation string");
|
|
99 |
}
|
|
100 |
|
|
101 |
JSONElement val = map.get(operation.toString());
|
|
102 |
if (!(val instanceof JSONPrimitive) || !(((JSONPrimitive) val).getValue() instanceof String)) {
|
|
103 |
throw new JSONDataException("Invalid JSON String");
|
|
104 |
}
|
|
105 |
|
|
106 |
String attrOrOperation = (String) ((JSONPrimitive) val).getValue();
|
|
107 |
JSONElement result = new JSONPrimitive("success");
|
|
108 |
|
|
109 |
val = map.get(MBEAN_ARGUMENTS);
|
|
110 |
if (val != null) {
|
|
111 |
if (!(val instanceof JSONArray)) {
|
|
112 |
throw new JSONDataException("Invalid JSON String");
|
|
113 |
}
|
|
114 |
}
|
|
115 |
|
|
116 |
JSONArray args = (JSONArray) val;
|
|
117 |
|
|
118 |
switch (operation) {
|
|
119 |
case READ:
|
|
120 |
result = readAttribute(name, attrOrOperation, args);
|
|
121 |
break;
|
|
122 |
case WRITE:
|
|
123 |
writeAttribute(name, attrOrOperation, args);
|
|
124 |
break;
|
|
125 |
case EXEC:
|
|
126 |
result = execOperation(name, attrOrOperation, args);
|
|
127 |
break;
|
|
128 |
}
|
|
129 |
return HttpResponse.getJsonObject(HttpURLConnection.HTTP_OK, jobject, result);
|
|
130 |
} catch (JSONDataException | MBeanException | JSONMappingException | ClassNotFoundException | IntrospectionException | InvalidAttributeValueException | IllegalArgumentException ex) {
|
|
131 |
return HttpResponse.getJsonObject(HttpResponse.getHttpErrorCode(ex), jobject, new JSONPrimitive(ex.toString()));
|
|
132 |
} catch (AttributeNotFoundException ex) {
|
|
133 |
return HttpResponse.getJsonObject(HttpResponse.getHttpErrorCode(ex), jobject, new JSONPrimitive("Invalid Mbean attribute"));
|
|
134 |
} catch (InstanceNotFoundException | ReflectionException ex) {
|
|
135 |
return HttpResponse.getJsonObject(HttpResponse.getHttpErrorCode(ex), jobject, new JSONPrimitive("Invalid Mbean"));
|
|
136 |
}
|
|
137 |
}
|
|
138 |
|
|
139 |
private JSONElement readAttribute(ObjectName name, String attribute, JSONArray args)
|
|
140 |
throws InstanceNotFoundException, IntrospectionException, ReflectionException, ClassNotFoundException,
|
|
141 |
MBeanException, AttributeNotFoundException, JSONMappingException {
|
|
142 |
MBeanInfo mInfos = mbeanServer.getMBeanInfo(name);
|
|
143 |
MBeanAttributeInfo attrInfo = Arrays.stream(mInfos.getAttributes()).filter(a -> a.getName().equals(attribute)).findFirst().orElse(null);
|
|
144 |
if (attrInfo != null && attrInfo.isReadable()) {
|
|
145 |
JSONMapper mapper;
|
|
146 |
if (attrInfo instanceof OpenMBeanAttributeInfo) {
|
|
147 |
OpenType<?> type = ((OpenMBeanAttributeInfo) attrInfo).getOpenType();
|
|
148 |
mapper = JSONMappingFactory.INSTANCE.getTypeMapper(type);
|
|
149 |
} else {
|
|
150 |
mapper = JSONMappingFactory.INSTANCE.getTypeMapper(Class.forName(attrInfo.getType()));
|
|
151 |
}
|
|
152 |
Object attrVal = mbeanServer.getAttribute(name, attribute);
|
|
153 |
return mapper.toJsonValue(attrVal);
|
|
154 |
} else {
|
|
155 |
throw new AttributeNotFoundException();
|
|
156 |
}
|
|
157 |
}
|
|
158 |
|
|
159 |
private void writeAttribute(ObjectName name, String attribute, JSONArray args)
|
|
160 |
throws InstanceNotFoundException, IntrospectionException, ReflectionException, ClassNotFoundException,
|
|
161 |
JSONDataException, AttributeNotFoundException, InvalidAttributeValueException, MBeanException {
|
|
162 |
|
|
163 |
if (args == null || args.isEmpty()) {
|
|
164 |
throw new JSONDataException("Null arguments for set attribute");
|
|
165 |
}
|
|
166 |
|
|
167 |
MBeanInfo mInfos = mbeanServer.getMBeanInfo(name);
|
|
168 |
MBeanAttributeInfo attrInfo = Arrays.stream(mInfos.getAttributes()).filter(a -> a.getName().equals(attribute)).findFirst().orElse(null);
|
|
169 |
if (attrInfo != null && attrInfo.isWritable()) {
|
|
170 |
JSONMapper mapper;
|
|
171 |
if (attrInfo instanceof OpenMBeanAttributeInfo) {
|
|
172 |
OpenType<?> type = ((OpenMBeanAttributeInfo) attrInfo).getOpenType();
|
|
173 |
mapper = JSONMappingFactory.INSTANCE.getTypeMapper(type);
|
|
174 |
} else {
|
|
175 |
mapper = JSONMappingFactory.INSTANCE.getTypeMapper(Class.forName(attrInfo.getType()));
|
|
176 |
}
|
|
177 |
|
|
178 |
JSONElement val = args.get(0);
|
|
179 |
Object argVal = mapper.toJavaObject(val);
|
|
180 |
Attribute attrObj = new Attribute(attribute, argVal);
|
|
181 |
mbeanServer.setAttribute(name, attrObj);
|
|
182 |
|
|
183 |
} else {
|
|
184 |
throw new AttributeNotFoundException();
|
|
185 |
}
|
|
186 |
}
|
|
187 |
|
|
188 |
private Object[] mapArgsToSignature(JSONArray args, MBeanParameterInfo[] signature) {
|
|
189 |
if (args.size() != signature.length) {
|
|
190 |
throw new IllegalArgumentException("Invalid parameters : expected - " + signature.length + " parameters, got - " + args.size());
|
|
191 |
}
|
|
192 |
if (signature.length == 0 && args.isEmpty()) {
|
|
193 |
return new Object[0];
|
|
194 |
}
|
|
195 |
int i = 0;
|
|
196 |
Object[] params = new Object[signature.length];
|
|
197 |
for (MBeanParameterInfo info : signature) {
|
|
198 |
if (info instanceof OpenMBeanParameterInfo) {
|
|
199 |
OpenType<?> openType = ((OpenMBeanParameterInfo) info).getOpenType();
|
|
200 |
JSONMapper typeMapper = JSONMappingFactory.INSTANCE.getTypeMapper(openType);
|
|
201 |
try {
|
|
202 |
params[i] = typeMapper.toJavaObject(args.get(i));
|
|
203 |
} catch (JSONDataException ex) {
|
|
204 |
throw new IllegalArgumentException("Invalid JSON String : " + args.get(i).toJsonString() + " for arguments");
|
|
205 |
}
|
|
206 |
} else {
|
|
207 |
Class<?> inputCls = primitiveToObject.get(info.getType());
|
|
208 |
try {
|
|
209 |
if (inputCls == null) {
|
|
210 |
inputCls = Class.forName(info.getType());
|
|
211 |
}
|
|
212 |
} catch (ClassNotFoundException | ClassCastException ex) {
|
|
213 |
throw new IllegalArgumentException("Invalid parameters : " + args.get(i).toJsonString() + " cannot be mapped to : " + info.getType());
|
|
214 |
}
|
|
215 |
JSONMapper typeMapper = JSONMappingFactory.INSTANCE.getTypeMapper(inputCls);
|
|
216 |
if (typeMapper == null) {
|
|
217 |
throw new IllegalArgumentException("Invalid parameters : " + args.get(i).toJsonString() + " cannot be mapped to : " + info.getType());
|
|
218 |
}
|
|
219 |
try {
|
|
220 |
params[i] = typeMapper.toJavaObject(args.get(i));
|
|
221 |
} catch (JSONDataException ex) {
|
|
222 |
throw new IllegalArgumentException("Invalid JSON String : " + args.get(i).toJsonString() + " for arguments");
|
|
223 |
}
|
|
224 |
}
|
|
225 |
i++;
|
|
226 |
}
|
|
227 |
return params;
|
|
228 |
}
|
|
229 |
|
|
230 |
private JSONElement execOperation(ObjectName name, String opstr, JSONArray args)
|
|
231 |
throws MBeanException, JSONMappingException, IntrospectionException, ReflectionException {
|
|
232 |
if (args == null) {
|
|
233 |
args = new JSONArray();
|
|
234 |
}
|
|
235 |
MBeanInfo mBeanInfo;
|
|
236 |
try {
|
|
237 |
mBeanInfo = mbeanServer.getMBeanInfo(name);
|
|
238 |
} catch (InstanceNotFoundException ex) {
|
|
239 |
throw new IllegalArgumentException("Invalid Operation String");
|
|
240 |
}
|
|
241 |
|
|
242 |
MBeanOperationInfo[] opinfos = Arrays.stream(mBeanInfo.getOperations()).
|
|
243 |
filter(a -> a.getName().equals(opstr)).toArray(MBeanOperationInfo[]::new);
|
|
244 |
|
|
245 |
if (opinfos.length == 0) {
|
|
246 |
throw new IllegalArgumentException("Invalid Operation String");
|
|
247 |
}
|
|
248 |
|
|
249 |
String[] signature = null;
|
|
250 |
Object[] params = null;
|
|
251 |
|
|
252 |
if (opinfos.length == 1) {
|
|
253 |
MBeanParameterInfo[] sig = opinfos[0].getSignature();
|
|
254 |
params = mapArgsToSignature(args, sig);
|
|
255 |
signature = Arrays.asList(sig).stream().map(a -> a.getType()).toArray(a -> new String[a]);
|
|
256 |
} else if (opinfos.length > 1) {
|
|
257 |
IllegalArgumentException exception = null;
|
|
258 |
for (MBeanOperationInfo opInfo : opinfos) {
|
|
259 |
MBeanParameterInfo[] sig = opInfo.getSignature();
|
|
260 |
try {
|
|
261 |
params = mapArgsToSignature(args, sig);
|
|
262 |
signature = Arrays.asList(sig).stream().map(a -> a.getType()).toArray(a -> new String[a]);
|
|
263 |
exception = null;
|
|
264 |
break;
|
|
265 |
} catch (IllegalArgumentException ex) {
|
|
266 |
exception = ex;
|
|
267 |
}
|
|
268 |
}
|
|
269 |
if (exception != null) {
|
|
270 |
throw exception;
|
|
271 |
}
|
|
272 |
}
|
|
273 |
|
|
274 |
Object invoke;
|
|
275 |
try {
|
|
276 |
invoke = mbeanServer.invoke(name, opstr, params, signature);
|
|
277 |
} catch (InstanceNotFoundException ex) {
|
|
278 |
throw new IllegalArgumentException("Invalid Operation String");
|
|
279 |
}
|
|
280 |
if (invoke != null) {
|
|
281 |
JSONMapper typeMapper = JSONMappingFactory.INSTANCE.getTypeMapper(invoke);
|
|
282 |
if (typeMapper != null) {
|
|
283 |
return typeMapper.toJsonValue(invoke);
|
|
284 |
} else {
|
|
285 |
return new JSONPrimitive();
|
|
286 |
}
|
|
287 |
} else {
|
|
288 |
return new JSONPrimitive();
|
|
289 |
}
|
|
290 |
}
|
|
291 |
|
|
292 |
private ObjectName getObjectName(JSONObject map) throws JSONDataException, InstanceNotFoundException {
|
|
293 |
do {
|
|
294 |
if (map.get(MBEAN_NAME) == null || !(map.get(MBEAN_NAME) instanceof JSONPrimitive)) {
|
|
295 |
break;
|
|
296 |
}
|
|
297 |
JSONPrimitive mbean_name = (JSONPrimitive) map.get(MBEAN_NAME);
|
|
298 |
if (!(mbean_name.getValue() instanceof String)) {
|
|
299 |
break;
|
|
300 |
}
|
|
301 |
if (!allowedMbeans.contains((String) mbean_name.getValue())) {
|
|
302 |
throw new InstanceNotFoundException("Invalid MBean");
|
|
303 |
}
|
|
304 |
try {
|
|
305 |
return ObjectName.getInstance((String) mbean_name.getValue());
|
|
306 |
} catch (MalformedObjectNameException ex) {
|
|
307 |
}
|
|
308 |
} while (false);
|
|
309 |
throw new JSONDataException("Invalid JSON String");
|
|
310 |
}
|
|
311 |
|
|
312 |
private static enum MBeanOps {
|
|
313 |
READ("read"),
|
|
314 |
WRITE("write"),
|
|
315 |
EXEC("exec");
|
|
316 |
|
|
317 |
private final String text;
|
|
318 |
|
|
319 |
private MBeanOps(final String text) {
|
|
320 |
this.text = text;
|
|
321 |
}
|
|
322 |
|
|
323 |
@Override
|
|
324 |
public String toString() {
|
|
325 |
return text;
|
|
326 |
}
|
|
327 |
}
|
|
328 |
}
|