How to avoid resource conflicts in library jars?

I'm worried about the situation where libraries Foo

and Bar

each expose a resource in the classpath with the same name, like properties.txt

in this example.

Assuming Maven is configured and jars

deployed with Maven, if I have this:

Library Foo:

$ cat Foo/src/main/resources/properties.txt
$ Foo

      

and the library panel:

$ cat Bar/src/main/resources/properties.txt
$ Bar

      

And App

, which depends on them, whose pom

looks something like this: in a nutshell it just says "build jar-with-dependencies and depends on Foo

and Bar

:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-assembly-plugin</artifactId>
  <executions>
    <execution>
      <id>bundle-project-sources</id>
      <phase>package</phase>
      <goals>
        <goal>single</goal>
      </goals>
      <configuration>
        <archive>
          <manifest>
            <mainClass>me.unroll.deptest.App</mainClass>
            <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
          </manifest>
          <manifestEntries>
            <Implementation-Build>${buildNumber}</Implementation-Build>
          </manifestEntries>
        </archive>
        <descriptorRefs>
          <descriptorRef>jar-with-dependencies</descriptorRef>
        </descriptorRefs>
      </configuration>
    </execution>
  </executions>
</plugin>

      

The problem is that the file has properties.txt

been tailored. Try a jar tf

:

unrollme-dev-dan:target Dan$ jar tf App-1.0-SNAPSHOT-jar-with-dependencies.jar 
META-INF/
META-INF/MANIFEST.MF
properties.txt
META-INF/maven/
META-INF/maven/me.unroll.deptest/
META-INF/maven/me.unroll.deptest/Bar/
META-INF/maven/me.unroll.deptest/Bar/pom.xml
META-INF/maven/me.unroll.deptest/Bar/pom.properties
META-INF/maven/me.unroll.deptest/Foo/
META-INF/maven/me.unroll.deptest/Foo/pom.xml
META-INF/maven/me.unroll.deptest/Foo/pom.properties
me/
me/unroll/
me/unroll/deptest/
me/unroll/deptest/App.class

      

So, I ran a class main

in App

that does:

try (InputStream is = App.class.getClassLoader().getResourceAsStream("properties.txt")) {

    java.util.Scanner s = new java.util.Scanner(is);
        System.out.println("Scanner: " + s.next());

}

      

And the result:

unrollme-dev-dan:target Dan$ java -jar App-1.0-SNAPSHOT-jar-with-dependencies.jar 
Scanner: Bar

      

Oops, Bar won. Warnings and errors in mvn package

-ing App

. No warnings or errors on startup that the wrong file may have been selected, in fact it was failing.

So I want to ask the correct practice to avoid this. First, something like this should fail loudly, not softly. Second, the only solution I can think of is that all resource files should be packaged correctly, like everything else in Java development, that is, the library should never be exposed properties.txt

in the "global" namespace; it should appear in the type folder me/unroll/deptest/foo

just like everything else. I'm skeptical that I haven't seen an example of Maven that actually does this. So what's the best thing here?

+5


source to share


2 answers


And what do you do in Java to avoid collisions between libraries? Packages! This is an established and well understood approach. Packages also work with resources:

com/example/foo/Foo.class
com/example/foo/properties.txt

      

and the second library:

com/example/bar/Bar.class
com/example/bar/properties.txt

      

Note what properties.txt

lies in different packages and therefore in the final JAR directories. In fact, this approach is preferred as the API for getting such resources becomes simpler:

App.class.getResourceAsStream("properties.txt"))

      

against.



Bar.class.getResourceAsStream("properties.txt"))

      

It just works because it Class.getResourceAsStream()

is local to the base class package by default. Of course, when you are inside an instance method Foo

, either or Bar

, you just say getClass().getResourceAsStream("properties.txt")

. Also, you can easily link to both files, just like you link to classes:

getClass().getResourceAsStream("/com/example/foo/properties.txt");
getClass().getResourceAsStream("/com/example/bar/properties.txt");

      


I'm skeptical because I haven't seen any Maven example that actually does this.

Real world example: You have a Spring integration test named com.example.foo.FooTest

. Default Spring expects the context of the file will be located under: /src/test/resources/com/example/foo/FooTest-context.xml

.

+3


source


To complete @Tomasz answer with maven code, do this:



<build>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <targetPath>com/example/bar/</targetPath>
        </resource>
    </resources>
</build>

      

0


source







All Articles