vect
vect

Reputation: 665

How to load one of same named resources from different jars in a classpath?

Let's say there is a jar main.jar which depends on two other jars - dep1.jar and dep2.jar. Both dependencies are in a classpath in MANIFEST.MF of main.jar. Each of dependency jars has a directory foo inside with a file bar.txt within:

dep1.jar
|
\--foo/
   |
   \--bar.txt

dep2.jar
|
\--foo/
   |
   \--bar.txt

Here is a main class of main.jar:

public class App
{
    public static void main( String[] args ) {
        ApplicationContext ctx = new StaticApplicationContext();
        Resource barResource = ctx.getResource("classpath:foo/bar.txt");
    }
}

Which of two bar.txt files will be loaded? Is there a way to specify in a resource URL a jar the file should be loaded from?

Upvotes: 6

Views: 3219

Answers (5)

Gary Gauh
Gary Gauh

Reputation: 5125

As @Sotirios said, you can get all resources with the same name using ctx.getResources(...), code such as :

    ApplicationContext ctx = new StaticApplicationContext();
    Resource[] resources = ctx.getResources("classpath*:/foo/bar.txt");

    for (Resource resource : resources) {

        System.out.println("resource file: " + resource.getURL());

        InputStream is = new FileInputStream(resource.getFile());

        if (is == null) {
            System.out.println("resource is null");
            System.exit(-1);
        }

        Scanner scanner = new Scanner(is);

        while(scanner.hasNextLine()) {
            System.out.println(scanner.nextLine());
        }
    }

Upvotes: 0

Sotirios Delimanolis
Sotirios Delimanolis

Reputation: 279940

Which one you get is undefined. However, you can use

Resource[] barResource = ctx.getResources("classpath*:foo/bar.txt");

to get them both (all). The URL in the Resource will tell you which jar they are in (though I don't recommend you start programming based on that information).

Upvotes: 2

Joop Eggen
Joop Eggen

Reputation: 109547

The specification says that the first class/resource on the class path is taken (AFAIK).

However I would try:

Dep1Class.class.getResource("/foo/bar.txt");
Dep2Class.class.getResource("/foo/bar.txt");

As Class.getResource works cannot take resources from another jar, as opposed to the system class loader.

With a bit of luck, you will not need to play with ClassLoaders and hava a different class loader load dep2.jar.

Upvotes: 0

PaulNUK
PaulNUK

Reputation: 5209

You wouldn't expect a compile time exception as resource loading is a run time process.

You can't specify which jar the resource will come from in code, and this is a common issue, particularly when someone bundles something like log4j.properties into a jar file.

What you can do is specify the order of jars in your classpath, and it will pick up the resource from the first one in the list. This is tricky in itself as when you are using something like ivy or maven for classpath dependencies, you are not in control of the ordering in the classpath (in the eclipse plugins at any rate).

The only reliable solution is to call the resources something different, or put them in separate packages.

Upvotes: 2

Ben Potter
Ben Potter

Reputation: 321

Flip a quarter, that's the one you'll get. Most likely, it will be the one highest alphabetically, so in your case the one inside dep1.jar. The files both have identical classpaths (foo.Bar), and while this should look to throw a compile time exception, it will not because it will just package both jars up and not try to compile/look at the (this specific file) file as it is a .txt file.

Upvotes: 1

Related Questions