Maven jar-with-dependencies including 2 of each of my classes - java

I'm using maven to create a self-contained executable jar file with a utility I've written to take a slice of a log file between given date/time stamps & copy this part of the log out to a smaller file. When I do
mvn install
maven creates 2 jar files, LogCopy-0.0.1-SNAPSHOT.jar & LogCopy-0.0.1-SNAPSHOT-jar-with-dependencies.jar. It's weird that it creates the two archives, but weirder yet, and a cause of concern for me, is that in the larger .jar there seems to be 2 of every class I've written.
My POM.xml uses the maven-assembly plugin thus:
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<id>create-my-bundle</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>com.mycompany.myproject.subproject.MainClass</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
Am I doing anything wrong here? I'm at a loss - Google doesn't seem to have much on it & I can't find anything much on it in O'Reilly's "Maven: The Definitive Guide". Why is it putting 2 of each class into the .jar, and why does it do a .jar without dependencies as well as the one with?

Two jar files are generated because LogCopy-0.0.1-SNAPSHOT.jar is created by a "normal" maven build. You've just added plugin that additionally (as a side task) creates LogCopy-0.0.1-SNAPSHOT-jar-with-dependencies.jar archieve. The maven-assembly-plugin does not disable "normal" build it just executes extra task during package phase.

It sounds like this bug: http://jira.codehaus.org/browse/MASSEMBLY-399
I'm not sure what version of Maven you are using, but one of the comments there indicates you can use the following XML...
<archiverConfig><duplicateBehavior>skip</duplicateBehavior></archiverConfig>
If you browse through that JIRA, you can see quite a few bugs related to that issue.

Just downloaded 2.2-beta-5 of the maven assembly plugin.The issue of .class duplicates in the jar file is fixed in that version. Though I still wonder how the same file name could be packed into the same location in a jar.

In version 2.2 all looks ok, so temporary you can add:
<version>2.2</version>

Related

How can I specify a classpath for executables created with javapackager?

The javapackager command in Java 8 is able to create standalone executables. The examples on the web usually only use a single jar file, but I have several jar files that need to be bundled into the application. The documentation states that it is possible to tell the bundler the classpath sending classPath=path through the -B option. However, in the resulting <applicationname>.cfg file inside the packaged application there is a line that says
app.classpath=
which is not affected at all by the classPath argument, but I have to manually edit it to include all the jars used by the app to make it work.
All this is on macOS, I couldn't try it on another platform yet.
Any insight greatly appreciated – a working example using more than one jar, for example.
The documentation is slightly flawed:
-Bclasspath=
with small p instead of capital P works (Tested on Windows and MacOS).
I have this problem and found an alternative solution that does not require modifying the cfg file's class path. My approach uses Maven but it can be done with Ant or scripting; just requires more work. The idea is to create a single jar file that has all of the contents of your main jar and all the dependent jars. This can be done with a single Maven plugin like so:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2-beta-4</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>com.yourapp.MainClass</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<phase>prepare-package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
I set the execution phase to prepare-package so that it runs before the app bundling, but you can change that as needed. The end result is a single jar named <appName>-<version>-jar-with-dependencies.jar which contains the extracted contents of all the dependent jars. You can test it using java -jar <jarName> before using it with javapackager.

How to add a dynamically generated file to my ear archive?

Basically, we have an ear archive that has multiple dependencies. In those dependencies, we plan on filling the manifest or some properties file with multiple properties:
build timestamp
buildnumber (from CI server)
groupId
artifactId
version
This should already be possible for individual artifacts that are built by our CI server, and this is also not really the problem I'm having.
Now I use maven for building my ear archive, with the maven-ear-plugin. My idea was to just use a groovy script (executed by gmaven-plus) that will read all the jar archives, and fetch the manifest. Then I can rewrite the manifest of the ear archive, or write my own properties file that contains the info I want.
import org.apache.commons.io.FileUtils
import java.util.jar.JarFile
def path = "target/${project.name}-${project.version}"
def artifactDir = new File(path)
def output = new File("$path/META-INF/build-info.properties")
def lines = []
artifactDir.listFiles().each { file->
if (file.isFile() && (file.name.endsWith(".war") || file.name.endsWith(".jar"))) {
def jar = new JarFile(file)
def manifest = jar.manifest
println "processing: $file.name"
println manifest.mainAttributes
println jar.properties
manifest.mainAttributes.keySet().each { attrKey->
def val = manifest.mainAttributes.getValue(attrKey.toString())
lines << "$file.name.$attrKey=$val"
}
}
}
FileUtils.writeLines(output, lines as Collection<?>
)
This script works fine, and will write the build-info.properties file with all the info I'd like to be in there.
This is the plugin configuration:
<plugin>
<groupId>org.codehaus.gmavenplus</groupId>
<artifactId>gmavenplus-plugin</artifactId>
<version>1.5</version>
<executions>
<execution>
<id>create-manifest</id>
<phase>package</phase>
<goals>
<goal>execute</goal>
</goals>
</execution>
</executions>
<configuration>
<scripts>
<script>file:///${project.basedir}/src/main/gmaven/create-manifest.groovy</script>
</scripts>
</configuration>
</plugin>
Of course, you'll already notice what's wrong with this: the packagephase is too late, prepare-package is too early, because the ear plugin has not yet copied the dependencies I'm scanning.
Conclusion
Well, the obvious thing to do would be to unzip the ear produced, and then add the file manually, then zip it again. But that seems pretty dirty to me, and I was wondering whether there is no cleaner way to do this?
Perhaps by leverageing gradle, or some option of the maven-ear-plugin I have not yet discovered? Or just in general, how would you tackle the specific problem I am facing here?
Why not using the maven possibilities..like the configuration for the archiver which means you can use a configuration like this for the maven-jar-plugin:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>${maven-jar-plugin.version}</version>
<configuration>
<archive>
<addMavenDescriptor>true</addMavenDescriptor>
<index>true</index>
<manifest>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
</manifest>
<manifestEntries>
<artifactId>${project.artifactId}</artifactId>
<groupId>${project.groupId}</groupId>
<version>${project.version}</version>
</manifestEntries>
</archive>
</configuration>
</plugin>
This can also be used for maven-war-plugin, maven-ejb-plugin and for maven-ear-plugin as well...See the docs for maven-ear-plugin see archive entry.
The buildnumber from your CI solution (for example Jenkins) has an env variable BUILD_NUMBER which you can use as well...
Full example how a corporate pom can look like take a look here.
Update:
What i missed is that you can use filtering to create such file. Put a file into src/main/resources with the following content:
groupId=${project.groupId}
artifactId=${project.artifactId}
version=${project.version}
Define the following as part of your pom file:
...
src/main/resources
true
Furthermore this will package the above file into your ear/war/jar file.
Apart from that a file which contains already the above information can be found in earchive META-INF/groupId/artifactId/pom.properties which contains the above. Not the build number and build time...
The build time can be used by using the following:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-ear-plugin</artifactId>
<version>2.6</version>
<configuration>
<archive>
<manifestEntries>
<buildTime>${maven.build.timestamp}</buildTime>
<buildNumber>${env.BUILDNUMBER}</buildNumber>
</manifestEntries>
</archive>
</configuration>
</plugin>
${env.BUILDNUMBER} is of course a refererence to bamboos variables which contains such information.

Maven Jar Builder: Could not find or load main class

I have been trying for several days now to create an executable jar file for my muli-module maven project. However, when I try to run this jar file I get "Could not find or load main class src.main.java.com.domain.Mainclass" (I have changed the name domain and MainClass for my company's privacy sake)
I have searched for days and none of the obvious tips seem to work.
My project has a main maven project that downloads all of the dependencies (packaging:POM)
and several module projects (Packaging:Jar).
Everything seems to work fine, and all of the files are compiled into class files, but somehow the main class is not being added to the pom.
My Pom File Plugin:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2-beta-4</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>com.domain.project.MainClass</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
The Commands I Use: mvn clean package, then I cd into the target folder and do: java -jar jarfilename.jar
Any tips or help would be most welcomed!
Edit:
My current configuration creates a 2 jar files for every module:
projectname-jar-with-dependencies.jar
projectname.jar
When I navigate to the target folder of the module with my main class, I can successfully run the jar file. However, when I try to run the jar file of my parent maven project (the one with packageing:pom) I still get the same error. Does anyone know why the main jar file cannot find the main class?
Thanks!
You should not have src.main.java as part of the package name of your main class. It's just part of the default maven project folder structure convention. Your configuration should probably read:
<archive>
<manifest>
<mainClass>com.domain.project.MainClass</mainClass>
</manifest>
</archive>
I'm going to necro this post because it is important to have 3 different things set properly:
MainClass.java needs to be in <project_root>/src/main/java/com/domain/project/
Maven assembly plugin needs <mainClass>com.domain.project.MainClass</mainClass>
Your package should be set to com.domain.project
When those three match, Maven should package an executable JAR file.
it seems like you have one of the plugins missing. add the bellow to pom.xml file
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.6</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>yourclassnameKt</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
The plugin will add MANIFEST.mf file, which will tell the Java runtime which class to execute.
There is another issue that has the same problem ->
Kotlin + Maven assembling: no main manifest attribute
Reference: https://michaelrice.com/2016/08/hello-world-with-kotlin-and-maven/
I have a bit of a different but similar scenario so I thought I would share. I have a primary maven application packaged into a war through the build (pom.xml packaging configuration). I wanted to add a jar file that is created from one package within our source code, and added into the assembled output along with the webapp war. This allows us to deliver the web application along with a cli tool separately. I didn't want to reconfigure the pom.xml to be multi-module, but just to add a jar as a separate executable within our existing structure. I was banging my head against this for a while.
First, I ended up deleting my entire .m2 directory with maven repositories locally. It appears there may have been an issue for me here, because my ultimate code that worked seems to be the same as what wasn't working originally. I suspect the reasoning for this was I was trying different versions of libraries and was creating conflicts, but who knows. First suggestion I have if you are having issues is delete your .m2 folder and try from scratch.
Also reference #Adam Howell's answer because that is the fundamentals. I created a simplified example project to figure out why nothing was happening and in there I realized i forgot to prefix the folder structure as src/main/java... doh! Of course in my existing project, this was not the case.
And here is my plugin code i inserted in my pom.xml that worked:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>com.myCompany.app.cli.CLITool</mainClass>
</manifest>
</archive>
<outputDirectory>${project.build.directory}</outputDirectory>
<finalName>our-cli</finalName>
</configuration>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
I am pretty sure you can change the phase you want this done in, I figured earlier on in compile made since to ensure it was available for packaging later. I'm not very experienced with Maven though, so note that this may not be semantically in-line with Maven conventions.
And it worked! Not sure why this took me hours to work out although it did. This I just updated my bin.xml to include the jar file in my assembled deliverable, and Voilah! I have a jar executable separate from my webapp that I can use as a command-line interface tool.

Obtaining the Build ID in a Java Application

Does anyone have a simple suggestion for recording a build ID (generated at compile time) which is displayed in the title bar of the app at runtime?
Building from within Eclipse, all I need is the ID, I can then pass it up to the title.
If you're using Maven, especially if you want the build number from SVN (though it can generate unique build numbers for you through a configuration), look at the buildnumber-maven-plugin.
You simply add a snippet similar to the following to your pom.xml file:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>buildnumber-maven-plugin</artifactId>
<version>1.0-beta-3</version>
<executions>
<execution>
<phase>validate</phase>
<goals>
<goal>create</goal>
</goals>
</execution>
</executions>
<configuration>
<doCheck>true</doCheck>
<doUpdate>true</doUpdate>
</configuration>
</plugin>
Then use ${buildNumber} later on in your pom to refer to the build id. I use it to write that number to the manifest like so, using the maven-war-plugin.
<archive>
<manifest>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
</manifest>
<manifestEntries>
<Implementation-Build>${buildNumber}</Implementation-Build>
</manifestEntries>
</archive>
If you are using Ant, you can easily set up your "jar" or "package" target so that it generates a file including the current timestamp and include this in your jar output.
If using Maven, there are a few ways to achieve something similar, such as dropping down to Ant using the antrun plugin.
If you want to use a timestamp for the build, you can obtain that from the jar or class modifation dates or the MANIFEST file.
Maven populates a MANIFEST file with the module version number in the jar. You can read this to obtain the version of all the maven modules you are using.
Making the timestamp part of the "unique id" ensures every build has a different id.
If you are completely building within Eclipse, you need to create a build action which generates a resource in your source folder with the information you need - a property file will do nicely - which then propagates to your binary output and can be read at runtime after which you can do what you need to show it.

How to change the default output from a Maven 2 / Cobertura instrument goal?

when i instrument my classes using Maven 2 using the command
mvn cobertura:instrument
The output (the instrumented classes) are put in \target\generated-classes. Is there a way to change the output location to \target\classes?
I checked the instrumentation tasks of the cobertura-maven plugin but this does not give me a solution sofar.
You have not said why you want to overwrite the default location, but I assume it is so that you can use the instrumented classes from another project, or perhaps include them in a web archive or something similar.
I added the following to my pom.xml
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<id>instrumented-classes</id>
<goals>
<goal>jar</goal>
</goals>
<phase>package</phase>
<configuration>
<classifier>instrumented</classifier>
<classesDirectory>${project.build.directory}/generated-classes/cobertura</classesDirectory>
</configuration>
</execution>
</executions>
</plugin>
This makes maven generate an additional jar file called projectname-instrumented.jar
It is then possible to depend on this jar file from any other pom (including for example a web module) using
<depends>
<group>mygroup</group>
<project>projectname</project>
<version>1</version>
<classifier>instrumented</classifier>
</depends>
I did not test this 100% but have used similar mechanisms in the past
As far as I understand, the instrumented classes are only needed by cobertura for report generation. If you create them in target/classes, they will overwrite the original class files.
If you need the instrumented files in a jar as a result, you can configure the maven-jar-plugin to pick up the files from the target/generated-classes directory instead of or in addition to the files from the standard ${build.project.outputDirectory}.
Edit
Have a look at the maven-jar-plugin description. To only use target/generated-classes, the following addition to your POM should work - try it and modify it to your needs:
<project>
...
<build>
<plugins>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.3</version> <!-- replace with correct version nbr! -->
<configuration>
<includes>
<include>${project.build.directory}/generated-classes/**/*.class</include>
</includes>
<excludes>
<exclude>${project.build.directory}/classes/**/*.class</include>
</excludes>
</configuration>
</plugin>
...
</plugins>
</build>
...
</project>
${project.build.directory} points to your target folder, ${project.build.ouputDirectory} to target/classes. I do not know if you can simply set ${project.build.ouputDirectory} to a new value - have a look at the this chapter of the maven book, maybe you find some hints
Edit 2
Alternativly or additionally you can use maven to copy the files from target/generated-classes to target/classes after coberture:instrument has finished. This question has one answer with an example POM (fragment), you just have to identify the correct phase (process-resources is definitely too early for your case)
Did you try "mvn cobertura:instrument install"? It will generate a jar file including all the cobertura version classes.
If you want to change back original version, just run the command without "cobertura:instrument".
I just implemented the solution proposed by Andreas_D, modified my pom and uses the maven-resources-plugin. So on some stage of my build the Cobertura generated files are copied to the /target/classes directory.
You can configure it using <classesDirectory>[Your DIR]</classesDirectory>
In cobertura-maven-plugin version 2.4 this is still not supported. I've just created an improvement ticket, patch is attached to the ticket.

Categories

Resources