51

In my Maven project there is one module (core) that has a few resources for its classes. When running classes inside the module its able to get its own resources. Everything works fine.

Where stuff breaks is when another module which depends on the core tries to run that class. The folder that Java is looking for resources in is this module, not the core module. So the class fails.

In short: How can I access the resources of a dependency?


I've experimented with trying to do this by declaring in Core's JAR Manifest Class-Path: .. However when listing the resources available with JSHookLoader.class.getClassLoader().getResources(""); (JSHookLoader is in Core if it means anything), I get:

Resource: W:\programming\quackbot-hg\impl\target\classes
File rebel.xml

Resource: W:\programming\maven-repo\org\quackbot\core\3.5-SNAPSHOT
File core-3.5-SNAPSHOT.jar
File core-3.5-SNAPSHOT.pom
File maven-metadata-local.xml
File _maven.repositories

This of course complicates things as I expected the JAR itself to be in the Classpath, not the directory the JAR is in

Any suggestions?


Coming back to this project I still have this issue. Other guides have talked about using maven-assembly-plugin and the remote resources plugin, but thats a lot of pain as all modules have to include the monster plugin XML.

Why don't I simplify the question to this: How can I add a dependencies JAR to the resource list?

  1. core.jar has some resources in it under the folder /resources. Running core.jar I can see /resources in the resource list.
  2. impl.jar depends on core.jar. Upon running it though /resources isn't in the resource list and therefore causes havoc.

This should be simple enough, but how can I do it? I've spend hours trying to figure out a simple clean way to do it but to no avail.

TheLQ
  • 14,830
  • 14
  • 69
  • 107

5 Answers5

48

After lots of searching I finally stumbled upon a solution.

My solution takes the core module and unpacks it with the Maven Dependency Plugin, making sure to exclude the META-INF folder and the org folder which is where the compiled classes are. The reason I'm excluding what I don't want instead of explicitly stating what I do want is so new resources can be added easily without me having to update my POM all the time.

The reason I'm not using the assembly plugin or the remote resources plugin is because they use dedicated resource modules. I don't think I should have to make a core module and a core-resources module, with the core-resources module only containing logback and hibernate configuration files + some other stuff.

Here is a copy of what I'm using to do this

    <build>
        <plugins>
            <!--Extract core's resources-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.2</version>
                <executions>
                    <execution>
                        <id>unpack</id>
                        <phase>generate-resources</phase>
                        <goals>
                            <goal>unpack-dependencies</goal>
                        </goals>
                        <configuration>
                            <includeGroupIds>${project.groupId}</includeGroupIds>
                            <includeArtifactIds>core</includeArtifactIds>
                            <excludeTransitive>true</excludeTransitive>
                            <overWrite>true</overWrite>
                            <outputDirectory>${project.build.directory}/core-resources</outputDirectory>
                            <excludes>org/**,META-INF/**,rebel.xml</excludes>
                            <overWriteReleases>true</overWriteReleases>
                            <overWriteSnapshots>true</overWriteSnapshots>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
        <!--New resource locations-->
        <resources>
            <resource>
                <filtering>false</filtering>
                <directory>${project.build.directory}/core-resources</directory>
            </resource>
            <resource>
                <filtering>false</filtering>
                <directory>${basedir}/src/main/resources</directory>
            </resource>
        </resources>
    </build> 
TheLQ
  • 14,830
  • 14
  • 69
  • 107
  • 1
    Thanks so much for posting your solution! I am having this exact problem and have found an answer ... but it was written in maven terms, and I was struggling to decipher it. – CaffiendFrog Jan 28 '14 at 23:06
  • 1
    Beware of circular dependencies when using this. You may have difficulties releasing. – Michael Dec 04 '17 at 17:47
  • good method, `true` this attribute seems cannot be used now. just remote it. – Xin Meng Jun 13 '18 at 10:00
  • Perfect solution. phase : generate-resource is important. If we specify any other phase like "package" then the unpacked resources will not be included in the fat jar. – tom Feb 14 '19 at 05:31
  • `${project.build.outputDirectory}` can be necessary in some cases. – Raedwald Aug 11 '20 at 13:52
8

If the resources are located in /src/main/resources and classes in the JAR are accessing them using getResourceAsStream(), you should be able to access these files from classes in other JARs as well (also using getResourceAsStream()). This is because all the files located in /src/main/resources eventually land in the same logical directory (CLASSPATH). In other words: all the files located in /src/main/resources and all the classes compiled from /src/main/java are so to say merged into a single namespace.

If you still can't access files even though all the classes are using uniform getResourceAsStream() API, you might have some class-loading visibility issues.

Tomasz Nurkiewicz
  • 334,321
  • 69
  • 703
  • 674
  • 2
    See the update in my question, its not working as you said it would. – TheLQ Mar 13 '11 at 23:11
  • It appears this is not the case for OP, but in general do not use the same folder names for the resources. E.g. when project `A` has the resource `myfolder/a.txt` and project `B` has `myfolder/b.txt`, and B is a dependency of A, then A will not see `myfolder/b.txt`. – Marcono1234 Apr 27 '19 at 16:17
2

I had a similar situation just now. I have a "test code builder" that takes data from the src/test/resources directory and pumps data into a H2 database. Works fine from module A, but when called from module B it didn't. It constructs a path with multiple semi-colons (due to resources being in the jar file); moduleA then fails to find files "local to itself".

In the end, I added

    <testResources>
        <testResource>
            <directory>src/test/resources</directory>
        </testResource>
        <testResource>
            <directory>../moduleA/src/test/resources</directory>
        </testResource>
    </testResources>

to moduleB's build section, and now the build runs through fine and as expected.

Gwaptiva
  • 351
  • 3
  • 11
2

If your dependent JAR has a class named OneDependent, then this should work:

OneDependent.class.getResourceAsStream(resourcePath)
Sasha O
  • 3,710
  • 2
  • 35
  • 45
  • 1
    That's what I did. It only showed the directory the Core JAR was in, not the contents of it. – TheLQ Mar 15 '11 at 13:27
  • Trying to understand what directory are you talking about...the getResourceAsStream() call either gives you a resource or null if it cannot find resource. What does it return in your case? – Sasha O Mar 16 '11 at 06:03
  • 3
    I looped over what was returned with `JSHookLoader.class.getClassLoader().getResources("");` – TheLQ Mar 16 '11 at 20:30
-1

I do not think it is a good idea. Suppose you have module A. It depends from 'B'. 'B' depends from C. Basically, you should care in A only about interface of B module. Perhaps, tomorrow it change dependence from C to D. If it would happen you will need change A. It is not good situation, is it? So declare dependence A from C in implicit way.

In short: How can I access the resources of a dependency?

In short: declare this dependence directly.

Stan Kurilin
  • 15,614
  • 21
  • 81
  • 132