java - pom - maven system path absolute path




Can I add jars to maven 2 build classpath without installing them? (16)

Maven2 is driving me crazy during the experimentation / quick and dirty mock-up phase of development.

I have a pom.xml file that defines the dependencies for the web-app framework I want to use, and I can quickly generate starter projects from that file. However, sometimes I want to link to a 3rd party library that doesn't already have a pom.xml file defined, so rather than create the pom.xml file for the 3rd party lib by hand and install it, and add the dependency to my pom.xml, I would just like to tell Maven: "In addition to my defined dependencies, include any jars that are in /lib too."

It seems like this ought to be simple, but if it is, I am missing something.

Any pointers on how to do this are greatly appreciated. Short of that, if there is a simple way to point maven to a /lib directory and easily create a pom.xml with all the enclosed jars mapped to a single dependency which I could then name / install and link to in one fell swoop would also suffice.


Problems of popular approaches

Most of the answers you'll find around the internet will suggest you to either install the dependency to your local repository or specify a "system" scope in the pom and distribute the dependency with the source of your project. But both of these solutions are actually flawed.

Why you shouldn't apply the "Install to Local Repo" approach

When you install a dependency to your local repository it remains there. Your distribution artifact will do fine as long as it has access to this repository. The problem is in most cases this repository will reside on your local machine, so there'll be no way to resolve this dependency on any other machine. Clearly making your artifact depend on a specific machine is not a way to handle things. Otherwise this dependency will have to be locally installed on every machine working with that project which is not any better.

Why you shouldn't apply the "System Scope" approach

The jars you depend on with the "System Scope" approach neither get installed to any repository or attached to your target packages. That's why your distribution package won't have a way to resolve that dependency when used. That I believe was the reason why the use of system scope even got deprecated. Anyway you don't want to rely on a deprecated feature.

The static in-project repository solution

After putting this in your pom:

<repository>
    <id>repo</id>
    <releases>
        <enabled>true</enabled>
        <checksumPolicy>ignore</checksumPolicy>
    </releases>
    <snapshots>
        <enabled>false</enabled>
    </snapshots>
    <url>file://${project.basedir}/repo</url>
</repository>

for each artifact with a group id of form x.y.z Maven will include the following location inside your project dir in its search for artifacts:

repo/
| - x/
|   | - y/
|   |   | - z/
|   |   |   | - ${artifactId}/
|   |   |   |   | - ${version}/
|   |   |   |   |   | - ${artifactId}-${version}.jar

To elaborate more on this you can read this blog post.

Use Maven to install to project repo

Instead of creating this structure by hand I recommend to use a Maven plugin to install your jars as artifacts. So, to install an artifact to an in-project repository under repo folder execute:

mvn install:install-file -DlocalRepositoryPath=repo -DcreateChecksum=true -Dpackaging=jar -Dfile=[your-jar] -DgroupId=[...] -DartifactId=[...] -Dversion=[...]

If you'll choose this approach you'll be able to simplify the repository declaration in pom to:

<repository>
    <id>repo</id>
    <url>file://${project.basedir}/repo</url>
</repository>

A helper script

Since executing installation command for each lib is kinda annoying and definitely error prone, I've created a utility script which automatically installs all the jars from a lib folder to a project repository, while automatically resolving all metadata (groupId, artifactId and etc.) from names of files. The script also prints out the dependencies xml for you to copy-paste in your pom.

Include the dependencies in your target package

When you'll have your in-project repository created you'll have solved a problem of distributing the dependencies of the project with its source, but since then your project's target artifact will depend on non-published jars, so when you'll install it to a repository it will have unresolvable dependencies.

To beat this problem I suggest to include these dependencies in your target package. This you can do with either the Assembly Plugin or better with the OneJar Plugin. The official documentaion on OneJar is easy to grasp.


Maven install plugin has command line usage to install a jar into the local repository, POM is optional but you will have to specify the GroupId, ArtifactId, Version and Packaging (all the POM stuff).


A quick&dirty batch solution (based on Alex's answer):

libs.bat

@ECHO OFF
FOR %%I IN (*.jar) DO (
echo ^<dependency^>
echo ^<groupId^>local.dummy^</groupId^>
echo ^<artifactId^>%%I^</artifactId^>
echo ^<version^>0.0.1^</version^>
echo ^<scope^>system^</scope^>
echo ^<systemPath^>${project.basedir}/lib/%%I^</systemPath^>
echo ^</dependency^>
)

Execute it like this: libs.bat > libs.txt. Then open libs.txt and copy its content as dependencies.

In my case, I only needed the libraries to compile my code, and this solution was the best for that purpose.


A strange solution I found:

using Eclipse

  • create simple (non-maven) java project
  • add a Main class
  • add all the jars to the classpath
  • export Runnable JAR (it's important, because no other way here to do it)
  • select Extract required libraries into generated JAR
  • decide the licence issues
  • tadammm...install the generated jar to your m2repo
  • add this single dependency to your other projects.

cheers, Balint


Even though it does not exactly fit to your problem, I'll drop this here. My requirements were:

  1. Jars that can not be found in an online maven repository should be in the SVN.
  2. If one developer adds another library, the other developers should not be bothered with manually installing them.
  3. The IDE (NetBeans in my case) should be able find the sources and javadocs to provide autocompletion and help.

Let's talk about (3) first: Just having the jars in a folder and somehow merging them into the final jar will not work for here, since the IDE will not understand this. This means all libraries have to be installed properly. However, I dont want to have everyone installing it using "mvn install-file".

In my project I needed metawidget. Here we go:

  1. Create a new maven project (name it "shared-libs" or something like that).
  2. Download metawidget and extract the zip into src/main/lib.
  3. The folder doc/api contains the javadocs. Create a zip of the content (doc/api/api.zip).
  4. Modify the pom like this
  5. Build the project and the library will be installed.
  6. Add the library as a dependency to your project, or (if you added the dependency in the shared-libs project) add shared-libs as dependency to get all libraries at once.

Every time you have a new library, just add a new execution and tell everyone to build the project again (you can improve this process with project hierachies).


For those that didn't find a good answer here, this is what we are doing to get a jar with all the necessary dependencies in it. This answer (https://.com/a/7623805/1084306) mentions to use the Maven Assembly plugin but doesn't actually give an example in the answer. And if you don't read all the way to the end of the answer (it's pretty lengthy), you may miss it. Adding the below to your pom.xml will generate target/${PROJECT_NAME}-${VERSION}-jar-with-dependencies.jar

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-assembly-plugin</artifactId>
            <version>2.4.1</version>
            <configuration>
                <!-- get all project dependencies -->
                <descriptorRefs>
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
                <!-- MainClass in mainfest make a executable jar -->
                <archive>
                  <manifest>
                    <mainClass>my.package.mainclass</mainClass>
                  </manifest>
                </archive>

            </configuration>
            <executions>
              <execution>
                <id>make-assembly</id>
                <!-- bind to the packaging phase -->
                <phase>package</phase> 
                <goals>
                    <goal>single</goal>
                </goals>
              </execution>
            </executions>
        </plugin>

I found another way to do this, see here from a Heroku post

To summarize (sorry about some copy & paste)

  • Create a repo directory under your root folder:
yourproject
+- pom.xml
+- src
+- repo
  • Run this to install the jar to your local repo directory
mvn deploy:deploy-file -Durl=file:///path/to/yourproject/repo/ -Dfile=mylib-1.0.jar -DgroupId=com.example -DartifactId=mylib -Dpackaging=jar -Dversion=1.0
  • Add this your pom.xml:
<repositories>
    <!--other repositories if any-->
    <repository>
        <id>project.local</id>
        <name>project</name>
        <url>file:${project.basedir}/repo</url>
    </repository>
</repositories>


<dependency>
    <groupId>com.example</groupId>
    <artifactId>mylib</artifactId>
    <version>1.0</version>  
</dependency>

I just wanted a quick and dirty workaround... I couldn't run the script from Nikita Volkov: syntax error + it requires a strict format for the jar names.

I made this Perl script which works with whatever format for the jar file names, and it generates the dependencies in an xml so it can be copy pasted directly in a pom.

If you want to use it, make sure you understand what the script is doing, you may need to change the lib folder and the value for the groupId or artifactId...

#!/usr/bin/perl

use strict;
use warnings;

open(my $fh, '>', 'dependencies.xml') or die "Could not open file 'dependencies.xml' $!";
foreach my $file (glob("lib/*.jar")) {
    print "$file\n";
    my $groupId = "my.mess";
    my $artifactId = "";
    my $version = "0.1-SNAPSHOT";
    if ($file =~ /\/([^\/]*?)(-([0-9v\._]*))?\.jar$/) {
        $artifactId = $1;
        if (defined($3)) {
            $version = $3;
        }
        `mvn install:install-file -Dfile=$file -DgroupId=$groupId -DartifactId=$artifactId -Dversion=$version -Dpackaging=jar`;
        print $fh "<dependency>\n\t<groupId>$groupId</groupId>\n\t<artifactId>$artifactId</artifactId>\n\t<version>$version</version>\n</dependency>\n";
        print " => $groupId:$artifactId:$version\n";
    } else {
        print "##### BEUH...\n";
    }
}
close $fh;

Note: When using the System scope (as mentioned on this page), Maven needs absolute paths.

If your jars are under your project's root, you'll want to prefix your systemPath values with ${basedir}.



This is how we add or install a local jar

    <dependency>
        <groupId>org.example</groupId>
        <artifactId>iamajar</artifactId>
        <version>1.0</version>
        <scope>system</scope>
        <systemPath>${project.basedir}/lib/iamajar.jar</systemPath>
    </dependency>

i gave some default groupId and artifactId because they are mandatory :)


This is what I have done, it also works around the package issue and it works with checked out code.

I created a new folder in the project in my case I used repo, but feel free to use src/repo

In my POM I had a dependency that is not in any public maven repositories

<dependency>
    <groupId>com.dovetail</groupId>
    <artifactId>zoslog4j</artifactId>
    <version>1.0.1</version>
    <scope>runtime</scope>
</dependency>

I then created the following directories repo/com/dovetail/zoslog4j/1.0.1 and copied the JAR file into that folder.

I created the following POM file to represent the downloaded file (this step is optional, but it removes a WARNING) and helps the next guy figure out where I got the file to begin with.

<?xml version="1.0" encoding="UTF-8" ?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.dovetail</groupId>
    <artifactId>zoslog4j</artifactId>
    <packaging>jar</packaging>
    <version>1.0.1</version>
    <name>z/OS Log4J Appenders</name>
    <url>http://dovetail.com/downloads/misc/index.html</url>
    <description>Apache Log4j Appender for z/OS Logstreams, files, etc.</description>
</project>

Two optional files I create are the SHA1 checksums for the POM and the JAR to remove the missing checksum warnings.

shasum -b < repo/com/dovetail/zoslog4j/1.0.1/zoslog4j-1.0.1.jar \
          > repo/com/dovetail/zoslog4j/1.0.1/zoslog4j-1.0.1.jar.sha1

shasum -b < repo/com/dovetail/zoslog4j/1.0.1/zoslog4j-1.0.1.pom \
          > repo/com/dovetail/zoslog4j/1.0.1/zoslog4j-1.0.1.pom.sha1

Finally I add the following fragment to my pom.xml that allows me to refer to the local repository

<repositories>
    <repository>
        <id>project</id>
        <url>file:///${basedir}/repo</url>
    </repository>
</repositories>

Using <scope>system</scope> is a terrible idea for reasons explained by others, installing the file manually to your local repository makes the build unreproducible, and using <url>file://${project.basedir}/repo</url> is not a good idea either because (1) that may not be a well-formed file URL (e.g. if the project is checked out in a directory with unusual characters), (2) the result is unusable if this project’s POM is used as a dependency of someone else’s project.

Assuming you are unwilling to upload the artifact to a public repository, Simeon’s suggestion of a helper module does the job. But there is an easier way now…

The Recommendation

Use non-maven-jar-maven-plugin. Does exactly what you were asking for, with none of the drawbacks of the other approaches.


What seems simplest to me is just configure your maven-compiler-plugin to include your custom jars. This example will load any jar files in a lib directory.

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <includes>
                    <include>lib/*.jar</include>
                </includes>
            </configuration>
        </plugin>

You may create local repository on your project

For example if you have libs folder in project structure

  • In libs folder you should create directory structure like: /groupId/artifactId/version/artifactId-version.jar

  • In your pom.xml you should register repository

    <repository>
        <id>ProjectRepo</id>
        <name>ProjectRepo</name>
        <url>file://${project.basedir}/libs</url>
    </repository>
    
  • and add dependency as usual

    <dependency>
        <groupId>groupId</groupId>
        <artifactId>artifactId</artifactId>
        <version>version</version>
    </dependency>
    

That is all.

For detailed information: How to add external libraries in Maven


You really ought to get a framework in place via a repository and identifying your dependencies up front. Using the system scope is a common mistake people use, because they "don't care about the dependency management." The trouble is that doing this you end up with a perverted maven build that will not show maven in a normal condition. You would be better off following an approach like this.







maven-2