Autoload?

3 views
Skip to first unread message

sysprv

unread,
Sep 22, 2008, 6:11:44 AM9/22/08
to Sleep Developers
Hi :)

I'm writing some scripts to extract information from app servers by
using JMX. Right now the scripts are written in Groovy, making heavy
use of the GroovyMBean [http://groovy.codehaus.org/Groovy+and+JMX]
object that acts as a proxy; we get/set properties and invoke methods
on the proxy, and it handles talking to the JMX mbean server on our
behalf;
It's the same functionality as Ruby's method_missing and Python's
__getattr__.

Is something like this possible with Sleep? Perhaps with AUTOLOAD?

Raphael Mudge

unread,
Sep 22, 2008, 8:26:24 AM9/22/08
to sleep-de...@googlegroups.com
Good morning,
I'm not 100% sure what an AUTOLOAD or method_missing hook have to do
with this case and my knowledge of enterprise Java is almost zero.
That said I took a look at JMX and how Groovy does thing. It looks
like the JMX API allows you to create a BeanProxy that emulates the
behavior of a remote Java bean.

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

Ishan Oshadi Jayawardene

unread,
Sep 22, 2008, 11:30:19 AM9/22/08
to sleep-de...@googlegroups.com
Hi 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.

Raphael Mudge

unread,
Sep 22, 2008, 3:17:47 PM9/22/08
to sleep-de...@googlegroups.com
Ok, I did a little digging around and I understand what you're asking
now. Is there a way in Sleep to create an object proxy that given a
name looks up the appropriate signature and match the arguments to
that signature for invoking? Here is a guess but I've never worked
with JMX and I haven't tested this:

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

Ishan Oshadi Jayawardene

unread,
Sep 22, 2008, 5:33:09 PM9/22/08
to sleep-de...@googlegroups.com
That is neat!

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!

Ishan Oshadi Jayawardene

unread,
Sep 24, 2008, 4:25:16 AM9/24/08
to sleep-de...@googlegroups.com
I've hit one issue... How do I get an empty Object array from @() ?
cast(@(), 'o') throws "Warning: attempted an invalid index at ...".

Also, is it possible to see Sleep-internal stack traces?

On Mon, Sep 22, 2008 at 21:17, Raphael Mudge <rsm...@gmail.com> wrote:
>

Raphael Mudge

unread,
Sep 24, 2008, 10:55:13 AM9/24/08
to sleep-de...@googlegroups.com
Unfortunately Sleep automatically converts any Java array to a Sleep
array and so the only way to get a Java array in Sleep is with cast
(). Since this is a nasty bug I've uploaded a patched sleep.jar at:

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

sysprv

unread,
Sep 24, 2008, 2:22:07 PM9/24/08
to Sleep Developers
Great :)

Now I've hit another block...

Consider:
cast( map( { return [$1 getType]; }, [$op getSignature] ), '*');

When getSignature returns @(), &map returns @(). &cast with '*' turns
this into Object[].
However, what I need later (to pass to invoke, an array with the names
of the types of the parameters of the operation to be invoked) is
String[].

Can anything be done about this? Basically to turn Object[] {} to
String[] {}...
> > On Mon, Sep 22, 2008 at 21:17, Raphael Mudge <rsmu...@gmail.com>  
> >>> On Mon, Sep 22, 2008 at 14:26, Raphael Mudge <rsmu...@gmail.com>
> >>>>http://sleep.dashnine.org/manual/Chapter 7 of the manual will  

Raphael Mudge

unread,
Sep 24, 2008, 3:47:49 PM9/24/08
to sleep-de...@googlegroups.com
Yes. :)

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

Ishan Oshadi Jayawardene

unread,
Sep 24, 2008, 4:09:26 PM9/24/08
to sleep-de...@googlegroups.com
Great :) Here's a complete script that works. Minimal changes from
your untested version...

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();

Reply all
Reply to author
Forward
0 new messages