Question

I have troubles to make loading resources from other jars running. Here is the setup I have

resource.jar  # contains resources I want to load
`-res/hwview/file1

engine.jar    # my application which need resources
`-res/hwview/file2

Interesting thing is that using the code below I'm able to load file2 (which is in the jar I run) but not the file1.

String dir = "res/hwview";
Enumeration<URL> e = getClass().getClassLoader().getResources(dir);
while(e.hasMoreElements()) {
    // prints only file1 from engine.jar 
    // (actually it's in classes directory because I run it from my IDE)
    System.out.println(e.nextElement());
}

[OUTPUT]
/path/to/my/project/SiHwViewUiModel/classes/res/hwview

So I thought maybe the jar was not picked up by the ClassLoader so I printed what was loaded

ClassLoader cl = ClassLoader.getSystemClassLoader();
URL[] urls = ((URLClassLoader)cl).getURLs();
for(URL url: urls){
    System.out.println(url.getFile());
}

[OUTPUT]
/path/to/my/project/SiHwViewUiModel/classes/
/path/to/my/project/Resources/deploy/resources.jar
... and other not so important jars

Any ideas? Thanks for any help!

Was it helpful?

Solution

I found the solution. The problem with getResources() method and similar is that thay cannot be given a directory but only a particular file. This means that if I want to search in the whole classpath for a particular structure I need to create marker file in base directories.

Example: I want to get to my/path directory -> create marker.info (name does not matter) file and then search for it.

resources.jar
`- my/path/
   |- my/directories
   `- marker.info

resources2.jar
`- my/path/
   |- my/other/directories
   `- marker.info

# search
Enumeration<URL> urls = getClass().getClassLoader().getResources("my/path/marker.info"); 

# print
print(urls);
/path/to/resources.jar!/my/path/marker.info
/path/to/resources2.jar!/my/path/marker.info

OTHER TIPS

If the JAR files are on the classpath, you don't need to do anything special. The resources will be found.

If they aren't on the classpath, you need to create a URLClassLoader and use its getResource() method.

In Spring, it can load xml file from all the jar files in the classpath:

ApplicationContext context = new ClassPathXmlApplicationContext(
        "classpath*:**/applicationContext*.xml");

You can check the Spring source to see how Spring achieve that.

public final class JarResource
{
private String jarFileName;
private Map<String, Long> hashSizes = new HashMap<String, Long>();
private Map<String, Object> hashJarContents = new HashMap<String, Object>();

public JarResource(String jarFileName) throws Exception
{
    this.jarFileName = jarFileName;
    ZipFile zipFile = new ZipFile(this.jarFileName);

    Enumeration<ZipEntry> e = (Enumeration<ZipEntry>) zipFile.entries();
    while (e.hasMoreElements())
    {
        ZipEntry zipEntry = e.nextElement();
        if(!zipEntry.isDirectory())
        {
            hashSizes.put(getSimpleName(zipEntry.getName()), zipEntry.getSize());
        }
    }
    zipFile.close();

    // extract resources and put them into the hashMap.
    FileInputStream fis = new FileInputStream(jarFileName);
    BufferedInputStream bis = new BufferedInputStream(fis);
    ZipInputStream zis = new ZipInputStream(bis);
    ZipEntry ze = null;

    while ((ze = zis.getNextEntry()) != null)
    {
        if (ze.isDirectory())
        {
            continue;
        }
        else
        {
            long size = (int) ze.getSize();
            // -1 means unknown size.
            if (size == -1)
            {
                size = hashSizes.get(ze.getName());
            }

            byte[] b = new byte[(int) size];
            int rb = 0;
            int chunk = 0;
            while (((int) size - rb) > 0)
            {
                chunk = zis.read(b, rb, (int) size - rb);
                if (chunk == -1)
                {
                    break;
                }
                rb += chunk;
            }

            hashJarContents.put(ze.getName(), b);
        }
    }
    zis.close();
}

public byte[] getResource(String name)
{
    return (byte[]) hashJarContents.get(name);
}

private String getSimpleName(String entryName)
{
    // Remove ".jar" extension
    int index = entryName.indexOf("/");
    String fileNameWithoutExt = entryName.substring(index, entryName.length());

    return fileNameWithoutExt;
}
}

Then use this class to load your resource:

public static void main(String[] args) throws Exception
{
    JarResource jr = new JarResource("/home/mjiang/Downloads/solr-4.8.0/dist/solr-cell-4.8.0-test.jar");
    byte[] resource = jr.getResource("/META-INF/NOTICE.txt");

    InputStream input = new ByteInputStream(resource, resource.length);

    BufferedReader dis = new BufferedReader(new InputStreamReader(input));

    String line = "";
    while((line = dis.readLine()) != null)
    {
        System.out.println(line);
    }

    dis.close();
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top