I'm currently investigating different solutions to manage our applications remotely (and from a single point), and JMX sounds like something that might satisfy our needs. So far it seems to do what I want, but there are some areas that are still unclear. Some clarifications will be appreciated.
Let's say I want to manage different applications using JMX, and that these applications are made of components that are wrapped as MBeans. So I will register all the mbeans of application A in a particular domain, all the mbeans of application B in another domain, and so on. This way I can limit the client to only manage mbeans from a particular application.
However, what I realize is that if I do that, all the mbeans will share the same classloader, which is bad. I don't want application A component to see application B component, of course, neither do I want application A component to access application B ressources.
I saw in the documentation that when you register (or create) a mbean on the mbeanServer, you can pass the ObjectName of a classloader, and that will create the mBean using that classloader. Does that mean that I have to handle classloaders by hand, manually instanciating classloaders before attempting to register my applications' components, passing a different one for each application?
At first I thought that using different domains for components would use different classloaders, but it doesn't seem to be the case.
Actually, as I'm typing, I'm realizing that using their own classloader won't be enough. Actually I don't want component from domain A to be able to invoke mbeans from domain B.
So to summarize my question: Is there an elegant way to automatically encapsulate some mbeans so that they are isolated from others?
Would registering the MBeans in separate MBeanServers solve your problem? If you want to prevent direct method calls, it may not solve your problem, but it may if you are trying to prevent calls using jmx api.
There are several layers of answers to your question.
Part 1:
If you want to prevent MBeans from different application to see each other
then using one MBeanServer per application seems like a good idea.
To expose these MBeans to remote clients, you would then normally need to
create one connector server per MBeanServer.
If having one connector server per MBeanServer is not desirable, then you could
instead use "cascading". You could write your own,
http://weblogs.java.net/blog/emcmanus/archive/2007/02/cascading_its_a.html
or use the OpenDMK cascading feature http://opendmk.dev.java.net/
(see CascadingAgent and MBeanServerConnectionFactory).
The idea here would be to create one MBeanServer per application, and then "mount"
all these MBeanServers in a master MBeanServer. You would then only need to create
a single connector server to expose your "master" MBeanServer.
MBeans from application A (a1 and a2) would only see MBeans from application A
(a1 and a2). MBeans from application B (b1, b2, b3) would only see MBeans from
application B (b1, b2, b3). However a client of the master MBeanServer would see
all MBeans: (A/a1, A/a2, B/b1, B/b2, B/b3).
Note: Cascading/Federation of MBeanServers is also discussed for standardization
in JMX 2.0 (JSR 255) expert group. Eamonn McManus will say a few words
about this in his talk at Java One tomorrow.
http://weblogs.java.net/blog/emcmanus/archive/2007/04/jmx_technology_2.html
Part 2:
Concerning classloaders, if you create an MBean, then register it using registerMBean,
e.g.:
1: My my = new MyMBean();
2: ObjectName myMBeanName = new ObjectName("my:type=My,name=x");
3: server.registerMBean(my, myMBeanName);
then you don't need to specify any classloader to the MBeanServer: the classloader
of your MBean is the classloader used to load the class MyMBean at line 1.
In other words: all depends on how/where these three lines of codes are executed, and
you can usually control that.
Another possibility is to use a PrivateMLet. Create a PrivateMLet that points to the jars
of application A, and register it in the MBeanServer you created for application A.
Then, for each MBean of application A you want to create use the server.createMBean
form that takes a loader name, the loader name being that of the PrivateMLet for
application A.
http://java.sun.com/javase/6/docs/api/javax/management/loading/PrivateMLet.html
1: PrivateMLet mletA = new PrivateMLet(...);
2: ObjectName loaderForA = new ObjectName("my:type=MLet,name=A");
3: server.registerMBean(mletA, loaderForA); // register the MLet for A
4: ObjectName myMBeanName = new ObjectName("my:type=My,name=x");
5: server.createMBean("my.MyClass",myMBeanName,loaderForA,...);
Be careful that there are some security implication when creating an MLet.
If you expose MLet MBeans to remote clients you will probably want to limit/control their access by setting up the appropriate security when creating your connector server.
http://blogs.sun.com/lmalventosa/entry/jmx_authentication_authorization
Hope this helps,
-- daniel
JMX, SNMP, Java, etc...
http://blogs.sun.com/jmxetc
Re: Managing multiple applications & classloaders
May 10, 2007 9:02 AM
(reply 3
of 10) (In reply to
#2 )
Hi,
Thanks a lot for your very complete and insightful response. That answers very well to my question. It removes some of the initial fears I had with JMX.
I will continue to play around with JMX with the new information you gave me, and if I have more questions, I will definitely come back here! There are some good and helpful folks around here!
I played a little with PrivateMlet, as it seems to me that it's the best way to achieve classloader separation, but it doesn't seem to work as expected. Beside the javadoc, I haven't found any detailed info on how to use them, so a little bit of help would certainly be welcomed!
Here is what I do:
I have a main class that create the mBeanServer and creates two mbean. I want these beans to have separated classloader.
So I created two folders, folder1 and folder2, each one containing a different jar file. These two folders are not in the classpath of my main class. The two mBean are on the main class classpath. They are both at the root of my base directory (where the app runs).
Before creating my mbeans, I do something like that:
URL url1 = new File("folder1").toURI().toURL();
PrivateMLet mlet1 = new PrivateMLet(new URL[] {url1}, false);
ObjectName nameMLet1 = new ObjectName("my:type=MLet,name=1");
mBeanServer.registerMBean(mlet1, nameMLet1);
( For the URL, I tried too putting "folder1/[nameOfTheJar].jar", with the same result )
As I understand, this PrivateMLet is a classloader that should have my jar in its classpath. So after that I do this:
However, in my mbean I try to do, for exemple, a Class.forName("[name of a class in the jar]"), and I have a ClassNotFoundException.
Actually I was expecting this forName to work, while in my other mBean, where I don't define that jar in the PrivateMLet, I was expecting the error. That would prove that classloaders are separated.
So I suspect that I do something wrong (in fact I'm pretty sure :) ). Am I right thinking that the URL[ ] is used to define the classpath?
That's the only hurdle left in my path to JMX adoption :)
Re: Managing multiple applications & classloaders
May 14, 2007 8:05 AM
(reply 7
of 10) (In reply to
#6 )
Hi,
I suggest you try the following:
final File myjar = new File("folder1/myjar.jar");
if (!myjar.canRead())
thrownew IOException("folder1/myjar.jar: no such file");
URL url1 = myjar.toURI().toURL();
PrivateMLet mlet1 = new PrivateMLet(new URL[] {url1}, false);
ObjectName nameMLet1 = new ObjectName("my:type=MLet,name=1");
mBeanServer.registerMBean(mlet1, nameMLet1);
AFAIK, the URL must be similar to a 'classpath element', that is, it must be
either a jar file, or a directory that contains classes - like what you would
put in the classpath.
If the URL is a directory, then it must end with '/' (otherwise, it's assumed to
be a jar) - see <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/net/URLClassLoader.html#URLClassLoader(java.net.URL[])">API doc</a>.
Let me know if this works!
BTW - you could also take a look at what I did here (steps 3 and 4) - it certainly
did work for me:
http://blogs.sun.com/jmxetc/entry/tracing_jmx_what_s_going
BTW2 - you will find an example here
http://docs.sun.com/app/docs/doc/816-7609/6mdjrf877?a=view
-- daniel
JMX, SNMP, Java, etc...
http://blogs.sun.com/jmxetc
Actually, the behavior is as expected. There's a confusion about the MBeanServer classloading and application level classloading. They are two totally different things.
The key thing to notice here is that as you said the two mBean are on the main class classpath (i.e. application class path).
So, even when you do: mBeanServer.createMBean([mbean classname], null, nameMLet1);
it will be the application classloader and not a PrivateMLet classloader that will be the defining class loader of your mbean.
That is why when you try to do in your mbean: Class.forName("[name of a class in the jar]") the PrivaeMLet won't be used as the classloader in this case but the application class loader, as invoking Class.forName(String className) is equivalent to invoking Class.forName(className, true, currentLoader), where currentLoader denotes the defining class loader of the current class.