do not pack specified resources in jar - java

I have a maven2 project, consisting of root project (with pom packaging) and a set of modules having dependencies on each other. The project is a library with a set of apps built on top of this library. Now the problem.
My library uses some resources which cannot be packed in jar - namely some sqlite databases, and I can't find a way to put it near the jar instead of inside it, and to buldle library this way to dependent applications.
Thanks. Any ideas?

Create a custom assembly to distribute the project as an archive (e.g. a zip or tar.gz) is clearly the way to go here.
To customize the way the Assembly Plugin creates your assemblies, you'll need to provide your custom descriptor (this gives you all the flexibility you need). Then, to build the assembly as part of the build, all you have to do is to bind the single or single-directory mojos into the default build lifecycle as explained in the Configuration and Usage of the plugin's documentation.
Another great resource is Sonatype's book which has an entire chapter dedicated to assemblies: see Chapter 14. Maven Assemblies .

Sounds like you could use the maven assembly plugin to create a distribution file of your choice (zip, jar, tar...) which would include the extra resources.

Here is the important fact from Maven: The Complete Reference's Assemblies Chapter Section 8.3.2:
When you generate assemblies as part
of your normal build process, those
assembly archives will be attached to
your main project’s artifact. This
means they will be installed and
deployed alongside the main artifact,
and are then resolvable in much the
same way. Each assembly artifact is
given the same basic coordinates
(groupId, artifactId, and version) as
the main project. However, these
artifacts are attachments, which in
Maven means they are derivative works
based on some aspect of the main
project build. To provide a couple of
examples, source assemblies contain
the raw inputs for the project build,
and jar-with-dependencies assemblies
contain the project’s classes plus its
dependencies. Attached artifacts are
allowed to circumvent the Maven
requirement of one project, one
artifact precisely because of this
derivative quality.
Since assemblies are (normally)
attached artifacts, each must have a
classifier to distinguish it from the
main artifact, in addition to the
normal artifact coordinates. By
default, the classifier is the same as
the assembly descriptor’s identifier.
When using the built-in assembly
descriptors, as above, the assembly
descriptor’s identifier is generally
also the same as the identifier used
in the descriptorRef for that type of
assembly.
It is important to understand that while most Maven projects only generate a single artifact it is possible to generate more than one and use the classifier coordinate to associate these artifacts with the same GAV coordinate. In your case, you'll want to attach an assembly plugin's "single" goal using something similar to this:
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2-beta-2</version>
<dependencies>
<dependency>
<groupId>org.sonatype.mavenbook.assemblies</groupId>
<artifactId>web-fragment-descriptor</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<executions>
<execution>
<id>assemble</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptorRefs>
<descriptorRef>web-fragment</descriptorRef>
</descriptorRefs>
</configuration>
</execution>
</executions>
</plugin>
You can attach as many of these executions as you wish, but once you have more than one execution for a particular plugin, each execution will require a unique "id" element. The "single" goal in the Maven Assembly plugin does the same thing that that the "assembly" goal does except it was designed to be bound to the lifecycle.
The other part of you question is about excluding specific resources from a JAR, you can accomplish this by excluding resources in your POM.

Related

Difference between maven plugins ( assembly-plugins , jar-plugins , shaded-plugins)

I am a beginner in maven and now I'm confused with the difference between these maven plugins. Is these all create jar files? now my questions are
what's the difference between the jar created in each plugins.( assembly plugin, jar-plugin, shaded plugin)
The purpose of each plugin. ( assembly, jar plugin, shaded plugin )
I know even without specifying any of these plugins once type mvn package there will be a jar output. What is the difference of the output jar without these plugins and the output jar with these plugins?. TIA
maven-jar-plugin: This plugin provides the capability to build and sign JARs. But it just compiles the java files under src/main/java and src/main/resources/. It doesn't include the dependencies JAR files.
maven-assembly-plugin: This plugin extracts all dependency JARs into raw classes and groups them together. It can also be used to build an executable JAR by specifying the main class. It works in project with less dependencies only; for large project with many dependencies, it will cause Java class names to conflict.
maven-shade-plugin: It packages all dependencies into one uber-JAR. It can also be used to build an executable JAR by specifying the main class. This plugin is particularly useful as it merges content of specific files instead of overwriting them by relocating classes. This is needed when there are resource files that have the same name across the JARs and the plugin tries to package all the resource files together.
Refer: comparison:maven plugin jar,assembly,shade
Jar plugin
Let's see what the following command can tell.
mvn help:describe -Dplugin=org.apache.maven.plugins:maven-jar-plugin
It has 3 goals, help, jar and test-jar. I believe you are mostly interested in the jar goal, which according to the description does the following:
Build a JAR from the current project.
As a side note, executing mvn help:effective-pom on a project with packaging set to jar, shows that this plugin is automatically configured and gets executed during the package phase.
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<executions>
<execution>
<id>default-jar</id>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
Assembly plugin
This one serves a different purpose. It has 8 goals, but 6 of them are deprecated. So apart from the help goal, this leaves us with the single goal.
mvn help:describe -Dplugin=org.apache.maven.plugins:maven-assembly-plugin
Assemble an application bundle or distribution from an assembly descriptor. This goal is suitable either for binding to the lifecycle or calling directly from the command line (provided all required files are available before the build starts, or are produced by another goal specified before this one on the command line).
You may use the assembly plugin when you want to deliver more than your project's artifact (JAR, WAR, etc.), but the configuration goes in another file.
Shade plugin
The description of the main goal is a bit disappointing.
mvn help:describe -Dplugin=org.apache.maven.plugins:maven-shade-plugin
Mojo that performs shading delegating to the Shader component.
You mostly want to use this plugin if you want to produce an uber-jar, which is your artifact in a JAR with all its transitive dependencies in it.
Basicly, if you're building a library, you'll stick with the default JAR plugin. If you're building an application, you could consider using the shade plugin, though to me, it's kind of quick and dirty. If uber-jar is not your taste or the distribution cannot fit inside a single JAR (external configuration, native dependencies, etc.) then you should go for the assembly plugin.

Deploying a library to Maven repo when it depends on non-Maven libraries

I run an open source library and am considering having it fully embrace Maven and upload it to to a central repository so that people can easily add it to their projects.
The problem is that it depends on a couple of older libraries that do not exist on any Maven repos. Currently, that means a pom file has to use the system scope of the dependency. I've also read about creating a local repository for the project to install the 3rd party libraries.
However, my impression is that neither of these approaches will work well when I deploy my library to a Maven repository. That is, if it depends on external "system" or local repositories, then when someone adds my library to their pom file, they're not actually done. They also have to download the 3rd party libraries and manually install them or add them to their own local repository.
What I'd like to have happen is for these couple of 3rd party libraries to simply be included in the jar file that I deploy to the central repository. That way, if someone adds my library to their pom file, they really are done and don't have to worry about getting and installing the 3rd party libraries. Is it possible to do that?
First off, I'll start by saying that you should back away as far as possible from the system scope. You can refer to this answer for more information.
A way to circumvent your problem is indeed to include in the deployed JAR all the libraries that aren't present in Maven Central. (Let's say you have installed those libraries in your local repository.) You can do that with the maven-shade-plugin, which is a plugin used to create uber jars. The attribute artifactSet controls what will be included in the shaded JAR. In the following snippet, only the two dependencies groupId:artifactId and groupId2:artifactId2 will be included.
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<artifactSet>
<includes>
<include>groupId:artifactId</include>
<include>groupId2:artifactId2</include>
</includes>
</artifactSet>
</configuration>
</execution>
</executions>
</plugin>
By default, the shaded JAR will replace your main artifact and this will be the JAR that will be deployed. The POM that will be deployed will not contain the dependency entries for the artifacts that were included in the shaded JAR, as such, a client depending on your deployed artifact won't be transitively-depending on them.

Dependency in Maven

I am really new to maven. I am bit confused about the dependency feature. I know that I can add dependency in the pom file like this
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.6.1</version>
</dependency>
What does this actually mean? Does it mean that I dont need to import the slf4j jar files into my project? If so how does my project get access to those libraries?
I have read about dependency from maven site but didnt help me much.
Can some one explain it in a simpler way.
Thanks
Nutshell: It means your project has a dependency on slf4j, version 1.6.1.
Furthermore:
If you build your project with Maven (or your IDE is Maven-aware), you don't have to do anything else in order to use slf4j. (Aside from normal source-code considerations, like a reasonable import statement, etc.)
slf4j v. 1.6.1 will be retrieved from a default Maven repository to your local repository, meaning...
... ~/.m2/repository is your repository. slf4j will be put in $M2_HOME/org/slf4j/$(artifactId}/1.6.1 and will include (in general) a jar file, a pom file, and a hash file.
Slf4j's dependencies will be downloaded into your local repository as well.
Dependencies of those dependencies will be downloaded ad infinitum/ad nauseum. (The source of "first use of a library downloads the internet" jokes if there are a lot of dependencies; not the case for slf4j.) This is "transitive dependency management"--one of Maven's original purposes.
If you were not using maven, you would manually download and use the dependencies that you needed for your project. You would probably place them in a lib folder and specify this location in your IDE as well as your build tool.
maven manages these dependencies for you. You specify the dependency your project needs in the prescribed format and maven downloads them for you from the internet and manages them. When building your project, maven knows where it has placed these dependencies and uses them. Most IDEs also know where these dependencies are, when they discover that it is a maven project.
Why is this a big deal? Typically most open source libraries release newer versions on a regular basis. If your project uses these, then each time a newer version is needed, you would need to manually download it and manage it. More importantly, each dependency, in turn may have other dependencies (called transitive dependency). If you do not use maven, you would need to identify, download and manage these transitive dependencies as well.
It becomes complex the more such dependencies that your project uses. It is possible that two dependencies end up using different versions of a dependency common to them.
When compiling your project, Maven will download the corresponding .jar file from a repository, usually the central repository (you can configure different repositories, either for mirroring or for your own libraries which aren't available on the central repositories).
If your IDE know about Maven, it will parse the pom and either download the dependencies itself or ask Maven to do so. Then it will open the dependencies' jars, and this is how you get autocompletion: the IDE "imports" the jars for you behind the scenes.
The repository contains not only the ".jar" file for the dependency, but also a ".pom" file, which describes its dependencies. So, maven will recursively download its dependencies, and you will get all the jars you need to compile your software.
Then, when you will try to run your software, you will have to tell the JVM where to find these dependencies (ie, you have to put them on the class path).
What I usually do is copy the dependencies to a target/lib/ directory, so it is easy to deploy the software and to launch it. To do so, you can use the maven-dependency-plugin, which you specify in the <build>:
<build>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</build>
There are a variety of servers on the internet that host artifacts (jars) that you can download as part of a maven build. You can add dependencies like you show above to describe what jars you need in order to build your code. When maven goes to build, it will contact one of these servers and download the jar to your computer and place it in a local repository usually
${user_home}/.m2/repository
The servers that maven contacts must be configured in your maven project pom file, under a section like
<repositories>
<repository>
</repository>
</repositories>
The prototypical server can be seen at repo1.maven.org
The nice thing about maven is that if a jar you list is needed, it will pull not only that jar, but any jars that that jar needs. Obviously, since you are pulling the jars to your machine, it only downloads them when it can't find them on your machine, thus not slowing down your build everytime (just the first time).

dependency not found at runtime maven

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).

Where in maven project's path should I put configuration files that are not considered resources

I have a simple java maven project. One of my classes when executing needs to load an xml configuration file from the classpath. I don't want to package such xml file when producing the jar but I want to include a default xml file in a zip assembly under a conf subfolder and I also want this default xml to be available in the unit tests to test against it.
As I see it there are 2 possible places of this default xml:
src/main/resources/conf/default.xml
src/main/conf/default.xml
Both solutions demand special pom actions:
In solution 1, I get the auto copy to target folder during build which means it is available in testing but I also get it in the produced jar which i don't want.
In solution 2, I get the jar as I want it(free of the xml) but I manually have to copy the xml to the target folder to be available for testing. (I don't want to add src's subfolders in test classpath. I think it is bad practice).
Question: what is the best solution of the two?
- If the correct is 2, what is the best way to copy it to target folder?
- Is there any other solution better and more common than those two?
(I also read Where should I put application configuration files for a Maven project? but I would like to know the most "correct solution" from the "convention over configuration" point of view and this link provides some configuration type solutions but not any convention oriented. Maybe there isn't one but I ask anyway. Also the solutions provided include AntRun plugin and appAssembler plugin and I wonder if I could do it with out them.)
The question is what is the best solution of the two? If the correct is 2, what is the best way to copy it to target folder? Is there any other solution better and more common than those two?
Since you want that file to be copied to the target/classes folder, it has somehow to be considered as a resource (so either put in under src/main/resources or declare src/main/conf as resource directory). And if you don't want it in the final jar, configure the Maven JAR Plugin to exclude it:
<project>
...
<build>
<plugins>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.3.1</version>
<configuration>
<excludes>
<exclude>**/conf/*</exclude>
</excludes>
</configuration>
</plugin>
...
</plugins>
</build>
...
</project>
For the assembly part, assembly descriptors are pretty flexible so it should be possible to achieve what you want regardless of the choice. I'd suggest using the easiest setup though.
My solution was to use two profiles: Development (default) and Packaging
My default / section contains both src/main/resources and src/main/conf. I call this my Development profile, which is an implicit profile.
My packaging profile is an explicit profile which is defined under section. There under / I only mentioned src/main/resources. When I'm running my packaging script (we currently have this external to maven since its building an RPM out of our WAR), I'm running 'mvn install -Drpm' to activate my Packaging profile (rpm is the id for the Packaging profile.
If this wasn't clear enough, feel free to ask more questions.
You could place it in src/test/conf/default.xml. Your testclasses can find it, but it wont be packaged using the standard method.
With an additional assembly you can package it from there. That step is always necessary.
A different solution could be to create a separate maven module and place it in /src/main/resources/conf/... .Then make this jar a test dependency. You do not need to do any special plugin configuration, but I think it is overkill for a single file.
If your packaging is war, you can use the packagingExcudes configuration option of the maven-war-plugin:
<project>
...
<build>
<plugins>
...
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
<configuration>
<!-- Exclude abc.properties found in src/main/resources/ (ends up getting packaged in WEB-INF/classes/) -->
<packagingExcludes>
WEB-INF/classes/abc.properties
</packagingExcludes>
</configuration>
</plugin>
...
</plugins>
</build>
...
</project>
Use commas to separate between multiple resources you want to exclude. Also, you can use wildcards and regex in your excluded paths. For regex, it's in the %regex[YOUR_REGEX_HERE] syntax. Check the documentation for more details.

Categories

Resources