Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Maven and Eclipse : loading default properties in maven library project and use it in runnable Jar

I believe this problem has been asked before on stackoverflow. And I'd like to mention that I tried the solutions that came with the questions related to mine. The one that came the closest to my problem was: Load properties file in JAR?.
Sadly the solution described there didn't work for me. And due the age of the question I thought asking it again is the way to go.

Heading on to describing my problem.
So currently I'm working on a library project which has been setup with maven and creates an extension for the current Spring AMQP project.
The goal here is to supply a JAR file which can be included into another project to support a specific way of communicating over a message broker.
At this point I'm implementing the configuration option to allow users to configure the messaging client to their liking. But while i was testing the functionality of this feature I hit a problem while using the library in an executable JAR.

While running it in the Eclipse workspace everything seems to work just fine. But when I try to run it from my desktop (as a runnable JAR) the properties file does not seem to be found anywhere.

Just to give a quick overview of the workspace/projects setup as described above: workspace overview

The project structure of both project reflects the Maven default one:

- src/main/java
    - java source files
- src/main/resources
    - resource files
- src/test/java
    - java test files
- src/test/resources
    - test resource files

Where the library file contains a default.properties file in the src/main/resources folder and the chatclient project a custom.properties file.

Once the runnable JAR file has been build it has the following structure in it.

- com
- junit
- META-INF
- org
- resources
    - default.resources
    - custom.resources

I believe the resource files should not be located there. but in the META-INF/maven folder instead. After trying out stuff like:

  • Adding a META-INF folder into my src/main/resources folder and putting the property files there.
  • Adding a MANIFEST file with Class-Path: . in it.
  • Loading the file in multiple ways in code.

But nothing seems to work. I guess it is Maven related and a simple change in the pom.xml could fix it. Sadly my knowledge on the Maven project setup and pom related subjects is very basic (this is my first project using maven). And I can't seem to find any documentation on it, even though I know it should be there (probably a problem caused by me).

Before I forget to mention it. I load the property files using this way:

Properties props = new Properties();
prop.load(<custom static class>.class.getResourceAsStream(filename));
return props;

Also the pom.xml for my library looks like:

-- Artifact stuff --
<packaging>jar</packaging>
<build>
  <plugins>
    <plugin>
      <artifactId>maven-compiler-plugin</artifactId>
      <version>3.1</version>
      <configuration>
        <source>1.8</source>
        <target>1.8</target>
      </configuration>
    </plugin>
  </plugins>
</build>
-- Dependency stuff --

And the one for the project that uses the library look like:

-- Artifact stuff --
<build>
  <plugins>
    <plugin>
      <artifactId>maven-compiler-plugin</artifactId>
      <version>3.1</version>
      <configuration>
        <source>1.8</source>
        <target>1.8</target>
      </configuration>
    </plugin>
  </plugins>
</build>

<dependencies>
  <dependency>
    <groupId>com.maxxton</groupId>
    <artifactId>async-amqp-messaging</artifactId>
    <version>0.2</version>
  </dependency>
</dependencies>
-- Other stuff --

I hope there is someone who's a little more advanced on this subject and could help find a solution for this problem. And if you need any additional information on the project files/structure, please let me know. I'd gladly share it with you.

Update (28-04-2015 {1})

For testing I created a sample project which tries to load property files the same way as the scenario described above. Even while following the Maven documentation (Using the META-INF folder) I was not able to load the properties.

For the sake of this question I uploaded the testing workspace here.

I hope someone could help me fix this, as the normal way as described on the Maven website does not seem to work for me.

Update (28-04-2015 {2})

Well I managed to fix a part of the problem. Since I added the configuration for the maven-assembly-plugin (building runnable JAR with deps), I was able to get the correct structure within my JAR file. The thing I added was:

<build>
    <plugins>
        <plugin>
            <artifactId>maven-assembly-plugin</artifactId>
            <configuration>
                <finalName>project</finalName>
                <appendAssemblyId>false</appendAssemblyId>
                <archive>
                    <manifest>
                        <mainClass>com.test.project.Application</mainClass>
                    </manifest>
                </archive>
                <descriptorRefs>
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
            </configuration>
            <executions>
                <execution>
                    <id>make-assembly</id>
                    <phase>package</phase>
                    <goals>
                        <goal>single</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

Then when running clean compile assembly:single I managed to get the right structure.

JAR root
    - com
    - META-INF
        - MANIFEST.MF
        - default.properties
        - custom.properties

While this fixes a part of the problem. The file loading still results in a NullPointerException.

Final Update (04-05-2015)

After a long Maven struggle I managed to get everything the way I want it to be. Following the advice given by both @Deepak and @Joop Eggen, I did some research on how to have all the dependencies in a lib folder as jar instead of unpacking them in a 'uber' jar. After trying loads of stuff I stumbled upon this answer. Following the instruction there seems to create this structure:

- runnable.jar
- lib
    - spring-amqp.jar
    - spring-core.jar
    ...

When following @Joop Eggen's advice I managed to get the property loaded the way I want to. So it seems this question has been answered. Currently I'm still figuring out how to award each answer as I'm not able to split the bounty into two pieces. I'll get back on that.

Side Note

Although I awarded both the bounty and the answer to @Joop Eggen does not mean that @Deepak's answer did not contribute. It did give some great information on best practice, but was not as complete as the accepted answer. So please when finding your answer here give him some of the credit too.

like image 945
Robin Hermans Avatar asked Apr 24 '15 09:04

Robin Hermans


1 Answers

There are two ways to get resources,

  • with a ClassLoader against the entire class path using absolute paths (without /...),
  • with a class, using a relative (...) or absolute (/...) path inside the jar of that class.

The latter seems more direct, and can be used as:

getClass().getResource("/...");
ClassInJar.class.getResource("/...");

Now getClass() is only possible in a non-static object and is dangerous too: the actual class might be some child, not in the library jar.


On the actual structure of your application. I know a maven directory convention:

src/main/java/...
src/main/resources/...

where /... gets into the jar/war; the package directory.

Reassembling jars is not good. There always is the Class-Path: ... entry in META-INF/MANIFEST.MF. Following the basic maven conventions is best.

like image 75
Joop Eggen Avatar answered Nov 02 '22 05:11

Joop Eggen