http://java.sun.com/docs/books/tutorial/jmx/remote/custom.html
Here is the Java code that creates an mbeanProxy of a remote object
(full example at the previous URL):
ObjectName mbeanName = new ObjectName("com.example:type=Hello");
HelloMBean mbeanProxy = JMX.newMBeanProxy(mbsc, mbeanName,
HelloMBean.class, true);
echo("\nAdd notification listener...");
mbsc.addNotificationListener(mbeanName, listener, null, null);
echo("\nCacheSize = " + mbeanProxy.getCacheSize());
mbeanProxy.setCacheSize(150);
echo("\nWaiting for notification...");
sleep(2000);
echo("\nCacheSize = " + mbeanProxy.getCacheSize());
echo("\nInvoke sayHello() in Hello MBean...");
mbeanProxy.sayHello();
echo("\nInvoke add(2, 3) in Hello MBean...");
echo("\nadd(2, 3) = " + mbeanProxy.add(2, 3));
Here is how I'd port this to Sleep (with some preamble):
$url = [new JMXServiceURL: "service:jmx:rmi:///jndi/rmi://:9999/
jmxrmi"];
$jmxc = [JMXConnectorFactory connect: $url, $null];
$mbsc = [$jmxc getMBeanServerConnection];
# $listener would be set to something here probably as well.
...
$mbeanName = [new ObjectName: "com.example:type=Hello"];
$bmbeanProxy = [JMX newMBeanProxy: $mbsc, $mbeanName, ^HelloMBean,
1]; # 1 = true
println("\nAdd notification listener...");
[$mbsc addNotificationListener: $mbeanName, $listener, $null, $null];
println("\nCacheSize = " . [$mbeanProxy getCacheSize]);
[$mbeanProxy setCacheSize: 150];
println("\nWaiting for notification...");
sleep(2000);
println("\nCacheSize = " . [$mbeanProxy getCacheSize]);
println("\nInvoke sayHello() in Hello MBean...");
[$mbeanProxy sayHello];
println("\nInvoke add(2, 3) in Hello MBean...");
println("\nadd(2, 3) = " . [$mbeanProxy add: 2, 3]);
It helps to have a handle on how Sleep interacts with Java objects.
http://sleep.dashnine.org/manual/ Chapter 7 of the manual will cover
that. Just note that ^HelloMBean is the same as HelloMBean.class in
Java and [target method: arg, arg, ...] is how Sleep invokes stuff on
Java objects.
This code is a partial port but should help you get started.
Hope this helps?
-- Raphael
The method laid out by Sun works when you have access to the classes
or code of the mbeans you want to work with. In my case, I only have
documentation. From that I could write down the necessary interfaces
myself, but I don't want to write Java code!
GroovyMBean or Ruby (there's a library - jmx4r) use invokeMethod and
method_missing respectively to look up properties and method at
runtime; no need for the interfaces at compile time (i.e.
HelloMBean.class) and easy to make changes later.
sub getOperations
{
local('%ops $info $op');
$info = [$1 getMBeanInfo: $2];
# make %ops into a hash with:
# key => name of the operation
# value => a Java array representing the signature of the op
foreach $op ([$info getOperations])
{
# first we map the array of signatures to a function that
calls getType on each
# then we cast the resulting array of the map into a
java.lang.String[] array
# based on type of [$1 getType] which is String
%ops[[$op getName]] = cast(map({ return [$1 getType]; }, [$op
getSignature]), '*');
}
}
# $1 = mbean server, $2 = a bean object name
sub newBean
{
local('$name %operations');
$name = [new ObjectName: $2];
%operations = getOperations($1, $name);
return lambda(&invokeBean, $beanName => [new ObjectName: $2],
$beanServer => $1, \%operations);
}
# this closure proxies a JMX object. $0 is the method name and @_
are the arguments.
# for this to work--make sure your remote bean doesn't have two
methods with same name and different signature
# and this does no checking of how the arguments are mapped... good
luck!
sub invokeBean
{
if (%operations[$0] !is $null)
{
return [$beanServer invoke: $beanName, $0, cast(@_, 'o'), %
operations[$0]];
}
else
{
warn("No such operation $0");
}
}
There, now in theory you can do...
$bean = newBean($bserver, "beanName:whateve=itis");
$value = [$bean someOperation: arg, arg];
The newBean function will return a sleep closure (an instance of
&invokeBean) that will have a hash of all possible operations that
were derived when you used newBean. My goal in writing all this is
to show how a Sleep closure can act as an object and how you can
intercept calls to it and handle the dispatching of the call
yourself. Check out the closures section of chapter 5 in the Sleep
2.1 Manual where I discuss this technique in detail. http://
sleep.dashnine.org/manual/
This may need some tweaking (or it may not work!) and it doesn't do
attributes. You can peep the JMX API to see how to do attributes
(the Sleep code is probably similar).
If you get this working please consider posting back any improvements
so other Sleep hackers can take advantage of this.
-- Raphael
I'm sorry for not explaining it better at first; I've been working
with JMX and different dynamic languages for a while and it's all
sitting snug in my head -- not necessarily in other peoples'.
This code is elegant. I don't think attributes will be a problem; I'll
try it out tomorrow.
There are standard JMX methods to get mbean method names, parameter
names and types. We can use that later to differentiate any methods
with the same name.
Thanks!
Also, is it possible to see Sleep-internal stack traces?
On Mon, Sep 22, 2008 at 21:17, Raphael Mudge <rsm...@gmail.com> wrote:
>
http://sleep.dashnine.org/download/sleepp.jar
This reflects the code currently checked into the subversion
repository if you want to recompile it yourself. With:
$ svn export svn://svn.berlios.de/sleep/sleep
$ cd sleep
$ ant
Onto the next thing. Sleep only collects a stack trace when a Sleep
exception has been thrown. Sleep handles most errors with checkError
() by default but you can make Sleep throw an exception for any error
with debug(debug() | 34)
Within a catch block simply use printAll(getStackTrace()) to see the
internal stack trace. The trace will be collected up to the point of
the catch block you're executing in, so you can't do anything clever
like try { throw ...; } catch $ex { printAll(getStackTrace()); } to
peep the whole call stack.
You can throw an exception with throw if you choose.
-- Raphael
Use ^String inside of cast...
like:
cast(map({ return [$1 getType]; }, [$op getSignature]), ^String);
That should work. ^String is how you specify a class literal to the
Sleep interpreter. This will become a scalar object holding
java.lang.String.class. Its funny this isn't documented and I just
noticed the behavior works as such today (the code makes concessions
for it).
I tested it out with:
$ java -jar sleep.jar
>> Welcome to the Sleep scripting language
> interact
$array = cast(@("abc", "def", "ghi", 3), ^String);
.
println($array);
.
[Ljava.lang.String;@80cf2a
println([Arrays asList: $array]);
.
[abc, def, ghi, 3]
println(map({ return [$1 getClass]; }, [[Arrays asList: $array]
iterator]));
.
@(class java.lang.String, class java.lang.String, class
java.lang.String, class java.lang.String)
-- Raphael
import javax.management.remote.*;
import javax.management.ObjectName;
# $1 - mbean server conn, $2 - objectname
sub getOperations {
local('%ops $info $op @tmp');
$info = [$1 getMBeanInfo: $2];
# println([$info getOperations]);
foreach $op ([$info getOperations]) {
%ops[ [$op getName] ] = cast( map( { return [$1 getType]; }, [$op
getSignature] ), ^String);
}
return %ops;
}
sub getAttributes {
local('@attrs $info $attr');
$info = [$1 getMBeanInfo: $2];
foreach $attr ([$info getAttributes]) {
push(@attrs, [$attr getName]);
}
return @attrs;
}
# $2 - bean pattern
sub newBean {
local('$name %operations');
$name = [new ObjectName: $2];
%operations = getOperations($1, $name);
return lambda(&invokeBean, $beanName => [new ObjectName: $2],
$beanServer => $1, \%operations);
}
sub invokeBean {
if (%operations[$0] !is $null) {
return [$beanServer invoke: $beanName, $0, cast(@_, 'o'), %operations[$0]];
} else {
warn("No such operation $0");
}
}
sub main {
debug(debug() | 34);
local('$mbsc $conn');
$conn = [JMXConnectorFactory connect: [new JMXServiceURL:
"service:jmx:rmi:///jndi/rmi://localhost:9878/jmxrmi"]];
$mbsc = [$conn getMBeanServerConnection];
$bean = newBean($mbsc, "java.lang:type=Memory");
[$bean gc];
[$conn close];
}
main();