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.
Related
I have a java program that I coded in intellij using maven. I exported it as a jar and tried running from the command line but it keeps on telling me
Error: Could not find or load main class com.company.Main
Caused by: java.lang.ClassNotFoundException: com.company.Main
I then attempted to run the program itself from the command line and that also tells me it cannot find the main class
command line:
for the jar: java -jar selenium_project.jar
for running the program :java com.company.Main
For the jar I am running it from the folder it is in. For the program I am running it from the root of the program.
Pom.xml
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<configuration>
<archive>
<index>true</index>
<manifest>
<mainClass>com.company.Main</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins></build>
Manifest:
Manifest-Version: 1.0
Main-Class: com.company.Main
It might be an issue with maven, I dont know. I created a simple hello world program and ran that from the command line and it worked. I ran it like this (c://etc)../src/> java com.company.HelloWorld. But again when i try for this one it cannot find the main class.
Thank you
These are my classes
EDIT:
I changed to use the maven-assembly-plugin and I rebuilt the jar and still does not work
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<archive>
<manifest>
<mainClass>
com.company.Main
</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</execution>
</executions>
</plugin>
</plugins></build>
class structure to show where main class is
code in the Main class
public class Main {
public static void main(String[] args) throws IOException, SQLException {
System.out.println("hello");
Executor ex = new Executor();
}
}
Your error is probably that your Main class is in the wrong package.
Furthermore, please not that you need the Maven shade plugin or Maven assembly plugin to build executable jars if you have dependencies.
I dont know what the issue was. I tried creating a new program and i copied over the classes and remade the pom.xml file and my new program works except for one dependency (chrome options) but I dont think that is what was causing an issue. So now I just need to figure out why Chrome options wont work but the actual jar from the new program does run.
From: https://www.xspdf.com/resolution/54971948.html
To extract the files from a jar file, use x , as in: C:\Java> jar xf myFile.jar. To extract only certain files from a jar file, supply their Install an archive program. JAR files work just like ZIP files. You can use any archive program to extract them. 2. Find the JAR file you want to extract. Use File Explorer (press Win + E to open File Explorer) or Finder on Mac to 3. Right-click the JAR file. Doing so . How to extract manifest file from jar. Extracting the Contents of a JAR File.
I'd need Maven war plugin to use a custom MANIFEST.MF. As I read from the documentation you can put your MANIFEST.MF file in src/main/resources/META-INF/ and add the following configuration to your pom.xml file:
<plugin>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<archive>
<manifestFile>src/main/resources/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
However I see the MANIFEST.MF added in the WebRoot/META-INF is the default one. I can't find what is going wrong. Any suggestion or link to github projects where it does work ?
Thanks
First in your configuration you missed the <groupid>!
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
...
<configuration>
<archive>
<manifestFile>src/main/resources/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
And then be aware that only those entries of your own manifest are used which are not in the generated by the plugin!
The content of your own manifest file will be merged with the entries created by Maven Archiver. If you specify an entry in your own manifest file it will override the value created by Maven Archiver.
See the apache maven plugin page for entries of the default manifest, and configurable entries. Then you can add further own entries by providing a manifest using <manifestFile>
edit: If you want to use a 1:1 copy you could try to get this done my using the maven assembly plugin, but I think you can you do this with the normal mavne plugins.
Is there anyway to get at runtime the full artifact version / build number generated by the maven deploy plugin of my war at runtime?
It'd be ok if the maven process generated and packaged up a properties file with the value.
Keep in mind that my project generates unique (timestamped) artifact versions for every deploy.
Try this:
<configuration>
<archive>
<manifest>
<addDefaultImplementationEntries/>
<addDefaultSpecificationEntries/>
</manifest>
</archive>
</configuration>
That will add the maven details from the pom that build the war into the MANIFEST.MF file, which you can read at runtime.
Another way that I occasionally use is to use filtering of properties files, and drop the pom values into there - useful if you already have a property file/config loading mechanism in place.
Then, when it's deployed, which uses the same values from the pom, you're fine.
-Chris
You could package it up in your Manifest file using maven-jar-plugin which can be read through you application wherever you need.
Here is the basic configuration which would write the properties in pom.xml to your Manifest file.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<configuration>
<archive>
<manifest>
<addClasspath>false</addClasspath>
</manifest>
<manifestEntries>
<Implementation-Vendor>Vendor Name</Implementation-Vendor>
<Implementation-Version>${revision}</Implementation-Version>
<Build-Timestamp>${timestamp}</Build-Timestamp>
<Build-Number>${release}_${revision}</Build-Number>
</manifestEntries>
</archive>
</configuration>
<executions>
<!-- your jar executions here -->
</executions>
</plugin>
where your property values would be defined in pom.xml as :
<properties>
<timestamp>${maven.build.timestamp}</timestamp>
<release>DevelopmentBuild</release>
<revision>Some Value</revision>
<properties>
EDIT:
This can be done using maven-war-plugin also.
You can get more information on WAR Manifest Customization Here.
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.
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>