I have modified this question since initially asking it. Please refer to sections UPDATE 1 and, specifically, UPDATE 2 at the end.
I am building a large JavaFX application with a lot of dependencies. I am using IntelliJ to do the job and am at the point of deployment. I am using IntelliJ's own tutorial on artifacts to build an executable jar. The tutorial can be seen in the "Working with artifacts" tutorial on jetbrains.
I have built my executable jar and it is working as it should, with one caveat, however:
I have to manually mimic the directory structure of my IntelliJ project for the executable jar file to find the resources necessary for the program to function properly.
This is where my question comes in: shouldn't IntelliJ include these files in the artifact, so it can run in and on its own?
My directory structure in IntelliJ looks like this:
Project root
.idea
.out
.src
.main
.java
.com
.myCompany
.package-with-classes1
.class1 ... N
.package-with-classes2
.class1 ... N
.package-with-files
.file1.someExtension
.file2.someExtension
.other-package-classes
.and-so-on
When I build the artifact under Project Structure - Artifacts - Output Layout, I then manually add the directory structure as can be seen above, and then place the files where they belong.
As per my question above, I would expect these files to be automatically included with the executable jar file.
UPDATE 1: Added Maven to project
Due to Andrey's comment I have now implemented Maven in my project. I have a lot of external dependencies which I have added to my pom.xml file like so:
<dependency>
<groupId>some.group.id</groupId>
<artifactId>some-artifact-id</artifactId>
<scope>system</scope>
<version>1.0.0</version>
<systemPath>${basedir}\path\to\jar\jarfile.jar</systemPath
</dependency>
I then do:
mvn clean
mvn compile
mvn package
All runs with no errors.
It places 2 jar files in my \target folder: (1) name-of-jar.jar and (2) name-of-jar-with-dependencies.jar.
Running (1) throws the error: no main manifest attribute. Running (2) throws ClassNotFoundException and NoClassDefFoundError errors. Why is this? The classes throwing the errors are included as dependencies using the above approach.
UPDATE 2: Progress with Maven, but...
I solved the issue in section UPDATE 1 by installing all my third party jar libraries to my local machine's Maven repository at C:\Users\$USER$.m2\repository. However, getting a null pointer...
I changed my dependency declarations in my pom.xml to the following:
<dependency>
<groupId>some.group.id</groupId>
<artifactId>some-artifact-id</artifactId>
<version>some.version.number</version>
</dependency>
I am currently building my fat jar using the maven assembly plugin (I have also tried using the shade plugin but am having the same issue). Here's the excerpt of the assembly plugin from my pom.xml:
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>com.myCompany.myMainClass</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>
This produces the same two jar files in the \target directory as described in section UPDATE 1.
Now, if I run jar -tf name-of-jar-with-dependencies.jar I can see from the directory contents of this jar that the jar in fact does contain all the third party jar libraries that was missing; and running the jar file using java -jar name-of-jar-with-dependencies.jar does not throw the errors as described in section UPDATE 1 any longer. So far, so good.
However, it does throw a NullPointerException which puzzles me. Specifically, it is complaining that the a certain class is missing. This seems a little strange to me since this class is part of a third party jar library which I did add as a dependency in my pom.xml. The fact that this class is indeed included in the final jar was confirmed by the approach above, printing out the contents of the name-of-jar-with-dependencies.jar, which - among a lot of other files - includes this very jar file.
Any thoughts?
Related
I'm not a Java dev and am unfamiliar with the packaging and building of Java programs. I'm trying to run this file: https://github.com/CodinGame/SpringChallenge2020/blob/master/src/test/java/Spring2020Main.java
by doing
mvn clean install
java -jar .\target\spring-2020-1.0-SNAPSHOT.jar
but I get this error:
no main manifest attribute, in .\target\spring-2020-1.0-SNAPSHOT.jar
I can't figure out for the life of me what I need to add to the pom.xml or whatever else I need to do to get this to work.
Any help will be appreciated.
A few things to understand about Java:
1) If you have a Maven project like this, code is divided between src/main/ and src/test/ directories. src/test/ is intended for unit tests. In your case, Spring2020Main is not actually set up as a unit test, so I'm not sure what the author intended here.
2) When you compile using mvn clean install, a jar (library) is built, but nothing from src/test will be included in the output.
Generally, tests are executed during build. And this one would have been, except it's not set up as a real junit test, so it didn't run during build.
3) You can move the file from src/test/java to src/main/java and it will be built into your resulting jar.
4) In this case, when you run the JVM, you need to specify a classpath. This is a list of all libraries to include when the application starts. You also need to specify the (fully qualified) name of the class to run:
java -cp target/spring-2020-1.0-SNAPSHOT.jar Spring2020Main
...the above won't work directly since there are more unsatisfied dependencies (the top level pom.xml brings in at least 3 other deps you'd also need to provide on the classpath).
As others pointed out, a solution could be to build a self-executing jar, but simplest for you would be to run this from an IDE:
Run this from IntelliJ. If you haven't installed it, install it.
1) File > New From Existing Sources, find the directory where this is coned to.
2) When asked, Import Project from Existing Model (Maven)
3) When the Project view is available (alt-1), or View > Tool Windows > Project, you can expand the structure till you find Spring2020Main in the test directory.
4) Right-click it and select Run.
For me, it exposed a web server running at http://localhost:8888/test.html
You can follow the steps below:
Move Spring2020Main.java to src/main/java/com/codingame directory
Add the following to your pom.xml after the </dependencies>:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.codingame.Spring2020Main</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
Run maven build using mvn clean install
Execute the program using java -jar target/spring-2020-1.0-SNAPSHOT.jar
Info: Apache Maven Shade Plugin helps in building what is called an uber-jar or a fat-jar. This means that all the dependencies are packaged as part of the resultant jar file without the need for any 'libraries' that you'd need to add in the classpath when executing the jar file. As part of the final jar, we need to specify which file needs to be treated as the main file to be executed. This is typically done using META-INF/MANIFEST.MF file inside the uber-jar. That's what the transformer specified inside the configuration of the plugin does for us.
The project you've linked has only a basic setup for compilation (that would be enough to run it from IDE though).
What you need is an executable jar. Check this thread.
As others mentioned (and I failed to notice) the class you linked to is a test class, so it may not be included in a jar by default. Run it through IDE or set it up in a proper source directory.
Sorry if this is too trivial, but I've recently jumped into Wep Applications from standard console java projects, and since in java projects I successfully used maven to download jars and include them into the classpath... in the web app I don't know how to accomplish the same stuff and downloading to the lib folder on WEB-INF instead of just adding the jars to my classpath.
I have searched stack overflow and the google for an answer, but since I haven't found any single answer, I'm afraid I should be completely wrong with my approach for this.
I have just created my webapp on eclipse, then converted it to maven project, and then added this dependencies in pom.xml, as I used to do in a normal java project:
<dependencies>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.9.1</version>
</dependency>
</dependencies>
Everything seems to be ok, and no error is shown, but the jars are not downloaded into the WEB-INF/lib folder.
Thanks in advance for any help you can provide.
If your project is correctly configured as "Maven project" there should be a folder called "Maven Dependencies" in your package view.
A good hint if your project is configured as Maven project is a little "M" on the top level folder.
Eclipse will download in a so called "Repository". This is mostly located in $HOME/.m2
Generally you don't have to care about jars directly. Maven will download them and create a classpath transparently for you.
The eclipse maven integration is called m2e.
The concept of Repositories is central to maven. They are the place where your dependencies and external dependencies are stored. Two repositories you can always assume to exist are the already mentioned local one and the other so called "Maven-Central" see here. Beside that you can setup , for example, company wide Repositories with tools like Nexus or Artifactory.
To upload a dependency in your local Repo use the mvn install command.
What maven within mvn install is executing the install lifecycle.
and then uploads the resulting artifact (generally a jar or war, but not necessarily) and some metadata (your pom mainly) to the repository.
You can then develop against these dependencies via the dependency mecahnism.
Dependencies in you installed in your local Repository are always of type "SNAPSHOT". There is more to know about the difference between SNAPSHOT and Release (only version number) and how to deploy/release them, but these questions are already awnsered several times.
Adding this plugin to pom.xml will help to get jars in lib folder.
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<phase>install</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>WebContent/WEB-INF/lib</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
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:
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.
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.
The solution to this is simpler, however there are couple of concepts that I would like you to be aware off.
It is not a good practice to create one big jar by using maven assembly plugin. In your examples, you are using small jars both developed by you, so it might seem okay. But as your real projects get bigger, this is not ideal, you would want separation of different modules and not one big jar. The ideal practice is opposite to what you are trying to achieve - you should aim to have smaller and smaller jars. You might in future want the convenience of replacing these smaller jars without having to deliver the entire package again.
You would surely not want to unpack 3PP jars and package them as your own jar!
Based on the above, you might wonder how do you create executable jars. Well, the answer is having the dependent jars in the class-path. When executing the "project" jar, you should have the "library" jar in the class-path and it will be able to find the properties file inside as expected.
You places the properties files in META-INF directory. The right place for them is the resources folder. And if you follow the point No.1, things will work as expected.
This question already has answers here:
How can I create an executable/runnable JAR with dependencies using Maven?
(33 answers)
Closed 8 years ago.
I have an eclipse Luna project with a bunch of maven dependencies defined in a pom.xml
Everything works fine in eclipse. But now I need to include all of those dependencies in an exportable jar file (so that I can ship them to workers in Apache Spark).
I keep fiddling with the export settings, but I don't see any way to export them into the jar file.
I find some answers explaining how to configure maven to package its dependencies. Is that my only option, or is there some way to do this in eclipse?
Take a look at this question: How can I create an executable JAR with dependencies using Maven?
I think that #Rocky Inde's answer is what you are looking for (using eclipse):
1) Just right-click on your project folder (in Eclipse) and select
Export
2) Then select Java -> Runnable Jar
3) You will be asked to choose the location of the jar file
4) Finally, select the class that has the Main method that you want to
run and choose Package dependencies with the Jar file and click Finish
Then you need to include shade pluggin in your pom.xml and mvn package will produce the shade jar(fat jar) this link provides info about shade pluggin
I think that this question is duplicated:
If you want to do it with the console and Maven you could take a look into this thread: the link that #Tarik mentions
If you want to use Eclipse, take a look into this one: How to create a jar with external libraries included in eclipse?
I suggest you use Maven assembly plugin. From the usage page:
For example, imagine that our project produces a JAR. If we want to create an assembly binary that includes our project's dependencies, we can take advantage of one of the Assembly Plugin's prefabricated descriptors. You configure it as follows in your project's pom.xml:
<project>
[...]
<build>
[...]
<plugins>
<plugin>
<!-- NOTE: We don't need a groupId specification because the group is
org.apache.maven.plugins ...which is assumed by default.
-->
<artifactId>maven-assembly-plugin</artifactId>
<version>2.5.3</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
[...]
</project>
I'm new to maven and somewhat new to java. Tried google and related sources, but I didn't find one which resembled my situation.
Right now, I have maven project X and Y. X can be seen as a shared library with some utilities, Y is a simple JFrame with a "hello world" printed and a call to a static method in X.
I do a "run as maven install" on project X, I get a "build successful". I add project X as dependency in project Y (using the pom-editor in Eclipse, browsing the repository and locating it). I do a "run as maven package" on project Y, I get a "build successful". Upon running project Y either via java -jar or inspect the produced jar, project X is missing everywhere and I get a fancy class not found exception. Eclipse finds it and there are no compile errors in the source editor.
Why is it only working in the Eclipse editor and not as jar?
POM:
<dependency>
<groupId>com.company.deployment.shared</groupId>
<artifactId>com.company.deployment.shared</artifactId>
<version>0.0.1-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
Maven doesn't produce a combined JAR file for you. What Eclipse is doing is looking at the Maven configuration and adding all the required classes / jars to your classpath for you when it runs.
If you want to run your program from the command-line, you will need to add all the JARs manually to your classpath.
Alternatively, you could run your program directly from Maven which should set up all your dependencies. There are a number of options depending on what you want to do, i.e. if it's an application which is meant to be run by an end-user you could look into the one-jar Maven plugin.
I recommend that you take a look at the Maven shade plugin. This produces an "uber-jar" comprising your project and all of its dependencies. It can also do other things such as setting the entry point class to make your JAR file an executable JAR.
You may also find exec-maven-plugin helpful
mvn exec:java -Dexec.mainClass="com.example.Main" [-Dexec.args="argument1"] ...
mvn exec:exec -Dexec.executable="maven" [-Dexec.workingdir="/tmp"] -Dexec.args="-X myproject:dist"
If your client can not download dependencies from maven m2 repo on the fly like behind firewall or no internet connection, then you also need to package the dependencies using maven-dependency-plugin to copy all dependencies and maven-assembly-plugin to assemble dependencies
It doesn't work because Maven resolves dependencies when building your project, but doesn't put all the dependencies magically in your jar. You're supposed to run your app with all its dependencies in the classpath:
java -classpath X.jar;Y.jar com.foo.bar.Main
Or you have to customize the maven jar plugin in order to create an executable jar, as described here. And you may also use the maven assemby plugin to copy all your Y project's dependencies to the target directory, next to the generated Y.jar.
The artifact produced in project Y contains only build results in project Y only, not including its dependencies.
If you want to build a JAR in Y, which u can execute directly, you can consider using assembly plugin.
For example, the easiest way to build a uber-jar for project Y:
<project>
...
<build>
...
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2.1</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-all-in-one-jar</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
...
</project>
Apart from a normal artifact, an assembly which contains classes etc from dependencies will be created, which is suitable to be executed by java -jar
visit http://maven.apache.org/plugins/maven-assembly-plugin/ for more sophisticated usage.
Phil Sacre already explained the basic problem well (there basically is just no information on where to find the X.jar embedded in your Y.jar).
Additionally you can also look at the appassembler-maven-plugin (which can e.g. generate launch scripts for your Y project that already have the right classpath set) and/or the exec-maven-plugin (which you can use to e.g. directly launch Y with the right classpath using maven).