How do I correctly load resources from a network directory in the java classpath?
Our java application relies on some resources which are available on a network share. This network share is located on the classpath, and the resources are read at runtime using MyClass.class.getResourceAsStream("/myfile.jpg")
.
java -Djava.class.path=\\myserver\myshare:C:\myjar.jar MainClass
When the share is available at startup, everything runs smoothly. Image and properties files which are located in the share can be read using getResourceAsStream()
. However, if the share is not online when the application starts, even if the share comes online before any resources are read, they cannot be read using getResourceAsStream()
.
Doing some digging using eclispse + decompiler, I noticed one difference. The default classloader inherits from URLClassLoader, and its ucp
member (URLClassPath
) contains a list of URLClassPath.Loader
instances. In the first scenario, it contains a URLClassPath.FileLoader
and a URLClassPath.JarLoader
. In the second scenario, it only contains a jar loader.
It's like java determines that the classpath entry is invalid and completely discards it.
Why is this? How can I avoid it?
Update I am unable to change the mechanism by which we are loading resources because of a few reasons:
- There are far too many areas which currently load files this way for me change at the moment
- There are situations where by the resource is actually being loaded by a third party component
I have no problem creating a custom class loader, I just need some guidance on how to do it.
I tried with this, but was unable to get expected results:
import java.net.URL;
import java.net.URLClassLoader;
public class MyUrlClassLoader extends URLClassLoader {
public MyUrlClassLoader(ClassLoader parent) {
super(new URL[0], parent);
System.out.println("MyUrlClassLoader ctor");
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
System.out.println("url find class " + name);
return super.findClass(name);
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
System.out.println("url load class " + name);
return super.loadClass(name);
}
@Override
public URL getResource(String name) {
System.out.println("url get resource " + name);
return super.getResource(name);
}
}
import java.net.URL;
public class ClassLoaderMain {
public static void main(String[] args) throws ClassNotFoundException {
URL url = ClassLoaderMain.class.getResource("/myfile.txt");
System.out.print("Loaded? ");
System.out.println(url != null);
System.out.println(ClassLoaderMain.class.getClassLoader().toString());
System.out.println(MyUrlClassLoader.class.getClassLoader().toString());
System.out.println(FakeClass.class.getClassLoader().toString());
}
}
When开发者_StackOverflow I run java -cp . -Djava.system.class.loader=MyUrlClassLoader ClassLoaderMain
This outputs:
MyUrlClassLoader ctor
url load class java.lang.System
url load class java.nio.charset.Charset
url load class java.lang.String
url load class ClassLoaderMain
Loaded? true
sun.misc.Launcher$AppClassLoader@923e30
sun.misc.Launcher$AppClassLoader@923e30
sun.misc.Launcher$AppClassLoader@923e30
So my class loader is being created, and load class is being called, but it doesn't appear to be the class loader for the classes it is loading?
I ended up resolving this by creating my own ClassLoader
, deriving from URLClassLoader.
import java.io.File;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
public class CustomClassLoader extends URLClassLoader {
public CustomClassLoader(ClassLoader parent) {
// System classloader will filter inaccessible URLs.
// Force null parent to avoid using system classloader.
super(createURLReferences(), null);
}
/**
* Build an array of URLs based on the java.class.path property.
* @return An array of urls to search for classes.
*/
private static URL[] createURLReferences() {
String classpath = System.getProperty("java.class.path");
String[] classpathEntries = classpath.split(System.getProperty("path.separator"));
List<URL> urls = new ArrayList<URL>();
for (String classpathEntry : classpathEntries) {
File classpathFile = new File(classpathEntry);
URI uri = classpathFile.toURI();
try {
URL url = uri.toURL();
urls.add(url);
} catch (MalformedURLException e) {
System.out.println("Ignoring classpath entry: " + classpathEntry);
}
}
return urls.toArray(new URL[urls.size()]);
}
}
精彩评论