Problem in setting ClassLoader
I m trying to Change the class Loader in a programm. I m using a
instance of URLClassLoader . plz look at the following programm.
import java.net.*;
import org.python.util.PythonInterpreter;
import org.python.core.*;
class Test
{
publicstaticvoid main(String[] args)throws Exception
{
System.out.println("Hello World!");
URL url1=new URL("file:///C:/MyProjectWorks/Flarion/MSS_JAVA/lib/jython.jar");
URL url2=new URL("file:///C:/MyProjectWorks/Flarion/MSS_JAVA/lib/jython-engine.jar");
URL[] urls={url1,url2,url3};
URLClassLoader myloader=new URLClassLoader(urls);
Thread.currentThread().setContextClassLoader(myloader);
PythonInterpreter interp =new PythonInterpreter();
interp.exec("import sys");
interp.exec("print sys");
}
}
i m getting the following error while executing the above programm
Exception in thread "main" java.lang.ClassNotFoundException: PythonInterpreter
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at Test.main(Test.java:47)
I m wondering why this is showing ClassNotFoundException as my classLoader contains the required jars.Plz help
# 1
try giving your classloader a parent
# 2
According to java doc if u dont specify a parent classLoader in the constructor of URLClassLoader then it constructs a new URLClassLoader for the specified URLs using the default delegation parent ClassLoader.
But still i tried to specify a parent classLoader using the following sniplet of code but i got the same error
URLClassLoader myloader=new URLClassLoader(urls,Thread.currentThread().getContextClassLoader());
Exception in thread "main" java.lang.NoClassDefFoundError: org/python/util/Pytho
nInterpreter
at Test.main(Test.java:50)
# 3
That's not the same error at all. Look again. You've done something to change the behaviour.For what that exception means, see reply #1 http://forum.java.sun.com/thread.jspa?threadID=5160016&tstart=0
ejpa at 2007-7-11 23:21:55 >

# 4
I dint get much information from that reply. when i m settng the classpath manually using set classpath i dont have any problem.
But the question which i m asking is that as i m using a URLClassLoader and setting this classloader as current threads classLoader but still the JVM is unable to find PythonInterpretor class.
I can successfully load classes without any exception by using the following sniplet of code.
myloader.loadClass("org.python.util.PythonInterpreter");
But i m wondering why JVM could not load this class when required.
plz let me know if u need any more information
# 5
There's a big difference between ClassNotFoundException and NoClassDefFoundException. The first one means the class wasn't there. The second one means either that the class was there but some class it relies on wasn't, or more probably that the .class file was found in the expected location but the package or class name in the .class file didn't agree with the location. So you need to check your JAR file layout.
ejpa at 2007-7-11 23:21:55 >

# 6
Take a look at the folwwing programm...
import java.net.*;
import org.python.util.*;
import org.python.core.*;
public class Test
{
public static void main(String[] args) throws Exception
{
System.out.println("Hello World!");
URL url1=new URL("file:///C:/MyProjectWorks/Flarion/MSS_JAVA/lib/jython.jar");
URL url2=new URL("file:///C:/MyProjectWorks/Flarion/MSS_JAVA/lib/jython-engine.jar");
URL[] urls={url1,url2};
URLClassLoader myloader=new URLClassLoader(urls,Thread.currentThread().getContextClassLoader());
Thread.currentThread().setContextClassLoader(myloader);
myloader.loadClass("org.python.util.PythonInterpreter");
System.out.println("Loadded Succesfully..");
org.python.util.PythonInterpreter interp =
new org.python.util.PythonInterpreter();
interp.exec("import sys");
interp.exec("print sys");
}
And this is the output which i got ..
C:\Temp>java Test
Hello World!
Loadded Succesfully..
Exception in thread "main" java.lang.NoClassDefFoundError: org/python/util/Pytho
nInterpreter
at Test.main(Test.java:20)
As u can see from the output i dont get exception while loading the PythonInterpreator class by folllowing sniplet of code
myloader.loadClass("org.python.util.PythonInterpreter");
That means thers no problem with the jar and the class Loader, but why i m getting the error while executing ..
org.python.util.PythonInterpreter interp =
new org.python.util.PythonInterpreter();
plz help me ..
# 7
You should ceratinly be using Class.newInstance() instead of 'new' in this situation.
ejpa at 2007-7-11 23:21:55 >

# 8
i know tat ..But i want to know why i m getting error with this code
# 9
Because the system class loader is being used to load the Python class, because the Python class is statically referenced from the Test class which was loaded by the system class loader. If it wasn't, you would be OK.
ejpa at 2007-7-11 23:21:55 >

# 10
But i m changing the default classLoader classLoader by the follwing statement which should work..Thread.currentThread().setContextClassLoader(myloader);
# 11
But you can't change the classloader that was used to load the Python class, because it's already loaded. It's looking as though classes which a class depends on are loaded by the same class loader that loaded the original class. But I'm not an expert on this, maybe someone else has a better explanation.
ejpa at 2007-7-11 23:21:55 >

# 12
> But i m changing the default classLoader classLoader
> by the follwing statement which should work..
>
> Thread.currentThread().setContextClassLoader(myl
> oader);
No, you can not change the 'default' class loader. All you are doing is changing the context class loader. That only matters for source code that is written to care about what the context classloader is. The context class loader is nothing special, its simply a place to store a class loader that you can retrieve with getContextClassLoader...
# 13
you can change the "default" classloader using a command-line argument, can't remember what right now, though. google it for a quicker response
# 14
Thanks _dnoyeB for the correction.
ejpa at 2007-7-11 23:21:55 >

# 15
Thanks _dnoyeB and ejp for your reply.
# 16
Forget about
Thread.currentThread().setContextClassLoader(myloader);
I have seen this code snippet over and over again, it has no effect for what you want to do.
try
Class pythonInterpreterClass = Class.forName("org.python.util.PythonInterpreter", true, myloader);
Object interp = pythonInterpreterClass.getConstructor(new Class[] {}).newInstance(new Object[] {});
or
Class pythonInterpreterClass = myloader.loadClass("org.python.util.PythonInterpreter");
Object interp = pythonInterpreterClass.getConstructor(new Class[] {}).newInstance(new Object[] {});
both statements would have the same effect: they would load the class from the class loader myloader, retrieve the empty constructor and create a new object from it.
You can manipulate your object with reflection the following way:
java.lang.reflect.Method execMethod = pythonInterpreterClass.getMethod("exec", new Class[] { String.class });
execMethod.invoke(interp, new Object[] { "import sys" });
execMethod.invoke(interp, new Object[] { "print sys" });
If this last snippet of code doesn't work, then tell so. It is possible that the getMethod method and the invoke method do not recognise the String class and the char[]-s as these are loaded by the system class loader and they would have to be loaded by the custom class loader "myloader" for them to be compatible with pythonInterpreterClass and execMethod. Classes loaded by different class loaders are not compatible. Though for core Java classes every class loader would have to fall back automatically on the system classes. If this is not the case, there is a workaround, but it is much more verbose. You will have to load the String class and character array class explicitly with "myloader" then.
# 17
> It is possible that the getMethod method and the
> invoke method do not recognise the String class and
> the char[]-s as these are loaded by the system class
> loader and they would have to be loaded by the custom
> class loader "myloader" for them to be compatible
> with pythonInterpreterClass and execMethod.
No they wouldn't. This difficulty is not possible. It is imaginary.
> Classes loaded by different class loaders are not compatible.
That statement refers to the same class loaded by two loaders.
ejpa at 2007-7-21 19:54:09 >

# 18
Of the two options I gave to load a class, this is the correct one:
Class pythonInterpreterClass = Class.forName("org.python.util.PythonInterpreter", true, myloader);
Object interp = pythonInterpreterClass.getConstructor(new Class[] {}).newInstance(new Object[] {});
The other solution only loads the class, but would not link it (according to the API documentation) and this is not what you want. So stick with this solution.
# 19
Thanks for the correction ejp,
I read through part of the JVM specification, have tested some cases and class loading is now more or less clear to me, but one thing I still cannot solve though is when an object is deserialized by someone else's code (like if you pass it as an argument to a session bean and the EJB container deserializes the argument) and you need to load the class definition from a .jar file. If you could help here, I would appreciate very much. See also http://forum.java.sun.com/thread.jspa?threadID=5163767
# 20
> The other solution only loads the class, but would
> not link it (according to the API documentation)
That's not 'according to the API documentation' at all. It doesn't say any such thing.
The real problem is that your other solution uses the system class loader, not your own, and if the system class loader can't find the class you're out of luck.
I've responded to your other question in the other thread, let's keep it there OK?
ejpa at 2007-7-21 19:54:09 >

# 21
Actually, he is using his own classloader. The problem I have is why use reflection when a simple interface works much easier and cleaner. If you do not use an interface, your goign to have ugly reflection everywhere in your program. Terribly messy and error prone.
Also, linking or not linking wont matter in this case because as soon as you try to use the class its going to get linked. So you can use either method. Only time to use the method that allows you to choose linking or not is if you are doing something special where you don't need the class to be linked yet. I have such a case where I want to just ensure all the class definitions are loaded (because they come from RMI remotely) but I don't need to use them just yet.
# 22
Agree with all that except that the 'other solution' I was referring to was Class.forName(name) which uses the system class loader, and the word is 'resolve' not 'link'.
ejpa at 2007-7-21 19:54:09 >

# 23
I am trying to do something very similar. In my code, I am subclassing URLClassLoader and expossing the addURL() method to the public. I have done some experimentation with reflections calling this method at runtime, and adding a URL has the effect of a dynamic ClassLoader. Unfortunately the "loophole" in Reflections that allows one to call a protected method doesn't work on the Mac, so I thought of switching to subclassing instead.
I have a class, EMFMain, that loads all the Jars in a directory, creates one of these DynamicURLClassLoaders, and sets the parent to the ClassLoader.getSystemClassLoader(). The code looks like this:
/**
* This is the main driver for the Enterprise Management Framework.
*/
package com.codechimp.emf;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.net.URL;
import com.codechimp.emf.DynamicURLClassLoader;
/**
* @author Michael Bauer
* @version 0.9a
*
*/
public class EMFMain {
private static File emfHome;
private static DynamicURLClassLoader dLoader = new DynamicURLClassLoader();
/**
* Main method. Loads the Spring Context XML file and starts all the
* services.
*
* @param args
*/
public static void main(String[] args) {
// Setup the EMF_HOME
String f = System.getenv("EMF_HOME");
if (f == null || f.equals("")) {
// EMF_HOME was not set, use current user.dir
f = System.getProperty("user.dir");
}
try {
emfHome = new File(f);
if (!emfHome.exists())
throw new Exception("EMF_HOME " + f + " did not exist.");
} catch (Exception e) {
System.err.println("Could not determine EMF Home: " + e);
System.exit(-1);
}
loadProperties();
loadLibraries();
try {
System.out.println("DynamicURLClassLoader:");
URL [] urls = dLoader.getURLs();
for(URL url : urls)
System.out.println("\t"+url);
EMFServer server = (EMFServer) dLoader.loadClass(
"com.codechimp.emf.EMFServer").newInstance();
Thread t = new Thread(server, "EMFServer");
t.start();
t.join();
} catch (Exception e) {
System.err
.println("An unrecoverable exception occured trying to start the EMFServerThread: "
+ e);
e.printStackTrace(System.err);
System.exit(-1);
}
}
/**
* Loads the properties tiles.
*
*/
private static void loadProperties() {
try {
File props = new File(emfHome.getAbsoluteFile()
+ System.getProperty("file.separator","/") + "conf");
FileFilter filter = new FileFilter() {
public boolean accept(File file) {
String p = file.getAbsolutePath();
return (p.toLowerCase().endsWith(".properties"));
}
};
File[] listing = props.listFiles(filter);
if (listing != null)
for (File f : listing)
System.getProperties().load(new FileInputStream(f));
} catch (Exception e) {
System.out.println("An error has occured loading the properties: "
+ e);
System.exit(-1);
}
}
/**
* Loads all the libraries in the EMF_HOME/libs directory
*
*/
private static void loadLibraries() {
try {
File libs = new File(emfHome.getAbsolutePath()
+ System.getProperty("file.separator","/") + "lib");
FileFilter filter = new FileFilter() {
public boolean accept(File file) {
String p = file.getAbsolutePath();
return (p.toLowerCase().endsWith(".jar") || p.toLowerCase()
.endsWith(".zip"));
}
};
File[] listing = libs.listFiles(filter);
for (File f : listing)
dLoader.addURL(f.toURL());
} catch (Exception e) {
System.err
.println("An error has occured trying to load the libraries: "
+ e);
System.exit(-1);
}
}
}
The EMFServer, which is in a separate JAR (I'll explain later), gets loaded along with all the other libraries. It uses the Spring Framework to do its bidding.
package com.codechimp.emf;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class EMFServer implements Runnable {
private static ApplicationContext appCtx;
private static boolean running = false;
public EMFServer() {
Object o = this.getClass().getClassLoader();
System.out.println("ClassLoader: "+o.getClass());
}
public void run() {
appCtx = new ClassPathXmlApplicationContext("componentContext.xml");
running = true;
while(running) {
// Do something...
}
}
}
Here is the error I am geting:
DynamicURLClassLoader:
file:/Applications/EMF/lib/emf_event.jar
file:/Applications/EMF/lib/emf_server.jar
file:/Applications/EMF/lib/spring-2.0.2.jar
Exception in thread "main" java.lang.NoClassDefFoundError: org/springframework/context/ApplicationContext
at java.lang.Class.getDeclaredConstructors0(Native Method)
at java.lang.Class.privateGetDeclaredConstructors(Class.java:2328)
at java.lang.Class.getConstructor0(Class.java:2640)
at java.lang.Class.newInstance0(Class.java:321)
at java.lang.Class.newInstance(Class.java:303)
at com.codechimp.emf.EMFMain.main(EMFMain.java:57)
My understanding is this. Since the EMFServer class is loaded by the DynamicURLClassLoader, his internally referenced ClassLoader is pointed to that one. The EMFMain's, being the class started by the JVM, is pointed to the one rerturned by ClassLoader.getSystemClassLoader(), which cannot be changed easily. I know, from much experience with JBoss, that ClassLoaders can look UP but not DOWN (I.E., Classes known by the parent can be seen by the child, but not visa versa).
It seems the error I am seeing is a result of me defining the ApplicationContext as an Class member variable. But, I am a little confussed. Is it the job of the EMFMain's classloader to resolve this class even thought I am clearly using the instance of DynamicURLClassLoader to load the EMFServer class? I was under the impression that since EMFServer was loaded by the DynamicURLClassLoader, his internal ClassLoader would point to it, and any further attempts to reconcile a Class would first go there. Is there a better, more appropriate way to do this?
# 24
Forgot to mention this...here is the real interesting part. If I change the code in EMFServer to look like this:
package com.codechimp.emf;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class EMFServer implements Runnable {
//private static ApplicationContext appCtx;
private static boolean running = false;
public EMFServer() {
Object o = this.getClass().getClassLoader();
System.out.println("ClassLoader: "+o.getClass());
}
public void run() {
ApplicationContext appCtx = new ClassPathXmlApplicationContext("componentContext.xml");
running = true;
while(running) {
// Do something...
}
}
}
The error moves some and looks like:
DynamicURLClassLoader:
file:/Applications/EMF/lib/emf_event.jar
file:/Applications/EMF/lib/emf_server.jar
file:/Applications/EMF/lib/spring-2.0.2.jar
ClassLoader: class sun.misc.Launcher$AppClassLoader
Exception in thread "EMFServer" java.lang.NoClassDefFoundError: org/springframework/context/support/ClassPathXmlApplicationContext
at com.codechimp.emf.EMFServer.run(EMFServer.java:17)
at java.lang.Thread.run(Thread.java:613)
This is interesting as I am not sure why the ClassLoader printed out is sun.misc.Launcher$AppClassLoader. This seems to go against what I read in the JavaDoc for ClassLoader.
# 25
> I am trying to do something very similar. In my
> code, I am subclassing URLClassLoader and expossing
> the addURL() method to the public. I have done some
> experimentation with reflections calling this method
> at runtime, and adding a URL has the effect of a
> dynamic ClassLoader. Unfortunately the "loophole" in
> Reflections that allows one to call a protected
> method doesn't work on the Mac, so I thought of
> switching to subclassing instead.
>
> I have a class, EMFMain, that loads all the Jars in a
> directory, creates one of these
> DynamicURLClassLoaders, and sets the parent to the
> ClassLoader.getSystemClassLoader(). The code looks
> like this:
I have not diagnosed your problem directly but I can start you out with a tip. I know lots of folks do this, but I do not like using getSystemClassLoader(). It is not very portable for code that may also use its own classloaders.
For instance, if you have code that has loaded a class by a URLClassloader, then within that class you have your code. In that case, your code will be skipping the URLClassloader and goign straight to the System Classloader, thus potentially missing some key classes.
Instead why don't you try
getClass().getClassLoader();
# 26
> The error moves some and looks like:
> DynamicURLClassLoader:
> file:/Applications/EMF/lib/emf_event.jar
> file:/Applications/EMF/lib/emf_server.jar
> file:/Applications/EMF/lib/spring-2.0.2.jar
> ClassLoader: class sun.misc.Launcher$AppClassLoader
> Exception in thread "EMFServer"
> java.lang.NoClassDefFoundError:
> org/springframework/context/support/ClassPathXmlApplic
> ationContext
> at
> t com.codechimp.emf.EMFServer.run(EMFServer.java:17)
> at java.lang.Thread.run(Thread.java:613)
>
>
> This is interesting as I am not sure why the
> ClassLoader printed out is
> sun.misc.Launcher$AppClassLoader. This seems to go
> against what I read in the JavaDoc for ClassLoader.
This is a NoClassDefFoundError, not a ClassNotFoundException. NCDFE is an indication that your runtime classpath does not, as a minimum, have all the classes of your compile time classpath. This has nothing to do with your dynamic classloading scheme. (unless you have a misunderstanding)
# 27
> > The other solution only loads the class, but would
> > not link it (according to the API documentation)
>
> That's not 'according to the API documentation' at
> all. It doesn't say any such thing.
The API and some other back ground information combined make come to this conclusion.
According to the JVM specification, classloading occurs in 3 steps:
1. loading, which consists of finding and loading the binary data of a type in memory
2. linking, which consists of processing the binary data of a class definition in memory
3. initialization, which is the execution of the static initialisation of a class when it is first referenced to.
In the class ClassLoader:
The method loadClass(String name) is equivalent to the method loadClass(name, false), which only loads the class and does not link it. The method resolveClass(Class<?> c) links the specified class (and has a misleading name).
The method Class.forName(String name, boolean initialize, ClassLoader loader) loads the class and links it, using the given class loader. Whether the class is initialized depends on the boolean value.
A reply to _dnoyeB: yes reflection can clutter things up, but in case the guy would have to use a lot of reflection calls, he can create proxies (java.lang.reflect.Proxy). The object loaded with the custom class loader is wrapped in an InvocationHandler. The InvocationHandler and a suitable Interface are used to create the proxy. The proxy is cast to the interface and the InvocationHandler takes care of the reflection (behind the scenes).
# 28
I don't see what reflection gains you except complexity.
# 29
> This is a NoClassDefFoundError, not a
> ClassNotFoundException. NCDFE is an indication that
> your runtime classpath does not, as a minimum, have
> all the classes of your compile time classpath. This
> has nothing to do with your dynamic classloading
> scheme. (unless you have a misunderstanding)
Thanks for the reply. I understand what NCDFE means, but I am more confussed about the class printout and the Class refered too via the error.
As you can see from the output, EMFServer is loaded by the DynamicURLClassLoader, which is a subclass of URLClassLoader. My understanding of ClassLoader is that a class has a reference to the ClassLoader that actually loaded him. Since I loaded the JAR in the DynamicURLClassLoader, I expected it to reference him.
In addition, the class its complaining about is org.springframework.context.support.ClassPathXmlApplicationContext, which is in the spring-2.0.2.jar. That JAR is being loaded by the DynamicURLClassLoader, so should be available in the same ClassLoader that loaded EMFServer. But, this doesn't seem to be the case, since it tosses the exception.
I am wondering if my whole approach is flawed. Perhaps I should write my own ClassLoader from the ground-up instead of subclassing. I know there are other applications that do this sort of thing, JBoss and Squrrel come to mind first, so I know its do-able. I just haven't found any "best practices" when it comes to loading JARs dynamically at Run-Time.
# 30
Your thinking is correct. you just made a few rookie mistakes.
> > This is a NoClassDefFoundError, not a
> > ClassNotFoundException. NCDFE is an indication
> that
> > your runtime classpath does not, as a minimum,
> have
> > all the classes of your compile time classpath.
> This
> has nothing to do with your dynamic classloading
> scheme. (unless you have a misunderstanding)
>
> Thanks for the reply. I understand what NCDFE means,
> but I am more confussed about the class printout and
> the Class refered too via the error.
>
Computers don't lie. I don't think. You should be scratching your head as to how you could possibly get a NCDFE right?
> As you can see from the output, EMFServer is loaded
> by the DynamicURLClassLoader, which is a subclass of
> URLClassLoader. My understanding of ClassLoader is
> that a class has a reference to the ClassLoader that
> actually loaded him. Since I loaded the JAR in the
> DynamicURLClassLoader, I expected it to reference
> him.
No, in fact the output says EMFServer is loaded by
"ClassLoader: class sun.misc.Launcher$AppClassLoader"
That should clue you in to the problem.
Your class EMFMain contains a direct reference to the class EMFServer. Therefore, the classloader of EMFMain will be the classloader of EMFServer. That is of course the AppClassLoader. That is not what you want.
Try changing this
EMFServer server = (EMFServer) dLoader.loadClass("com.codechimp.emf.EMFServer").newInstance();
To this
Runnable server = (Runnable) dLoader.loadClass("com.codechimp.emf.EMFServer").newInstance();
For dynamic classloading to work, the class can not be present at compile time.
# 31
First, let me appologize if I came out sounding negative...I am just frustrated, and did not mean to direct that at anyone.
That said, I tried altering the code as you suggested:
/**
* This is the main driver for the Enterprise Management Framework.
*/
package com.codechimp.emf;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.net.URL;
import com.codechimp.emf.DynamicURLClassLoader;
/**
* @author Michael Bauer
* @version 0.9a
*
*/
public class EMFMain {
private static File emfHome;
private static DynamicURLClassLoader dLoader = new DynamicURLClassLoader();
/**
* Main method. Loads the Spring Context XML file and starts all the
* services.
*
* @param args
*/
public static void main(String[] args) {
// Setup the EMF_HOME
String f = System.getenv("EMF_HOME");
if (f == null || f.equals("")) {
// EMF_HOME was not set, use current user.dir
f = System.getProperty("user.dir");
}
try {
emfHome = new File(f);
if (!emfHome.exists())
throw new Exception("EMF_HOME " + f + " did not exist.");
} catch (Exception e) {
System.err.println("Could not determine EMF Home: " + e);
System.exit(-1);
}
loadProperties();
loadLibraries();
try {
System.out.println("DynamicURLClassLoader:");
URL [] urls = dLoader.getURLs();
for(URL url : urls)
System.out.println("\t"+url);
Runnable server = (Runnable) dLoader.loadClass("com.codechimp.emf.EMFServer").newInstance();
Thread t = new Thread(server, "EMFServer");
t.start();
t.join();
} catch (Exception e) {
System.err
.println("An unrecoverable exception occured trying to start the EMFServerThread: "
+ e);
e.printStackTrace(System.err);
System.exit(-1);
}
}
/**
* Loads the properties tiles.
*
*/
private static void loadProperties() {
try {
File props = new File(emfHome.getAbsoluteFile()
+ System.getProperty("file.separator","/") + "conf");
FileFilter filter = new FileFilter() {
public boolean accept(File file) {
String p = file.getAbsolutePath();
return (p.toLowerCase().endsWith(".properties"));
}
};
File[] listing = props.listFiles(filter);
if (listing != null)
for (File f : listing)
System.getProperties().load(new FileInputStream(f));
} catch (Exception e) {
System.out.println("An error has occured loading the properties: "
+ e);
System.exit(-1);
}
}
/**
* Loads all the libraries in the EMF_HOME/libs directory
*
*/
private static void loadLibraries() {
try {
File libs = new File(emfHome.getAbsolutePath()
+ System.getProperty("file.separator","/") + "lib");
FileFilter filter = new FileFilter() {
public boolean accept(File file) {
String p = file.getAbsolutePath();
return (p.toLowerCase().endsWith(".jar") || p.toLowerCase()
.endsWith(".zip"));
}
};
File[] listing = libs.listFiles(filter);
for (File f : listing)
dLoader.addURL(f.toURL());
} catch (Exception e) {
System.err
.println("An error has occured trying to load the libraries: "
+ e);
System.exit(-1);
}
}
}
But I am still getting the same NCDFE. Also, the output still suggests that the EMFServer class is being loaded by class sun.misc.Launcher$AppClassLoader. This is my first dive into wrtting a ClassLoader of any sorts, so I am sure I am making more than one rookie mistake.
# 32
This is not 100% required but it will help you to find the error. Setup your classpath such that if you put EMFServer in EMFMain again, the compiler will complain about not being able to find the class.
Either way, please always post the exception and do not merely say its the same. I have yet to see the code for DynamicURLClassLoader, but you really don't need a custom classloader as URLClassLoader will do the job just fine. At this point I am guessing that EMFServer is referenced inside DYnamicURLClassLoader class.
# 33
Well, I found the problem. When you mentioned I hadn't posted the DynamicURLClassLoader, it made me take a second look. Well, the default contructor was called the constructor URLClassLoader(new URL[0],ClassLoader.getSystemClassLoader()) in my feable attempt to force the parent loader. As was mentioned early on, this is not a good idea. As soon as I took that out, it started working just fine.
The new class looks like:
package com.codechimp.emf;
import java.io.File;
import java.io.FileFilter;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLStreamHandlerFactory;
import java.util.ArrayList;
import java.util.HashMap;
public class DynamicURLClassLoader extends URLClassLoader {
public DynamicURLClassLoader() {
super(new URL[0]);
}
public DynamicURLClassLoader(URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory) {
super(urls, parent, factory);
}
public DynamicURLClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}
public DynamicURLClassLoader(URL[] urls) {
super(urls);
}
@Override
public void addURL(URL arg0) {
super.addURL(arg0);
}
}
The reason I did this instead of just using the URLClassLoader is that on my Mac the code I usually use doesn't work. Usually, I do something like:
URLClassLoader loader = ClassLoader.getSystemClassLoader();
Method m = URLClassLoader.getDeclaredMethod("addURL",new Class{URL.getClass()});
m.invoke(loader,new Object{<some file url>});
Strangely enough, this technique works on Windows to get around private and protected methods. Reflections apparently doesn't check the method's visibility, it just runs it. On the Mac, however, this causes an InvocationException stating the method is protected. Seems the JVM on the Mac is a little more strict in that sense.
# 34
Glad its working.
>
> The reason I did this instead of just using the
> URLClassLoader is that on my Mac the code I usually
> use doesn't work. Usually, I do something like:
> [code]
> URLClassLoader loader =
> ClassLoader.getSystemClassLoader();
> Method m =
> URLClassLoader.getDeclaredMethod("addURL",new
> Class{URL.getClass()});
>m.invoke(loader,new Object{<some file url>});
> ode]
>
> Strangely enough, this technique works on Windows to
> get around private and protected methods.
> Reflections apparently doesn't check the method's
> visibility, it just runs it. On the Mac, however,
> this causes an InvocationException stating the
> method is protected. Seems the JVM on the Mac is a
> little more strict in that sense.
Ahh I see. I like the subclass methodology. Much more portable and proper. The MAC issue is probably just different default permissions.
You can't simply build the array of URLs before you create the URLClassLoader?