SpringBoot fully executable jar without dependencies inside - java

NOTE: Please, before marking this question as a duplicate make sure you know the difference between executable JAR and fully executable SpringBoot JAR.
The official Spring Boot documentation describes how to build fully executable JAR. Then generated JAR file can be linked from /etc/init.d/ and started/stopped/restarted/statused as a normal unix service without additional scripts or tools like JSVC.
But the generated JAR contains all libraries and can be big enough in size (in my case 70Mb+).
I want to generate such fully executable JAR without libraries, but then to be able to run it as SystemV service on Linux and link external libraries (JARs) somehow.
UPDATE
I want to reduce the artifact size in order to speed up deploy->test->fix cycle. Sometimes I'm working via mobile network and big file size can decrease my job speed dramatically.
In case there is no a simple configuration property or a profile or a command line option I would use a kind of hack.
At the beginning, I can generate a build containing all dependencies.
Then I can unzip it and move all libraries to a special folder.
Then I need to pack it again as fully executable somehow and run with pointing to the folder with libraries.
I don't think this can be done with jar utility because file utility recognizes fully executable jar as data
$ file fully-executable.jar
file fully-executable: data
unlike the usual jar
$ file usual.jar
usual.jar: Java Jar file data (zip)

You may want to consider using Spring Boot Thin Launcher. It creates a jar file with your application code but none of its dependencies. It adds a special thin launcher that knows how to resolve your application's dependences from a remote Maven repository or from a local cache when the jar is executed. Judging by the description of what you want to do, you'd utilise the local cache option.
The configuration of Spring Boot's Maven plugin to produce a fully executable jar that uses the thin launcher looks like this:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot.experimental</groupId>
<artifactId>spring-boot-thin-layout</artifactId>
<version>1.0.3.RELEASE</version>
</dependency>
</dependencies>
<configuration>
<executable>true</executable>
</configuration>
</plugin>
</plugins>
</build>

I can use this setting to create spring boot jar without dependency jars.
Copy dependency jars to dist/lib
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<includeScope>runtime</includeScope>
<outputDirectory>${project.build.directory}/dist/lib</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
Specify 'lib' as classpath prefix, so MAINFEST.MF will be created like this:
Class-Path:lib/httpcore-nio-4.4.14.jar lib/guava-24.1.1-jre.jar...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>com.xxx.MyMainClass</mainClass>
</manifest>
</archive>
<outputDirectory>${project.build.directory}/dist</outputDirectory>
</configuration>
</plugin>
Let spring boot plugin includes dependency which is not existing, will cause spring boot jar exclude all dependencies.
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
<configuration>
<layout>ZIP</layout>
<includes>
<include>
<groupId>not-exist-in-my-project</groupId>
<artifactId>not-exist-in-my-project</artifactId>
</include>
</includes>
<outputDirectory>${project.build.directory}/dist</outputDirectory>
</configuration>
</plugin>

Related

Maven package effective pom

I have a Maven project with a number of sub modules. Some of these sub modules are packaged as jar that are deployed to a Nexus Maven repository.
The problem I have is that the packaged jar references the parent pom which is not necessarily deployed.
Is there a way for Maven to deploy the effective pom instead of the pom.xml?
You need to be perfectly aware of the consequences of what you want to do: the effective POM will also contain your current settings (content of settings.xml), thereby possibly publicly exposing whatever passwords you have hard-coded in there. A better solution would be just to deploy the parent POM.
However, if you really want to go down that path, you can have the following configuration:
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<configuration>
<archive>
<addMavenDescriptor>false</addMavenDescriptor>
</archive>
</configuration>
</plugin>
<plugin>
<artifactId>maven-help-plugin</artifactId>
<version>2.1.1</version>
<executions>
<execution>
<phase>generate-resources</phase>
<goals>
<goal>effective-pom</goal>
</goals>
<configuration>
<output>${project.build.outputDirectory}/META-INF/maven/${project.groupId}/${project.artifactId}/pom.xml</output>
</configuration>
</execution>
</executions>
</plugin>
This tells the maven-jar-plugin not to add the Maven descriptor pom.xml and pom.properties to the jar. Instead, the pom.xml is generated by the maven-help-plugin and its effective-pom goal.
If you want the pom.properties file also, you will need to create it manually with the maven-antrun-plugin.

How to get maven to build a war with minified files using yuicompressor-maven-plugin

So I'm trying something I thought would be rather simple, I basically want maven to minify all my js and css files for me before building a war. My plugins look like this:
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<version>1.0.0</version>
<configuration>
<manifestLocation>META-INF</manifestLocation>
<instructions>
<Export-Package>!test.impl,test*</Export-Package>
<Import-Package>*</Import-Package>
<!--
add ,plugin.xml if it's present i.e.
src/main/resources,plugin.xml
-->
<Include-Resource>src/main/resources</Include-Resource>
</instructions>
</configuration>
</plugin>
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>yuicompressor-maven-plugin</artifactId>
<version>1.1</version>
<executions>
<execution>
<goals>
<goal>compress</goal>
</goals>
</execution>
</executions>
<configuration>
<nosuffix>true</nosuffix>
</configuration>
</plugin>
The problem is that the YUI plugin does correctly minify the files, but just before the war is built it looks like it copies over all the files from my main source directory and thus wipes out the changes the YUI plugin had done.
I'm calling maven by the following: mvn compile war:war. I've been playing around for awhile with different settings, but so far I haven't found a way around this.
What I would like is for just after the war has copied over the files it needed from the src directory it would run the YUI plugin, but I tried all the permutations of phases on the YUI plugin, but that didn't seem to make any difference.
I've googled around, but pretty much everything I've read so far seems to indiciate that I should just need to drop the YUI plugin in there like I have and everything should magically work. So far I haven't seem to have found the magic.
The accepted answer doesn't work.
Much better is this answer (as pointed out by koga in his comment):
https://stackoverflow.com/a/11495021/11451
Here is what I ended up doing:
Step 1: minify the css and js
<plugin>
<groupId>com.samaxes.maven</groupId>
<artifactId>minify-maven-plugin</artifactId>
<version>1.7.2</version>
<executions>
<execution>
<phase>prepare-package</phase>
<goals>
<goal>minify</goal>
</goals>
</execution>
</executions>
<configuration>
<charset>UTF-8</charset>
<skipMerge>true</skipMerge>
<cssSourceDir>resources</cssSourceDir>
<jsSourceDir>resources</jsSourceDir>
<jsEngine>CLOSURE</jsEngine>
<nosuffix>true</nosuffix>
<webappTargetDir>${project.build.directory}/minify</webappTargetDir>
<cssSourceIncludes>
<cssSourceInclude>**/*.css</cssSourceInclude>
</cssSourceIncludes>
<cssSourceExcludes>
<cssSourceExclude>**/*.min.css</cssSourceExclude>
</cssSourceExcludes>
<jsSourceIncludes>
<jsSourceInclude>**/*.js</jsSourceInclude>
</jsSourceIncludes>
<jsSourceExcludes>
<jsSourceExclude>**/*.min.js</jsSourceExclude>
</jsSourceExcludes>
</configuration>
</plugin>
Step 2: overwrite css and js in the war with minified files
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.4</version>
<configuration>
<webResources>
<resource>
<directory>${project.build.directory}/minify</directory>
</resource>
</webResources>
</configuration>
</plugin>
Use the war generator and add the configuration to exclude the sources file.
Example:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>${war-plugin.version}</version>
<configuration>
<warSourceExcludes>js/**/*.js</warSourceExcludes>
</configuration>
</plugin>
After that you need to include your target files to your war. You do this by setting the "prepare-package" phase of maven lifecycle (I'm using Minify plugin) and adding the files on Minify configuration (jsSourceIncludes,cssSourceIncludes,...)
For example:
<plugin>
<groupId>com.samaxes.maven</groupId>
<artifactId>minify-maven-plugin</artifactId>
<version>1.7.2</version>
<executions>
<execution>
<id>default-minify</id>
<goals>
<goal>minify</goal>
</goals>
<phase>prepare-package</phase>
<configuration>
<jsSourceDir>/js</jsSourceDir>
<jsSourceIncludes>
<jsSourceInclude>/**/*.js</jsSourceInclude>
</jsSourceIncludes>
</configuration>
</execution>
</executions>
</plugin>
What happens is that the config above is running the compressor on the process-resources phase, but then the package phase overwrites these files with the original ones.
By changing the phase to package, it should work:
<execution>
<phase>package</phase>
<goals>
<goal>compress</goal>
</goals>
Now the compression is done after the files where copied to target in order to build the WAR content.
The reason why this is happening is that only compressing files without concatenating them or renaming them with a suffix is not the most common use case for the plugin.
Normally we want to compress and concatenate files into only one file, and give it a new name.
The new name is usually something like originalname-min.css / original.name-min.js where .min is the suffix, so removing the nosuffix option on the config above would also work.
Edit: Log example
[INFO] --- yuicompressor-maven-plugin:1.1:compress (default) # yui-compressor-test -
[INFO] prettify.css (817b) -> prettify.css (617b)[75%]
[INFO] total input (1510b) -> output (1134b)[75%]
as stated above maven-war-plugin overwrites files created by minify plugin.
it seems that this cannot be changed. however wanted behavior may be achieved by simply changing project structure.
here example what I did with my project. I'm using minify-maven-plugin and spring framework, static files are stored in static directory.
1) move static directory from src/main/webapp to src/main/resources
2) change minify plugin config in pom.xml. so source points to resources and target points to classes:
<webappSourceDir>src/main/resources</webappSourceDir>
<webappTargetDir>target/classes</webappTargetDir>
<jsSourceDir>static</jsSourceDir>
<cssSourceDir>static</cssSourceDir>
3) change spring config. so spring is serving static files from class path:
<mvc:resources location="classpath:/static/" mapping="/static/**"/>
and now
mvn clean && mvn package
produces correct war with minified files inside /WEB-INF/classes/static
You should take a look to Minify Maven Plugin which sounds like the thing you need.
Let me know if you need any help configuring it.

Best way to convert existing java projects to osgi bundles

We have lot of components out of which we want to modularize only a few to start with. Wondering what is the best way (in the context of my build environment) to create bundles out of all these components?
My environment: Java 6, Maven 2.2.1, Hudson
Technologies: Spring 3.0.5, WebSphere 7, Hibernate 3.2.x and most of apache commons.
Requirements
Modularize only few components. Rest of the components can export all of the packages.
When imported into eclipse, I should be able to see the bundles of imported-packages as dependencies in build path (mvn eclipse:eclipse doesn't seem to do this)
Start by only changing the MANIFEST.MF entries such that all your artifacts become bundles - they obviously won't magically work but it's a good non-destructive first step.
When using the maven-bundle-plugin ensure you set extensions and supportedProjectTypes as you may have problems with CI builds, Maven repos and m2e failing if the packaging type is bundle (see end).
Test your riskiest/core external dependencies early on - for example if you're using JPA for persistence then ensure that the provider works in an OSGi environment with your domain bundle and JDBC driver.
If you're migrating from Java EE/spring look at Karaf or Virgo. But if your components are for embedded systems or have no external dependencies the Felix or Equinox may be enough (though check out the pax-url project if that's the case).
Might be worth editing your question to be a bit more specific about the domain/technologies?
eclipse:eclipse only generates that when the project is first configured, m2e's lifecycle problems might be a bit of pain but it's far better than using the old eclipse plug.
The following will add manifest entries to your existing artifacts without changing them in any other way. It tells the standard maven jar and war plugins to use the MANIFEST.MF generated by maven-bundle-plugin.
Put this in the parent POM:
<pluginManagement>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>2.3.7</version>
<extensions>true</extensions>
<configuration>
<archive>
<addMavenDescriptor>true</addMavenDescriptor>
</archive>
<supportedProjectTypes>
<supportedProjectType>jar</supportedProjectType>
<supportedProjectType>war</supportedProjectType>
</supportedProjectTypes>
<instructions>
<Built-By>${project.organization.name}</Built-By>
<Bundle-Vendor>${project.organization.name}</Bundle-Vendor>
<Bundle-ContactAddress>${project.organization.url}</Bundle-ContactAddress>
<Bundle-Description>${project.description}</Bundle-Description>
<Bundle-DocURL>${bundle.doc.url}</Bundle-DocURL>
<Bundle-Category>${bundle.category}</Bundle-Category>
<Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
<Bundle-Version>${project.version}</Bundle-Version>
<Import-Package>*</Import-Package>
<Export-Package>*</Export-Package>
</instructions>
</configuration>
<executions>
<execution>
<id>bundle</id>
<goals>
<goal>manifest</goal>
</goals>
<phase>prepare-package</phase>
<inherited>true</inherited>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.3.1</version>
<configuration>
<archive>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>2.1.1</version>
<executions>
<execution>
<id>create-war</id>
<phase>package</phase>
<goals>
<goal>war</goal>
</goals>
<inherited>true</inherited>
</execution>
</executions>
<configuration>
<archive>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
</pluginManagement>
Then in child POMs, you may simply do:
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
</plugin>
<!-- Below is mutually exclusive: Either jar or war plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
</plugin>
</plugins>
</build>
Take a look at bndtools, it has excellent support for creating projects that wrap bundles. It provides a lot of insight how JARs are structured and how they depend on other things.
Use the maven bundle plugin. It will add the required import and export statements to you manifest based on scanning your code and the dependencies defined in the pom. This will require the least amount of effort to convert.
I also recommend you use M2Eclipse instead of mvn eclipse:eclipse. It will keep your maven config and workspace in sync.

log4j configuration file in a multi-module Maven project

I am working on a multi-module Maven project, whose structure is like this:
war-module
jar-module
The war-module depends on the jar-module, and will add the jar artifact into the webapp's lib directory after packaging.
And both the war-module and jar-module use Apache log4j for logging, and share the same log4j configuration file (log4j.xml), which locates in jar-module project at present. And this log4j.xml will be packaged into jar-module.jar file, however, I would like to make it into WEB-INF/classes directory in the war package rather than in the jar file so that users will be easy to find this configuration file and modify it if necessary (it is very hard for them to find it if this file is in the WEB-INF/lib/jar-module.jar because there are many other jars under that directory).
My question is: what is the Maven way to solve this problem?
Update:
My real project is a bit more complex, and there is a ear-module which depends on the jar-module too (aka. the jar-module can be used independently in several different projects, and I cannot just put the file into war-module/src/main/resources directory to fix this problem). And I don't want to duplicate some configuration files such as log4j.xml (and other configuration files such as myapp.properties) across the several projects.
I found the answer via some more searching on the web.
Generally, there are three ways to share resources in a multi module Maven project:
Cut and paste them.
Use Assembly and Dependency plugins
Use the maven-remote-resources-plugin
Here's a blog post from Sonatype, the company behind Maven, on sharing resources across projects in Maven, and it is the exact answer I need:
http://www.sonatype.com/people/2008/04/how-to-share-resources-across-projects-in-maven/
In the jar module, exclude the file from the jar:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.3.1</version>
<configuration>
<excludes>
<exclude>log4j.xml</exclude>
</excludes>
</configuration>
</plugin>
Use the buildhelper plugin to attach the log4j.xml to the build as a separate artifact
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>1.5</version>
<executions>
<execution>
<id>attach-artifacts</id>
<phase>package</phase>
<goals>
<goal>attach-artifact</goal>
</goals>
<configuration>
<artifacts>
<artifact>
<file>${project.build.outputDirectory}/log4j.xml</file>
<type>xml</type>
<classifier>log4j</classifier>
</artifact>
</artifacts>
</configuration>
</execution>
</executions>
</plugin>
Now in your war artifact, copy the xml to the output directory:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy</id>
<phase>prepare-package</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>${project.groupId}</groupId>
<artifactId>your.jar.project.artifactId</artifactId>
<version>${project.version}</version>
<type>xml</type>
<classifier>log4j</classifier>
<outputDirectory>${project.build.outputDirectory}
</outputDirectory>
<destFileName>log4j.xml</destFileName>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
But of course it would be easier to just put the file in [web-artifact]/src/main/resources in the first place :-)
From my experience this can be implemented in an easy way , just :
Make the log4j configuration resource at the parent module.
Call the dependency of log4j in all pom modules.
Detailed steps:
Create a project with common-config
In the src/main/resources folder put the log4j or logback config file
Install the artifact in the local Maven repository to be used by other projects
Add the dependency as a normal Maven dependency in the subsequent projects
<dependency>
<groupId>com.your.group.id</groupId>
<artifactId>common-config</artifactId>
<scope>provided</scope>
</dependency>
I prefer to use the scope provided and provide the file from a classpath config folder at runtime.

Building non-webapp maven2 based project with extra non-essential files and batch files

I'm just beginning to grasp the setup of maven2 while porting over a simple project of mine. I've ran through the package command line example on the Sonatype web site but I'm a bit confused of how I could expand and change the packaging of this project. I've tried to find more information on this subject but I think I'm either thinking about the problem wrong or phrasing the searches wrong.
Essentially I want to build a project that would be a zip of all the dependency jars, the main jar itself, batch scripts to start it for convenience and maybe other various files for the application (like properties files or something). However, I don't want these all bundled into the jar. I'd like to just have a compressed archive build of the project with a lib directory of all the jars, the root containing properties files and batch scripts and maybe sub folders to hold extra non-essential files. Kind of like:
sample-proj.zip:
easy.bat
props.ini
lib
dependent1.jar
dependent2.jar
main.jar
sub_dir
someextrafile.txt
What's the correct way of building a project like this using maven2? Do I need to make a parent project that builds the zip and includes the sub project as a module? This would be just a simple project that wouldn't need to be multi-module...
Building on matt b's answer. Here's some examples of how to use the referenced plugins. Note the executions are bound to the integration-test phase to ensure the jar has been created before attempting to package it.
The appassembler-maven-plugin will generate batch/shell files, download the dependencies (in this example to the lib directory), and place all the contents in target/appassembler
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>appassembler-maven-plugin</artifactId>
<version>1.0</version>
<executions>
<execution>
<id>assemble-standalone</id>
<phase>integration-test</phase>
<goals>
<goal>assemble</goal>
<!--if you only want to create the repo and skip script generation,
use this goal instead-->
<!--goal>create-repository</goal-->
</goals>
<configuration>
<programs>
<program>
<mainClass>name.seller.rich.Foo</mainClass>
<name>foo</name>
</program>
</programs>
<platforms>
<platform>windows</platform>
<platform>unix</platform>
</platforms>
<repositoryLayout>flat</repositoryLayout>
<repositoryName>lib</repositoryName>
</configuration>
</execution>
</executions>
</plugin>
The assembly plugin can be used to package the appassembler output into a zip:
<profiles>
<profile>
<id>archive</id>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2-beta-2</version>
<executions>
<execution>
<phase>integration-test</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptors>
<descriptor>src/main/assembly/archive.xml</descriptor>
</descriptors>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
And the assembly descriptor looks something like this:
<assembly>
<id>archive</id>
<formats>
<format>zip</format>
</formats>
<fileSets>
<fileSet>
<directory>${project.build.directory}/appassembler</directory>
<outputDirectory>/</outputDirectory>
</fileSet>
</fileSets>
<!-- add any other filesets to include docs, readmes, licences etc here -->
</assembly>
The appassembler plugin does exactly what you describe: create shell scripts, and package up the dependencies.
However I don't believe that it packages them up into a zip/tar.gz/etc (appassembler just puts them into a folder structure under target/appassembler. But, you can use the maven-assembly plugin in concert with appassembler to just package up that output - maven-assembly does an excellent job of producing a zip/jar/tar.gz/bzip2/whatever, and you can control what goes into the file completely with an XML descriptor.

Categories

Resources