Runtime class loading
I'm working on an application and I need to do something kind of tricky. The classes needed to run will vary depending on what a user selects in the gui. If one thing is selected use these classes, if the other thing is selected use these other classes. The worst part of all this is that the jar files that contain the different classes interfere with each other. So if I put all of the jars in the classpath it stops working.
So what I'd like to do is use the URLClassLoader, if possible. So in my code I have this:
File root = new File(System.getProperty("user.dir"),"lib");
File[] jars = root.listFiles();
URL[] urls = new URL[jars.length];
for(int i=0;i<jars.length;i++) {
urls = jars.toURL();
}
loader = URLClassLoader.newInstance(urls,Main.class.getClassLoader());
In the File(System.getProperty("user.dir"),"lib") directory I put the jar files and it seems to be loading properly. But now what?Do I specifically load each class before I can use it using loader.loadClass()?>
[1066 byte] By [
sasdava] at [2007-10-3 0:37:03]

i love this. i love all of this.
> I'm working on an application and I need to do
> something kind of tricky.
Ill work from the premise that you know what youre doing and that indeed dynamic classloading is the correct solution to your problem.
But to be safe, if you doubt its the best or only solution you should run it by us.
> The worst part of all this is that the jar files
> that contain the different classes interfere with
> each other. So if I put all of the jars in the
> classpath it stops working.
why? you should be more specific about whats going on.
> But now what?Do I specifically
> load each class before I can use it using
> loader.loadClass()?
You will and you wont. If a class depends on another class the RESOLVE flag in a Classloaders load() method will load those classes.
It'd be bad form to rely totally on this resolve feature to load all your classes - so YES you have to load every class.
Ive done a lot of custom classloader stuff. Look into writing a custom classloader (there are many great tutorials by googling "java custom classloader"). Also keep in mind the problems that arise between classes loaded by different classloaders (as in the System classloader and your new custom one) and see if they might come up in whatever it is you are doing.
There isnt much advice beyond that b.c. you havent really explained what you are doing.
OK, more detail.
The application I'm developing is an MBean browser for WebSphere and WebLogic. Eventually I'll probably add JBoss to it but that's for the future.
Each application server requires it's own jar files be included in the classpath in order to establish the connection from the java application to the server. For WebLogic you need the weblogic.jar file and for WebSphere there is a list of about 19 jars to include. Both of the application servers support only version 1.0 of JMX, so I'm forced to use JDK 1.4. If I try anything higher the JMX classes included in the JDK, which are version 1.2, cause errors when connecting to the server.
In addition, if I have the weblogic.jar file in my classpath when I connect to a WebSphere server there are problems. For example, the connection is established but queries fail. Yes, I'm sure it's the weblogic.jar that causes the failure because when that is NOT in the classpath everything works to perfection.
So, I was hoping I could do this type of dynamic loading of the jar files, rather than including everything in the classpath.
By the way, I did try the Classloaders loadClass() method with the resolve flag and I was told it's protected.
Thanks for the help
first let me start by saying im not really qualified to help you b.c. im not familiar with WebSphere or WebLogic etc.
that being said, what im thinking is: this is NOT a case for dynamic classloading.
i may be totally misunderstanding what you are doing so ill repeat what i think is going on:
- you are having problems loading WS and WL together b.c. you claim that their jar files in the classpath are poison, lol.
- you are writing a program that communicates with WS and WL
- will your program communicate with both at the same time?
im a fan of a simple answer over eloborate schemes that can bite you later so id say: start the 3 apps (WS, WL, yours) with three different JVMs. You can have your program run a script that launches the other two. Now you just have to handle communication between your program and those.
The benefit is you can run your programs JVM as 1.5 or higher and have the other 2 run on the older JVM. Just keep the old JRE in a folder and have the run scrpt point to it. When you deploy you can provide the old JRE and let the program run on your users own JVM 1.4 or higher.
Whatever happened with this?
Sorry for the delay in getting back on this but vacation interferred with my fun :)
Yes, my application communicates with both WS and WL, and in the future with other application servers.
No, it won't be attached to both servers at the same time, but there may be time when I would be required to log on to one, log off, then log on to the other in the same session.
All three already run in different JVMs. In fact, they are running on different machines. WS and WL run in 1.4 and the JVM they use is included in their installations. So the only JVM I can control is the one running my application. And it has to be 1.4 because of the version of JMX WS and WL support in the versions of WS and WL my application is connecting to.
Well, I spent most of today working on it and I'm not any closer than I was a week ago.
Here's where I go wrong:
At this point in my code
if(serverType.equalsIgnoreCase("weblogic")) {
rmbs = new WebLogicServerInstance();
}
else if(serverType.equalsIgnoreCase("websphere")) {
rmbs = new WebSphereServerInstance();
}
I'm trying to use a static initializer in the WebLogicServerInstance, it looks like this:
static {
try {
File root = new File(System.getProperty("user.dir")+File.separator+"lib","weblogic.jar");
urlLoader = new URLClassLoader(new URL[] { root.toURL() } );
urlLoader.loadClass("javax.management.AttributeNotFoundException");
urlLoader.loadClass("javax.management.InstanceNotFoundException");
urlLoader.loadClass("javax.management.MalformedObjectNameException");
urlLoader.loadClass("javax.management.MBeanAttributeInfo");
urlLoader.loadClass("javax.management.MBeanException");
urlLoader.loadClass("javax.management.MBeanInfo");
urlLoader.loadClass("javax.management.MBeanServer");
urlLoader.loadClass("javax.management.ObjectInstance");
urlLoader.loadClass("javax.management.ObjectName");
urlLoader.loadClass("javax.management.ReflectionException");
urlLoader.loadClass("weblogic.management.Helper");
urlLoader.loadClass("weblogic.management.MBeanHome");
urlLoader.loadClass("weblogic.management.WebLogicMBean");
}
catch(Exception ex) {
ex.printStackTrace();
}
}
and what I'm getting is
java.lang.NoClassDefFoundError: javax/management/MBeanServer
at mbeanbrowser.MBeanBrowserMainWindow.login(MBeanBrowserMainWindow.java:191)
Line 191 is rmbs = new WebLogicServerInstance();
So it looks like one of three things.
First, the static initializer isn't being run. I tried adding a System.out.println("Initializer"); to the top of it and it never prints it. So maybe that is the problem. But I also tried a constructor instead of a static initializer and got exactly the same results at exactly the same point.
Second, you can't us URLClassLoader to load jar files that are not in the classpath. According to all my research (Google is a nice tool for developers) URLClassLoader CAN load jar files that are not in the classpath. So I hope I'm wrong about that one.
Third, I am too stupid to live. Right now my brain has turned to oatmeal so that's the way I'm leaning.
Any help for me or am I lost in the woods?
1) The code is failing in the initializer. That's running when the class is loaded for the first time. That's part of the trace is telling you where that is happening. There is probably some more in the stack trace where you would see it failing to execute the code in the initializer.
This is sort of a minor point but just wanted to mention.
2) Have you noticed that javax.management.MBeanServer is an interface? I mean don't you want MBeanServerFactory?
3) I hope not, oatmeal is soggy.
1. Here's the full stacktrace. If there is any helpful information in it I don't see it.
java.lang.NoClassDefFoundError: javax/management/MBeanServer
at mbeanbrowser.MBeanBrowserMainWindow.login(MBeanBrowserMainWindow.java:191)
at mbeanbrowser.MBeanBrowserMainWindow.access$200(MBeanBrowserMainWindow.java:54)
at mbeanbrowser.MBeanBrowserMainWindow$1.actionPerformed(MBeanBrowserMainWindow.java:107)
at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1786)
at javax.swing.AbstractButton$ForwardActionEvents.actionPerformed(AbstractButton.java:1839)
at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:420)
at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:258)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:245)
at java.awt.AWTEventMulticaster.mouseReleased(AWTEventMulticaster.java:231)
at java.awt.Component.processMouseEvent(Component.java:5100)
at java.awt.Component.processEvent(Component.java:4897)
at java.awt.Container.processEvent(Container.java:1569)
at java.awt.Component.dispatchEventImpl(Component.java:3615)
at java.awt.Container.dispatchEventImpl(Container.java:1627)
at java.awt.Component.dispatchEvent(Component.java:3477)
at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:3483)
at java.awt.LightweightDispatcher.processMouseEvent(Container.java:3198)
at java.awt.LightweightDispatcher.dispatchEvent(Container.java:3128)
at java.awt.Container.dispatchEventImpl(Container.java:1613)
at java.awt.Window.dispatchEventImpl(Window.java:1606)
at java.awt.Component.dispatchEvent(Component.java:3477)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:480)
at java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:201)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:151)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:145)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:137)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:100)
2. I tried MBeanServerFactory instead of MBeanServer and got exactly the same error.
3. I hope not too. I hate oatmeal.
What you need to grasp is that class loaders generally form a heirarchy. When you create a URLClassLoader, generally, it will only load classes itself which
aren't on the classpath, any that are it will delegate to the system class loaders. Once a class loader has loaded a class, that class will use that loader to load any classes is references.
Therefore there should be no need to explicitly load a whole bunch of classes.
Create a URLClassLoader which points to whichever library jar you want. Then, rather than loading all these utility classes you need to have the URLClassLoader load the class which references all these utility classes, which means that the class in question must not be on the class path.
You may want to create another URLClassLoader pointing to the location of the class that uses the library, intialising it with it's parent defined as the library ClassLoader.
References on a chain of class loaders are allowed only towards the system class loader, anything loaded from the classpath can't reference stuff from the URLClassLoader.
Yes, this is pretty much what I figured out during my struggle, and what I'm doing as well.
Currently I have nothing in my classpath, either on the commandline or as an environment variable. Also nothing in my lib\ext. I'm letting the system classloader load everything needed for the GUI, and I'm trying to load everything needed for JMX using the URLClassLoader. So for now my URLClassLoader is loading on jar file (weblogic.jar) and then attempting to load all of the JMX classes. But I'm getting the stacktrace I posted before and I can't figure out where it's happening or why.
How are you loading the class mbeanbrowser.MBeanBrowserMainWindow?
If that's loading as part of your main application stuff it won't be able to reference anything from your URLClassLoader. Classes loaded by the system class loader can't. A class can only reference classes loaded by the same classloader or one of it's parents, and that means the classloader that actually loaded it, not the one you requested it from.
When I've done this kind of thing I have a small main class, which creates URLClassLoaders one of which loads the real application from a completely separate code source and runs it.
Granted it's rather tiresome to set up, with two separate build directories, but it really is the only way I've found.
I'm not sure I understand any of that, but does that explain why the static initializer isn't working?
More oddness,I just ran it all again with -verbose and it's showing:[Loaded mbeanbrowser.WebLogicServerInstance]Doesn't that mean the static initializer should have run?
Maybe I'm not giving enough information. The class where the static initializer is failing implements an interface. The interface looks like this:
public interface ServerBeanInstance {
public void login(String host, String port, String username, String password) throws MBeanException;
public MBeanServer getMBeanServer() throws MBeanException;
public Set getDomains() throws MBeanException;
public Set getTypes(Object domain) throws MBeanException, MalformedObjectNameException ;
public Set getMBeans(Object domain,Object type) throws MBeanException, MalformedObjectNameException;
public Object getAttribute(ObjectName objName, String name) throws MBeanException, AttributeNotFoundException, InstanceNotFoundException, ReflectionException;
public MBeanAttributeInfo[] getAttributes(ObjectName objectName) throws MBeanException, AttributeNotFoundException, InstanceNotFoundException, ReflectionException;
}
Am I trying something that can't be done or have I run into a bug of some kind?
You are, I think, referencing a whole stack of classes and interfaces which are present in the URLClassLoader but not the system class loader. If your class is on the class path it can only reference classes loaded by the sytem class loader. It will give you a class not found exception when it tries to resolve references to your special library classes.
What you probably need to do is to generate two separate jars or class directories. The one containing your main class cannot reference any of these library routines, directly or otherwise. The other contains your real application classes which do.
The main class creates the URLClassLoaders, as discussed, and calls loadClass on them to get the real application class (which is not on the classpath). Then it invokes it by reflection, without having any reference to it.
Then your application classes will be loaded by a ClassLoader that can access the library classes you need and their references will be filled successfully.
Let's see if I'm understanding.
Here's my Main class
public class Main {
private static URLClassLoader classLoader;
public Main() throws Exception {
File root = new File(System.getProperty("user.dir")+File.separator);
classLoader = new URLClassLoader(new URL[] {root.toURL()});
Class c = classLoader.loadClass("mbeanbrowser.MBeanBrowserMainWindow");
MBeanBrowserMainWindow mainWindow = (MBeanBrowserMainWindow) c.newInstance();
mainWindow.pack();
mainWindow.setVisible(true);
}
public static URLClassLoader getLoader() {
return classLoader;
}
public static void main(String[] args) {
try {
new Main();
}
catch(Exception ex) {
ex.printStackTrace();
System.exit(1);
}
}
}
Is this headed in the right direction? My MBeanBrowserMainWindow is displaying fine but I'm still getting the exact same error in the exact same way. What changes do I need to make to WebLogicServerInstance? The initializer is shown on reply 7 of 16.
Does it matter how I invoke Main? Right now I'm in the directory with Main.class and I invoke it with
c:\j2sdk1.4.2_12\bin\java Main
Also in this directory is a lib directory containing weblogic.jar
Thanks
The trouble is you're still referencing the MBeanBrowserMainWindow class by using it in a variable declaration and cast. You can't reference it at all.
You don't need to, having created an instance cast it to JFrame (which it presumably extends) and set that visible.
The main class doesn't need to know exactly what class it is. Incidentally it's a good idea to set your classloader as the current thread's "context" class loader, which some indirect loading methods use.
Thread.currentThread().setContextClassloader(classLoader);
And note, this assumes that the class file for MBeanBrowserMainWindow.class is in the added directory, not on the classpath.
Mind you, AFAIKS, you are pointing the URLClassloader at the current directory, which probably is on the classpath (it's the default classpath). That doesn't look right.
OK, here's my new Main class:
package starter;
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.UIManager;
public class Main {
public Main() throws Exception {
File root = new File(System.getProperty("user.dir")+File.separator);
URLClassLoader classLoader = new URLClassLoader(new URL[] {root.toURL()});
Thread.currentThread().setContextClassLoader(classLoader);
Class c = classLoader.loadClass("mbeanbrowser.MBeanBrowserMainWindow");
javax.swing.JFrame mainWindow = (javax.swing.JFrame) c.newInstance();
mainWindow.pack();
mainWindow.setVisible(true);
}
public static void main(String[] args) {
try {
new Main();
}
catch(Exception ex) {
ex.printStackTrace();
System.exit(1);
}
}
}
I'm invoking this by this command:
java -classpath build\classes starter.Main
The mainwindow comes up fine, the GUI looks just the same. But I am still getting the exact same error in the exact same place. Where am I going wrong here?
My understanding was that using the -classpath option would remove the current directory (D:\Code\Starter) from the classpath. Is that wrong?
I am pointing the URLClassLoader to the current directory, but where else could I point it and how?
Where's the MBeanBrowserMainWindow.class file? Make sure there isn't one (even an obsolete one) on the classpath.I rarely write code using the current directory. You can put the path in in numerous ways, a System property, a properties file, whatever.
I'm invoking the program like this:
java -Dbrowser.path=D:\Code\MBeanBrowser\build\classes -classpath build\classes starter.Main
It seems like it's almost working now that I changed the URLClassLoader in Main so that it is created like this:
File root = new File(System.getProperty("browser.path")+File.separator);
File root2 = new File(System.getProperty("browser.path")+File.separator+"lib"+File.separator+"weblogic.jar");
URLClassLoader classLoader = new URLClassLoader(new URL[] {root.toURL(),root2.toURL()});
Thread.currentThread().setContextClassLoader(classLoader);
And in WebLogicServerInstance I have this:
static {
System.out.println("Initializer");
try {
File root = new File(System.getProperty("browser.path")+File.separator+"lib"+File.separator+"weblogic.jar");
URLClassLoader urlLoader = new URLClassLoader(new URL[] { root.toURL() }, Thread.currentThread().getContextClassLoader());
urlLoader.loadClass("javax.management.AttributeNotFoundException");
urlLoader.loadClass("javax.management.InstanceNotFoundException");
urlLoader.loadClass("javax.management.MalformedObjectNameException");
urlLoader.loadClass("javax.management.MBeanAttributeInfo");
urlLoader.loadClass("javax.management.MBeanException");
urlLoader.loadClass("javax.management.MBeanInfo");
urlLoader.loadClass("javax.management.MBeanServer");
urlLoader.loadClass("javax.management.ObjectInstance");
urlLoader.loadClass("javax.management.ObjectName");
urlLoader.loadClass("javax.management.ReflectionException");
urlLoader.loadClass("weblogic.management.Helper");
urlLoader.loadClass("weblogic.management.MBeanHome");
urlLoader.loadClass("weblogic.management.WebLogicMBean");
}
catch(Exception ex) {
ex.printStackTrace();
}
}
With this in place the static initializer is running and the login starts, but I'm getting AssertionErrors on one of the internal classes.
Not the best solution, in my opinion, because I was hoping to create a separate class loader for the JMX classes.
You definitely shouldn't need all those loadClass calls in the intialiser, the classes will be loaded automatically as they are referenced. In fact none of this intialisation code in the WebServerLogicInstance should be needed. It will have no effect anyway. You are loading these classes into a classloader which you then discard the reference to, which will discard the classes as well.
And you can have a separate classloader for your own code and for the library classes providing you make the library class loader the parent of the loader which loads your code.
The JVM has no central cache of loaded classes, each classloader instance caches the classes it loads itself. If ask a different classloader for the same class then, unless the first classloader is an ancestor of the second, it will attempt to load a new copy.
OK, good. That was some information I didn't have before. Helpful as it is, though, it doesn't resolve the problem I'm having.
I've modified this program and experimented with more approaches than I've ever needed to for any problem and I'm still getting the same error in the same place, and as far as I can see I'm no closer to a resolution than I was when I first opened this post. This is getting frustrating.
Hmm.. you are running on 1.5 aren't you? I notice the documentation for MBeanServer says "since 1.5" and that you don't use the new for syntax when you might.
No, unfortunately. The versions of WebSphere and Weblogic my program is connecting to use JMX 1.0, so I'm forced to use 1.4.2.
Then the MBeanServer thing isn't part of the standard API, and the question becomes one of access to the JMX library from the WebSphere stuff. Now I think you've put your JMX library in the URLClassLoader, in which case the Webspher e libraries also must be loaded via that route, rather than being on the classpath.
Or you could create another level of ClassLoader with the JMX one as it's parent to load websphere.
Actually, I haven't gotten to the WebSphere side yet. Right now I'm still trying to get weblogic to work using this dynamic loading technique.
The JMX library is in the weblogic.jar file, so it is supposed to be loading from there. But it's not, obviously, because of the error.
When I get around to the WebSphere side I was planning on creating another URLClassLoader to handle the 19 jar files that WebSphere requires for this. The JMX library is in one of those files TOO, which is the reason I'm not putting all 20 of the jar files in the classpath and calling it quits. I've tried that and things blow up as soon as I do.
FYI, MBeanServer isn't loaded with new. For Weblogic, first you login with this:
String url = "t3://" + host + ":" + port;
MBeanHome localHome = Helper.getAdminMBeanHome(username, password, url);
Then you get the MBeanServer by calling a method from the MBeanHome class which returns javax.management.MBeanServer:
localHome.getMBeanServer();
But we're not getting that far, obviously.
For WebSphere the login process is completely different, but I'll get to that later, hopefully.
I don't know much about WebLogic but, from the trace, it's happening very early in the process of loading WebLogicServerInstance, which suggests that WebLogicServerInstance probably implements or extends the offending class (check the API doc). If it were happening during initialisation the stack trace would show an <init> or <clinit> depending whether it was instance or class initialization.
Are you absolutely sure the class is in the same jar as the weblogic class?
Which class do you mean? MBeanServer? Yes, I'm positive it's in there.
WebLogicServerInstance implements ServerBeanInstance. It's my own class and doesn't extent or implement anything else. I posted the code for it earlier but I'll put it in again here:
package mbeanbrowser;
import java.util.Set;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceNotFoundException;
import javax.management.MalformedObjectNameException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.ReflectionException;
public interface ServerBeanInstance {
public void login(String host, String port, String username, String password) throws MBeanException;
public MBeanServer getMBeanServer() throws MBeanException;
public Set getDomains() throws MBeanException;
public Set getTypes(Object domain) throws MBeanException, MalformedObjectNameException ;
public Set getMBeans(Object domain,Object type) throws MBeanException, MalformedObjectNameException;
public Object getAttribute(ObjectName objName, String name) throws MBeanException, AttributeNotFoundException, InstanceNotFoundException, ReflectionException;
public MBeanAttributeInfo[] getAttributes(ObjectName objectName) throws MBeanException, AttributeNotFoundException, InstanceNotFoundException, ReflectionException;
}
I did it this way because the login to Weblogic and WebSphere are differnt, as I discussed before.
Is your ServerBeanInstance class on the classpath by any chance? If the system loader loaded it it wouldn't then be able to reference MBeanServer. You need all your classes in the off-classpath directory except that main class.
I notice MBeanServer is the first JMX class referenced in this interface.
I'm positive ServerBeanInstance isn't in the classpath. It's in mbeanbrowser along with MBeanBrowserMainWindow so the classloader created in Main should find it.
I notice MBeanServer was the first JMX class referenced too, so I moved that line to the bottom. MBeanException is the first one now and the error remains unchanged.
I'm still tending to think your problem is due to an attempt by a class unexpectedly loaded by the system classloader to reference the class in the URLClassLoader.
At this level of deperation I'd resort to some serious logging. For example overload the loadClass method on your class loader so it logs classes loaded, and, for each, whether Class.getClassloader() points back to itself.
i.e whether it loaded the class itself or did it delegate it.
I'm not sure if this is what you had in mind or not:
package starter;
import java.net.URL;
import java.net.URLClassLoader;
public class JarClassLoader extends URLClassLoader {
public JarClassLoader(URL[] urls) {
super(urls);
}
public Class loadClass(String name) throws ClassNotFoundException {
Class c = super.loadClass(name);
System.out.println(c.getClass().getName() + " loaded by " + c.getClassLoader());
return c;
}
}
And my Main class now looks like this:
package starter;
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import javax.swing.JFrame;
public class Main {
public Main() throws Exception {
File root = new File(System.getProperty("browser.path")+File.separator);
JarClassLoader classLoader = new JarClassLoader(new URL[] {root.toURL()});
Thread.currentThread().setContextClassLoader(classLoader);
Class c = classLoader.loadClass("mbeanbrowser.MBeanBrowserMainWindow");
javax.swing.JFrame mainWindow = (javax.swing.JFrame) c.newInstance();
classLoader.loadClass("mbeanbrowser.ServerBeanInstance");
mainWindow.pack();
mainWindow.setVisible(true);
}
public static void main(String[] args) {
try {
new Main();
}
catch(Exception ex) {
ex.printStackTrace();
System.exit(1);
}
}
}
Here's the output, hope it means more to you than it does to me:
java.lang.Class loaded by null
java.lang.Class loaded by starter.JarClassLoader@1893efe
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by starter.JarClassLoader@1893efe
java.lang.Class loaded by starter.JarClassLoader@1893efe
java.lang.Class loaded by starter.JarClassLoader@1893efe
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by starter.JarClassLoader@1893efe
java.lang.Class loaded by null
java.lang.Class loaded by starter.JarClassLoader@1893efe
java.lang.Class loaded by starter.JarClassLoader@1893efe
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by starter.JarClassLoader@1893efe
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by starter.JarClassLoader@1893efe
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by starter.JarClassLoader@1893efe
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by starter.JarClassLoader@1893efe
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.Class loaded by null
java.lang.NoClassDefFoundError: javax/management/MBeanServer
at mbeanbrowser.MBeanBrowserMainWindow.login(MBeanBrowserMainWindow.java:191)
at mbeanbrowser.MBeanBrowserMainWindow.access$200(MBeanBrowserMainWindow.java:54)
at mbeanbrowser.MBeanBrowserMainWindow$1.actionPerformed(MBeanBrowserMainWindow.java:107)
at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1786)
at javax.swing.AbstractButton$ForwardActionEvents.actionPerformed(AbstractButton.java:1839)
at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:420)
at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:258)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:245)
at java.awt.AWTEventMulticaster.mouseReleased(AWTEventMulticaster.java:231)
at java.awt.Component.processMouseEvent(Component.java:5100)
at java.awt.Component.processEvent(Component.java:4897)
at java.awt.Container.processEvent(Container.java:1569)
at java.awt.Component.dispatchEventImpl(Component.java:3615)
at java.awt.Container.dispatchEventImpl(Container.java:1627)
at java.awt.Component.dispatchEvent(Component.java:3477)
at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:3483)
at java.awt.LightweightDispatcher.processMouseEvent(Container.java:3198)
at java.awt.LightweightDispatcher.dispatchEvent(Container.java:3128)
at java.awt.Container.dispatchEventImpl(Container.java:1613)
at java.awt.Window.dispatchEventImpl(Window.java:1606)
at java.awt.Component.dispatchEvent(Component.java:3477)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:480)
at java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:201)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:151)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:145)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:137)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:100)
java.lang.Class loaded by null
I'm confused by the "loaded by null" statements. Does this mean that it's being loaded by the system classloader, as you suspected? Is there a better way to do this? Be gentle with me, it's my first time writing a classloader.
Thanks
The trouble is you didn't print the name of the class being loaded the mistake was the getClass() when you are already looking at the class.
I think classes with a null classloader tend to be the ones which are loaded by the "primordial" class loader, classes like Object and Class which are actually hard coded into the JVM.
I had in mind something like:
public Class loadClass(String name) throws ClassNotFoundException {
Class c = super.loadClass(name);
ClassLoader cl = c.getClassLoader();
System.out.println("Loaded " + name + (cl == null ? " primordial " : cl == this ? " me" : " other");
return c;
}
All right, the results are pretty much the same even if it's a little clearer now:
Loaded javax.swing.JFrame primordial
Loaded mbeanbrowser.MBeanBrowserMainWindow me
Loaded java.lang.Object primordial
Loaded javax.swing.tree.TreeNode primordial
Loaded mbeanbrowser.ServerBeanInstance me
Loaded mbeanbrowser.WebLogicServerInstance me
Loaded mbeanbrowser.WebSphereServerInstance me
Loaded javax.swing.tree.MutableTreeNode primordial
Loaded java.awt.Component primordial
Loaded java.awt.Container primordial
Loaded java.lang.Throwable primordial
Loaded java.io.IOException primordial
Loaded java.util.logging.Formatter primordial
Loaded java.util.logging.SimpleFormatter primordial
Loaded java.util.logging.Handler primordial
Loaded java.util.logging.FileHandler primordial
Loaded java.io.InputStream primordial
Loaded java.io.FileInputStream primordial
Loaded java.awt.event.ActionListener primordial
Loaded javax.swing.JButton primordial
Loaded javax.swing.JToolBar primordial
Loaded javax.swing.tree.TreeModel primordial
Loaded javax.swing.event.TreeSelectionListener primordial
Loaded javax.swing.JTree primordial
Loaded javax.swing.JScrollPane primordial
Loaded javax.swing.table.TableModel primordial
Loaded javax.swing.JTable primordial
Loaded javax.swing.JSplitPane primordial
Loaded java.awt.LayoutManager primordial
Loaded javax.swing.JLabel primordial
Loaded javax.swing.JPanel primordial
Loaded javax.swing.plaf.basic.BasicPanelUI primordial
Loaded com.sun.java.swing.plaf.windows.WindowsRootPaneUI primordial
Loaded java.util.logging.Logger primordial
Loaded java.util.Properties primordial
Loaded java.io.File primordial
Loaded java.lang.System primordial
Loaded com.sun.java.swing.plaf.windows.WindowsToolBarUI primordial
Loaded com.sun.java.swing.plaf.windows.WindowsBorders primordial
Loaded com.sun.java.swing.plaf.windows.WindowsButtonUI primordial
Loaded javax.swing.plaf.basic.BasicBorders primordial
Loaded mbeanbrowser.MBeanBrowserMainWindow$1 me
Loaded java.lang.Exception primordial
Loaded mbeanbrowser.MBeanBrowserMainWindow$2 me
Loaded mbeanbrowser.MBeanBrowserMainWindow$3 me
Loaded java.io.OutputStream primordial
Loaded java.io.FileOutputStream primordial
Loaded com.sun.java.swing.plaf.windows.WindowsSplitPaneUI primordial
Loaded javax.swing.tree.DefaultTreeModel primordial
Loaded javax.swing.tree.DefaultMutableTreeNode primordial
Loaded com.sun.java.swing.plaf.windows.WindowsTreeUI primordial
Loaded java.awt.Image primordial
Loaded java.util.List primordial
Loaded java.lang.String primordial
Loaded java.net.URL primordial
Loaded com.sun.java.swing.plaf.windows.WindowsLabelUI primordial
Loaded mbeanbrowser.MBeanBrowserMainWindow$BeanTreeSelectionListener me
Loaded javax.swing.plaf.basic.BasicViewportUI primordial
Loaded com.sun.java.swing.plaf.windows.WindowsScrollBarUI primordial
Loaded javax.swing.plaf.basic.BasicScrollPaneUI primordial
Loaded javax.swing.table.DefaultTableModel primordial
Loaded mbeanbrowser.BeanInfoTableModel me
Loaded java.io.Writer primordial
Loaded java.io.FileWriter primordial
Loaded java.util.Vector primordial
Loaded com.sun.java.swing.plaf.windows.WindowsTableHeaderUI primordial
Loaded javax.swing.plaf.basic.BasicTableUI primordial
Loaded java.awt.FlowLayout primordial
Loaded javax.swing.BorderFactory primordial
Loaded mbeanbrowser.ServerBeanInstance me
Loaded sun.io.CharToByteCp1252 primordial
Loaded sun.awt.windows.CharToByteWingDings primordial
Loaded sun.awt.CharToByteSymbol primordial
Loaded javax.swing.plaf.BorderUIResource primordial
Loaded mbeanbrowser.LoginPanel me
Loaded javax.swing.JComboBox primordial
Loaded javax.swing.JTextField primordial
Loaded javax.swing.JPasswordField primordial
Loaded java.awt.GridBagLayout primordial
Loaded java.awt.GridBagConstraints primordial
Loaded java.awt.Insets primordial
Loaded java.awt.event.ActionEvent primordial
Loaded javax.swing.JOptionPane primordial
java.lang.NoClassDefFoundError: javax/management/MBeanServer
at mbeanbrowser.MBeanBrowserMainWindow.login(MBeanBrowserMainWindow.java:191)
Loaded java.awt.Cursor primordial
at mbeanbrowser.MBeanBrowserMainWindow.access$200(MBeanBrowserMainWindow.java:54)
at mbeanbrowser.MBeanBrowserMainWindow$1.actionPerformed(MBeanBrowserMainWindow.java:107)
at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1786)
at javax.swing.AbstractButton$ForwardActionEvents.actionPerformed(AbstractButton.java:1839)
at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:420)
at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:258)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:245)
at java.awt.AWTEventMulticaster.mouseReleased(AWTEventMulticaster.java:231)
at java.awt.Component.processMouseEvent(Component.java:5100)
at java.awt.Component.processEvent(Component.java:4897)
at java.awt.Container.processEvent(Container.java:1569)
at java.awt.Component.dispatchEventImpl(Component.java:3615)
at java.awt.Container.dispatchEventImpl(Container.java:1627)
at java.awt.Component.dispatchEvent(Component.java:3477)
at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:3483)
at java.awt.LightweightDispatcher.processMouseEvent(Container.java:3198)
at java.awt.LightweightDispatcher.dispatchEvent(Container.java:3128)
at java.awt.Container.dispatchEventImpl(Container.java:1613)
at java.awt.Window.dispatchEventImpl(Window.java:1606)
at java.awt.Component.dispatchEvent(Component.java:3477)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:480)
at java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:201)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:151)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:145)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:137)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:100)
So, I guess that answers that question. The system classloader is still loading the majority of classes, especially the GUI classes. But the URLClassLoader (in the guise of JarClassLoader) is loading my classes, including:
MBeanBrowserMainWindow and all of its anonymous inner classes
ServerBeanInstance
WebLogicServerInstance
WebSphereServerInstance
In fact, ServerBeanInstance is loaded twice by JarClassLoader.
What does that tell us?
AFAIKS that pretty much elimnates ClassLoader problems as a likely source of the bug.
You could try puting a log out both before and after the super.loadClass() so you can make absolutely sure it's that classloader that's trying, and failing to load MBeanServer, but I expect you'll find it is, since ServerBeanInstance is pointing at the right loader.
You could try extracting, and decompiling the MBeanServer.class file from the jar and making sure that, for example, the class is public.
Hang on a sec though. I didn't really check the URLs that the URLClassLoader is pointed at, but, given you adding a separater to the end I guess it's a directory. You do know you need a separate URL for each jar file to be accessed? Give it a directory and it expects unjarred class files. (When you've gone through the exotic it's time to check the obvious ;-) )
Have you run this stuff OK by putting everything on the class path?
Message was edited by:
malcolmmc
The URLClassLoader in starter.Main is loading the GUI classes in a directory. The URLClassLoader in WebLogicServerInstance is loading classes from a jar file, weblogic.jar to be specific.
Everything works fine when it's all in the classpath, except that the javax.management classes interfere with each other, as I described earlier.
Well, that could be the problem, I didn't realise you still had two classloaders active.
The classloader that loads your GUI components must be able to load classes from the weblogic.jar file too. So either you need to put both URLs into the one classloader or the GUI classloader needs to have the other as a parent, i.e the jar classloader needs to be set up first. Remember references can only go from a class' classloader to it's ancestors.
Incidentally you don't need to add a / to the end of directory paths - File.toURL should sort that out.
The sound you are hearing is me banging my head on the desk.
I guess this takes us back to my original question, doesn't it. In my program if I select "WebLogic" from the JComboBox I want to load the classes located in weblogic.jar. If I select "WebSphere" I want to load the classes located in the other 19 jar files. I cannot put the 20 jar files in the classpath or put all 20 URL's into the constructor for one URLClassLoader because the javax.management classes interfere with each other.
So, am I stuck writing two versions of the same program or can I do this?
You can still do it, but it means a more careful separation between the new runner package and the GUI package, specifically the runner package needs to make the selection before it invokes the "real" (i.e. weblogic using) classes. You might want. for example, to separate your JFrame from it's working contents. You'd open the JFrame, probably empty, ask for the selection, then load the browser part, passing it a reference to your JFrame which it then populates.
I'm not sure that approach is feasible since the selection process is part of the GUI, but I'll work on it and see if I can make it work.
I think maybe you need more view/model separation with a common view on two versions of the model.
Whatever that means :)
I'm getting closer, I think. I'm bringing up the login screen before the main window comes up and it's connecting correctly. That's a MAJOR improvement.
I'm getting some new errors now but I'm not sure if the problem is my technique or the server I'm connecting to.
Unable to access I18n properties file, weblogic/i18n/i18n.properties: java.io.FileNotFoundException: Response: '404: Not Found' for url: 'http://10.11.12.238:7501/bea_wls_internal/classes/weblogic/i18n/i18n.properties'
java.lang.ExceptionInInitializerError
at weblogic.j2ee.ApplicationManager.loadClass(ApplicationManager.java:309)
at weblogic.j2ee.ApplicationManager.loadClass(ApplicationManager.java:258)
at weblogic.j2ee.ApplicationManager.loadClass(ApplicationManager.java:253)
at weblogic.j2ee.ApplicationManager.loadClass(ApplicationManager.java:216)
at weblogic.rmi.internal.ClientRuntimeDescriptor.computeInterfaces(ClientRuntimeDescriptor.java:239)
at weblogic.rmi.internal.ClientRuntimeDescriptor.intern(ClientRuntimeDescriptor.java:127)
at weblogic.rmi.internal.StubInfo.readObject(StubInfo.java:121)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:324)
at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:838)
etcetcetc
> Whatever that means :)
It's called "loose coupling", dividing your classes between data handling and view components. Hopefully, then, you've class path complexities can be confined to the data classes.
Where you need two possible versions of a model class (in your case because they need different classloader setups to load them) you would peal of an interface, which both of them would implement, The interface can then be back on the classpath. So you need muck about with class loaders only when instaciating such an object and thereafter all access to it goes through the interface. You can create a "factory class" which instanciates all these variant classes according to the version you required. That confines all the classpath stuff to a single, static getInstance() method in an abstract ancestor of the factory class.
So wherever you'd currently use a new on one of the model classes you get the appropriate instance of the factory class and call a createXXX method on it, the object returned will be a concrete class in the appropriate environment, but you use it by casting it to an interface which is fully available.
You can even have both sets of classes going at the same time (it's permissiable to have two classes with the same FQN loaded if they aren't on the same line of descent in the classloader tree).
I don't know enough about WebLogic and the way you are using it to be certain such separation is practical but I would think it should be.
If you use an IDE like Netbeans there are facilities for automatically generating an interface which defines all the methods of an existing class.
>
> Unable to access I18n properties file,
> weblogic/i18n/i18n.properties:
> java.io.FileNotFoundException: Response: '404: Not
> Found' for url:
> 'http://10.11.12.238:7501/bea_wls_internal/classes/web
> logic/i18n/i18n.properties'
>
Now that has to be an error in your WebLogic server setup. It's a failure to connect to a server, probably not a program problem at all.
No, it's definately a program problem. My Weblogic only version of the program connects without a problem.The weblogic/i18n/i18n.properties object is in the weblogic.jar file, so that leaves the question, can URLClassLoader load a resource other than a class out of a local jar?
Sure it can. In fact that's one of the scenarios that made me suggest setting the context classloader, but it depends how the class that loads the resource requests it. If it uses getClass().getResource() then it will use the classloader that loaded the class doing the loading.
However the error message you got exhibited a domain:port format which points to a connection, rather than a resource within the class heirarchy.
Of course it may have the wrong domain due to not reading the right .properties file. It looks like the sort of thing you may have to configure.
I would suppose that this would be easy to implement as three applications...
1. The GUI
2. App to talk to websphere
3. App to talk to weblogic
The GUI uses sockets to talk to the two apps. The GUI can even launch the two apps. The two apps talk to the respective J2EE container. All that is needed is an interface to each. Each can be tested seperately. No classloaders are needed at all.
It even easily supports adding yet another J2EE container.
Just for the sake of curiosity, how would the GUI application invoke the others? Using Runtime?
> Just for the sake of curiosity, how would the GUI
> application invoke the others? Using Runtime?
Yep.
Or you could just install them as a service on the box as well.
Technically you could just install them as servers too. Then only one install would be needed. You could even just do one that way because then the other j2ee could be accessed directly in the gui.
Installing as services? Wouldn't that do away with crossplatform?
> Installing as services? Wouldn't that do away with> crossplatform?Huh?You can run a cron job on a unix box - it serves the same purpose in that the GUI would then not have to explicitly start it.
That may be a little complex for something that's supposed to be a fairly simple diagnostic tool.