1/*
2 * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 *   - Redistributions of source code must retain the above copyright
9 *     notice, this list of conditions and the following disclaimer.
10 *
11 *   - Redistributions in binary form must reproduce the above copyright
12 *     notice, this list of conditions and the following disclaimer in the
13 *     documentation and/or other materials provided with the distribution.
14 *
15 *   - Neither the name of Oracle nor the names of its
16 *     contributors may be used to endorse or promote products derived
17 *     from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
20 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/*
33 * This source code is provided to illustrate the usage of a given feature
34 * or technique and has been deliberately simplified. Additional steps
35 * required for a production-quality application, such as security checks,
36 * input validation and proper error handling, might not be present in
37 * this sample code.
38 */
39
40/*
41 * This is a collection of utilities for Monitoring
42 * and management API.
43 *
44 * File dependency:
45 *    conc.js -> for concurrency utilities
46 */
47
48// At any time, we maintain atmost one MBeanServer
49// connection. And so, we store the same as a global
50// variable.
51var mmConnection = null;
52
53function jmxConnect(hostport) {
54    if (mmConnection != null) {
55        // close the existing connection
56        try {
57            mmConnection.close();
58        } catch (e) {
59        }
60    }
61
62    var JMXServiceURL = javax.management.remote.JMXServiceURL;
63    var JMXConnectorFactory = javax.management.remote.JMXConnectorFactory;
64
65    var urlPath = "/jndi/rmi://" + hostport + "/jmxrmi";
66    var url = new JMXServiceURL("rmi", "", 0, urlPath);
67    var jmxc = JMXConnectorFactory.connect(url);
68    // note that the "mmConnection" is a global variable!
69    mmConnection = jmxc.getMBeanServerConnection();
70}
71jmxConnect.docString = "connects to the given host, port (specified as name:port)";
72
73function mbeanConnection() {
74    if (mmConnection == null) {
75        throw "Not connected to MBeanServer yet!";
76    }
77
78    return mmConnection;
79}
80mbeanConnection.docString = "returns the current MBeanServer connection";
81
82/**
83 * Returns a platform MXBean proxy for given MXBean name and interface class
84 */
85function newPlatformMXBeanProxy(name, intf) {
86    var factory = java.lang.management.ManagementFactory;
87    return factory.newPlatformMXBeanProxy(mbeanConnection(), name, intf);
88}
89newPlatformMXBeanProxy.docString = "returns a proxy for a platform MXBean";
90
91/**
92 * Wraps a string to ObjectName if needed.
93 */
94function objectName(objName) {
95    var ObjectName = Packages.javax.management.ObjectName;
96    if (objName instanceof ObjectName) {
97        return objName;
98    } else {
99        return new ObjectName(objName);
100    }
101}
102objectName.docString = "creates JMX ObjectName for a given String";
103
104/**
105 * Creates a new (M&M) Attribute object
106 *
107 * @param name name of the attribute
108 * @param value value of the attribute
109 */
110function attribute(name, value) {
111    var Attribute = Packages.javax.management.Attribute;
112    return new Attribute(name, value);
113}
114attribute.docString = "returns a new JMX Attribute using name and value given";
115
116/**
117 * Returns MBeanInfo for given ObjectName. Strings are accepted.
118 */
119function mbeanInfo(objName) {
120    objName = objectName(objName);
121    return mbeanConnection().getMBeanInfo(objName);
122}
123mbeanInfo.docString = "returns MBeanInfo of a given ObjectName";
124
125/**
126 * Returns ObjectInstance for a given ObjectName.
127 */
128function objectInstance(objName) {
129    objName = objectName(objName);
130    return mbeanConnection().objectInstance(objectName);
131}
132objectInstance.docString = "returns ObjectInstance for a given ObjectName";
133
134/**
135 * Queries with given ObjectName and QueryExp.
136 * QueryExp may be null.
137 *
138 * @return set of ObjectNames.
139 */
140function queryNames(objName, query) {
141    objName = objectName(objName);
142    if (query == undefined) query = null;
143    return mbeanConnection().queryNames(objName, query);
144}
145queryNames.docString = "returns QueryNames using given ObjectName and optional query";
146
147/**
148 * Queries with given ObjectName and QueryExp.
149 * QueryExp may be null.
150 *
151 * @return set of ObjectInstances.
152 */
153function queryMBeans(objName, query) {
154    objName = objectName(objName);
155    if (query == undefined) query = null;
156    return mbeanConnection().queryMBeans(objName, query);
157}
158queryMBeans.docString = "return MBeans using given ObjectName and optional query";
159
160// wraps a script array as java.lang.Object[]
161function objectArray(array) {
162    return Java.to(array, "java.lang.Object[]");
163}
164
165// wraps a script (string) array as java.lang.String[]
166function stringArray(array) {
167    return Java.to(array, "java.lang.String[]");
168}
169
170// script array to Java List
171function toAttrList(array) {
172    var AttributeList = Packages.javax.management.AttributeList;
173    if (array instanceof AttributeList) {
174        return array;
175    }
176    var list = new AttributeList(array.length);
177    for (var index = 0; index < array.length; index++) {
178        list.add(array[index]);
179    }
180    return list;
181}
182
183// Java Collection (Iterable) to script array
184function toArray(collection) {
185    if (collection instanceof Array) {
186        return collection;
187    }
188    var itr = collection.iterator();
189    var array = new Array();
190    while (itr.hasNext()) {
191        array[array.length] = itr.next();
192    }
193    return array;
194}
195
196// gets MBean attributes
197function getMBeanAttributes(objName, attributeNames) {
198    objName = objectName(objName);
199    return mbeanConnection().getAttributes(objName,stringArray(attributeNames));
200}
201getMBeanAttributes.docString = "returns specified Attributes of given ObjectName";
202
203// gets MBean attribute
204function getMBeanAttribute(objName, attrName) {
205    objName = objectName(objName);
206    return mbeanConnection().getAttribute(objName, attrName);
207}
208getMBeanAttribute.docString = "returns a single Attribute of given ObjectName";
209
210// sets MBean attributes
211function setMBeanAttributes(objName, attrList) {
212    objName = objectName(objName);
213    attrList = toAttrList(attrList);
214    return mbeanConnection().setAttributes(objName, attrList);
215}
216setMBeanAttributes.docString = "sets specified Attributes of given ObjectName";
217
218// sets MBean attribute
219function setMBeanAttribute(objName, attrName, attrValue) {
220    var Attribute = Packages.javax.management.Attribute;
221    objName = objectName(objName);
222    mbeanConnection().setAttribute(objName, new Attribute(attrName, attrValue));
223}
224setMBeanAttribute.docString = "sets a single Attribute of given ObjectName";
225
226// invokes an operation on given MBean
227function invokeMBean(objName, operation, params, signature) {
228    objName = objectName(objName);
229    params = objectArray(params);
230    signature = stringArray(signature);
231    return mbeanConnection().invoke(objName, operation, params, signature);
232}
233invokeMBean.docString = "invokes MBean operation on given ObjectName";
234
235/**
236 * Wraps a MBean specified by ObjectName as a convenient
237 * script object -- so that setting/getting MBean attributes
238 * and invoking MBean method can be done with natural syntax.
239 *
240 * @param objName ObjectName of the MBean
241 * @param async asynchornous mode [optional, default is false]
242 * @return script wrapper for MBean
243 *
244 * With async mode, all field, operation access is async. Results
245 * will be of type FutureTask. When you need value, call 'get' on it.
246 */
247function mbean(objName, async) {
248    var index;
249    objName = objectName(objName);
250    var info = mbeanInfo(objName);
251    var attrs = info.attributes;
252    var attrMap = new Object;
253    for (index in attrs) {
254        attrMap[attrs[index].name] = attrs[index];
255    }
256    var opers = info.operations;
257    var operMap = new Object;
258    for (index in opers) {
259        operMap[opers[index].name] = opers[index];
260    }
261
262    function isAttribute(name) {
263        return name in attrMap;
264    }
265
266    function isOperation(name) {
267        return name in operMap;
268    }
269
270    return new JSAdapter() {
271        __has__: function (name) {
272            return isAttribute(name) || isOperation(name);
273        },
274        __get__: function (name) {
275            if (isAttribute(name)) {
276                if (async) {
277                    return getMBeanAttribute.future(objName, name);
278                } else {
279                    return getMBeanAttribute(objName, name);
280                }
281            } else {
282                return undefined;
283            }
284        },
285        __call__: function(name) {
286            if (isOperation(name)) {
287                var oper = operMap[name];
288
289                var params = [];
290                for (var j = 1; j < arguments.length; j++) {
291                    params[j-1]= arguments[j];
292                }
293
294                var sigs = oper.signature;
295
296                var sigNames = new Array(sigs.length);
297                for (var index in sigs) {
298                    sigNames[index] = sigs[index].getType();
299                }
300
301                if (async) {
302                    return invokeMBean.future(objName, name, params, sigNames);
303                } else {
304                    return invokeMBean(objName, name, params, sigNames);
305                }
306            } else {
307                return undefined;
308            }
309        },
310        __put__: function (name, value) {
311            if (isAttribute(name)) {
312                if (async) {
313                    setMBeanAttribute.future(objName, name, value);
314                } else {
315                    setMBeanAttribute(objName, name, value);
316                }
317            } else {
318                return undefined;
319            }
320        }
321    };
322}
323mbean.docString = "returns a conveninent script wrapper for a MBean of given ObjectName";
324
325if (this.application != undefined) {
326    this.application.addTool("JMX Connect",
327        // connect to a JMX MBean Server
328        function () {
329            var url = prompt("Connect to JMX server (host:port)");
330            if (url != null) {
331                try {
332                    jmxConnect(url);
333                    alert("connected!");
334                } catch (e) {
335                    error(e, "Can not connect to " + url);
336                }
337            }
338        });
339}
340