240

I have a proprietary jar that I want to add to my pom as a dependency.

But I don't want to add it to a repository. The reason is that I want my usual maven commands such as mvn compile, etc, to work out of the box. (Without demanding from the developers a to add it to some repository by themselves).

I want the jar to be in a 3rdparty lib in source control, and link to it by relative path from the pom.xml file.

Can this be done? How?

Bozho
  • 554,002
  • 136
  • 1,025
  • 1,121
flybywire
  • 232,954
  • 184
  • 384
  • 491

9 Answers9

355

I want the jar to be in a 3rdparty lib in source control, and link to it by relative path from the pom.xml file.

If you really want this (understand, if you can't use a corporate repository), then my advice would be to use a "file repository" local to the project and to not use a system scoped dependency. The system scoped should be avoided, such dependencies don't work well in many situation (e.g. in assembly), they cause more troubles than benefits.

So, instead, declare a repository local to the project:

<repositories>
  <repository>
    <id>my-local-repo</id>
    <url>file://${project.basedir}/my-repo</url>
  </repository>
</repositories>

Install your third party lib in there using install:install-file with the localRepositoryPath parameter:

mvn install:install-file -Dfile=<path-to-file> -DgroupId=<myGroup> \ 
                         -DartifactId=<myArtifactId> -Dversion=<myVersion> \
                         -Dpackaging=<myPackaging> -DlocalRepositoryPath=<path>

Update: It appears that install:install-file ignores the localRepositoryPath when using the version 2.2 of the plugin. However, it works with version 2.3 and later of the plugin. So use the fully qualified name of the plugin to specify the version:

mvn org.apache.maven.plugins:maven-install-plugin:2.3.1:install-file \
                         -Dfile=<path-to-file> -DgroupId=<myGroup> \ 
                         -DartifactId=<myArtifactId> -Dversion=<myVersion> \
                         -Dpackaging=<myPackaging> -DlocalRepositoryPath=<path>

maven-install-plugin documentation

Finally, declare it like any other dependency (but without the system scope):

<dependency>
  <groupId>your.group.id</groupId>
  <artifactId>3rdparty</artifactId>
  <version>X.Y.Z</version>
</dependency>

This is IMHO a better solution than using a system scope as your dependency will be treated like a good citizen (e.g. it will be included in an assembly and so on).

Now, I have to mention that the "right way" to deal with this situation in a corporate environment (maybe not the case here) would be to use a corporate repository.

radistao
  • 12,489
  • 8
  • 51
  • 80
Pascal Thivent
  • 535,937
  • 127
  • 1,027
  • 1,106
  • 2
    This is a great idea, but on Maven 2.2.1, the install plugin seems to be ignoring `localRepositoryPath`... – Jake May 27 '10 at 01:32
  • @Jake Indeed, looks like a bug in version 2.2 of the plugin. I've updated my answer with a version that works. – Pascal Thivent May 27 '10 at 01:50
  • 2
    Why declare a local repo? Why not just let it go into ~/.m2/ with the rest. – Leif Gruenwoldt Aug 03 '12 at 18:46
  • 7
    @leif81 Because then the repo and libraries are checked into the SCM repository -> Anyone that does a source checkout has everything they need to build a copy of the library/application. – Darth Android Nov 20 '12 at 19:30
  • 1
    Didn't work when I placed the repo inside the project folder, I had to move it to `basedir/../my-local-repo` to get it to work. – lemon Dec 19 '12 at 11:07
  • 7
    I had the same problem as @lemon, which I fixed by doing `basedir/./my-local-repo` with a single `.` instead. – Brian Sep 13 '13 at 06:29
  • this answer is bad because what user wants is to run mvn install or mvn package out of box and not some other mvn commands before – Enerccio Jun 08 '16 at 11:55
  • @Enerccio You dp not have to run any mvn commands, the repo is checked into the SCM for you. – user831217 Apr 25 '18 at 08:25
  • So from a maven perspective, it seems to find the dependencies and so on, but in Eclipse I cannot run it any more due to a `java.lang.NoClassDefFoundError`. Do I have to change any settings in the IDE as well? – Janothan Jul 05 '18 at 08:44
  • 2
    Packaging has to be jar, therefore -Dpackaging=jar – Danila Piatov Jul 10 '18 at 11:45
  • When I build the project it can't find the libs [ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.7.0:compile (default-compile) on project sh: Compilation failure: Compilation failure: [ERROR] /C:/Users/.../User.java:[3,40] package com.roufid.tutorials does not exist – MTZ Sep 04 '18 at 10:36
  • The options `-DgeneratePom=true -DcreateChecksum=true` are also usable. WIthout them, maven generates warnings on installing artifacts from custom local repo into main local repo (`~/.m2`) when it builds the project. – Ruslan Stelmachenko Jan 12 '19 at 03:03
  • I found that as of 3.6.0 Maven understands relative paths (instead of full file URL's) in the repository definition in the parent pom. – Thorbjørn Ravn Andersen Feb 20 '19 at 13:41
133

Using the system scope. ${basedir} is the directory of your pom.

<dependency>
    <artifactId>..</artifactId>
    <groupId>..</groupId>
    <scope>system</scope>
    <systemPath>${basedir}/lib/dependency.jar</systemPath>
</dependency>

However it is advisable that you install your jar in the repository, and not commit it to the SCM - after all that's what maven tries to eliminate.

Hendy Irawan
  • 17,676
  • 10
  • 94
  • 105
Bozho
  • 554,002
  • 136
  • 1,025
  • 1,121
  • 15
    The scope system must be avoided everywhere it is possible. Install the JAR in the repository is a better solution... – Gandalf StormCrow Feb 09 '10 at 14:39
  • 14
    yes, if possible. he said explicitly that he doesn't want to put it in the repository. I added a comment to point out that this is not a good practice. But it does work. – Bozho Feb 09 '10 at 14:41
  • groovy, you solution is the most acceptable so far I guess .. I totally misread the question – ant Feb 09 '10 at 14:43
  • Yes - the question itself excludes the best answer. Putting everything in your *single* source control server has little to do with "building out of the box"; rather, everything just has to be "controlled". Do check-in pom's & settings.xml (pointing to the *internal* repo), and use *two* servers for your project: (1) source control, (2) generated artifact control. It makes about as much sense checking in jars as it does checking in dll's (my old corp actually did check-in jars & lib.a/.so/.dll's. Our p4 server was so slow afterwards, some secretly used hg for day-to-day work. Problem solved? – michael May 27 '11 at 20:57
  • any way to specify a directory that contains jars instead so we dont' have to add each and every one just like gradle can do it? – Dean Hiller Jan 04 '13 at 19:46
  • like that - no. you'd need them in your repository (as Pascal showed) – Bozho Jan 04 '13 at 21:09
  • It didn't work now. Now you need to use this method http://stackoverflow.com/a/31023523/5492006 – aaroh Feb 24 '16 at 10:26
  • You need to add the version (any) as well, otherwise won't build. For instance: 1.0 – Enrico Giurin Jan 16 '17 at 08:15
  • is base directory defined somewhere? – Steven Smart Oct 29 '19 at 04:03
29

This is another method in addition to my previous answer at Can I add jars to maven 2 build classpath without installing them?

This will get around the limit when using multi-module builds especially if the downloaded JAR is referenced in child projects outside of the parent. This also reduces the setup work by creating the POM and the SHA1 files as part of the build. It also allows the file to reside anywhere in the project without fixing the names or following the maven repository structure.

This uses the maven-install-plugin. For this to work, you need to set up a multi-module project and have a new project representing the build to install files into the local repository and ensure that one is first.

You multi-module project pom.xml would look like this:

<packaging>pom</packaging>
<modules>
<!-- The repository module must be first in order to ensure
     that the local repository is populated -->
    <module>repository</module>
    <module>... other modules ...</module>
</modules>

The repository/pom.xml file will then contain the definitions to load up the JARs that are part of your project. The following are some snippets of the pom.xml file.

<artifactId>repository</artifactId>
<packaging>pom</packaging>

The pom packaging prevents this from doing any tests or compile or generating any jar file. The meat of the pom.xml is in the build section where the maven-install-plugin is used.

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-install-plugin</artifactId>
            <executions>
                <execution>
                        <id>com.ibm.db2:db2jcc</id>
                        <phase>verify</phase>
                        <goals>
                            <goal>install-file</goal>
                        </goals>
                        <configuration>
                            <groupId>com.ibm.db2</groupId>
                            <artifactId>db2jcc</artifactId>
                            <version>9.0.0</version>
                            <packaging>jar</packaging>
                            <file>${basedir}/src/jars/db2jcc.jar</file>
                            <createChecksum>true</createChecksum>
                            <generatePom>true</generatePom>
                        </configuration>
                </execution>
                <execution>...</execution>
            </executions>
        </plugin>
    </plugins>
</build>

To install more than one file, just add more executions.

Community
  • 1
  • 1
Archimedes Trajano
  • 22,850
  • 10
  • 113
  • 154
  • This is the only thing which worked for my multi-module project. The local approach for an unknown reason didn't work. So thanks! – Lonzak Oct 06 '17 at 17:11
11

This is working for me: Let's say I have this dependency

<dependency>
    <groupId>com.company.app</groupId>
    <artifactId>my-library</artifactId>
    <version>1.0</version>
    <scope>system</scope>
    <systemPath>${project.basedir}/lib/my-library.jar</systemPath>
</dependency>

Then, add the class-path for your system dependency manually like this

<Class-Path>libs/my-library-1.0.jar</Class-Path>

Full config:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <version>2.4</version>
    <configuration>
        <archive>
            <manifestEntries>
                <Build-Jdk>${jdk.version}</Build-Jdk>
                <Implementation-Title>${project.name}</Implementation-Title>
                <Implementation-Version>${project.version}</Implementation-Version>
                <Specification-Title>${project.name} Library</Specification-Title>
                <Specification-Version>${project.version}</Specification-Version>
                <Class-Path>libs/my-library-1.0.jar</Class-Path>
            </manifestEntries>
            <manifest>
                <addClasspath>true</addClasspath>
                <mainClass>com.company.app.MainClass</mainClass>
                <classpathPrefix>libs/</classpathPrefix>
            </manifest>
        </archive>
    </configuration>
</plugin>
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-dependency-plugin</artifactId>
    <version>2.5.1</version>
    <executions>
        <execution>
            <id>copy-dependencies</id>
            <phase>package</phase>
            <goals>
                <goal>copy-dependencies</goal>
            </goals>
            <configuration>
                <outputDirectory>${project.build.directory}/libs/</outputDirectory>
            </configuration>
        </execution>
    </executions>
</plugin>
RufusSC2
  • 191
  • 1
  • 7
9

I've previously written about a pattern for doing this.

It is very similar to the solution proposed by Pascal, though it moves all such dependencies into a dedicated repository module so that you don't have to repeat it everywhere the dependency is used if it is a multi-module build.

gunr2171
  • 10,315
  • 25
  • 52
  • 75
Brett Porter
  • 5,597
  • 23
  • 24
7

Basically, add this to the pom.xml:

...

<repositories>
   <repository>
       <id>lib_id</id>
       <url>file://${project.basedir}/lib</url>
   </repository>
</repositories>

...

<dependencies>
  ...
  <dependency>
      <groupId>com.mylibrary</groupId>
      <artifactId>mylibraryname</artifactId>
      <version>1.0.0</version>
  </dependency>
  ...
</dependencies>
Fulgencio Jara
  • 379
  • 1
  • 6
  • 11
4

we switched to gradle and this works much better in gradle ;). we just specify a folder we can drop jars into for temporary situations like that. We still have most of our jars defined i the typicaly dependency management section(ie. the same as maven). This is just one more dependency we define.

so basically now we can just drop any jar we want into our lib dir for temporary testing if it is not a in maven repository somewhere.

Dean Hiller
  • 17,183
  • 19
  • 103
  • 176
2

One small addition to the solution posted by Pascal

When I followed this route, I got an error in maven while installing ojdbc jar.

[INFO] --- maven-install-plugin:2.5.1:install-file (default-cli) @ validator ---
[INFO] pom.xml not found in ojdbc14.jar

After adding -DpomFile, the problem was resolved.

$ mvn install:install-file -Dfile=./lib/ojdbc14.jar -DgroupId=ojdbc \
   -DartifactId=ojdbc -Dversion=14 -Dpackaging=jar -DlocalRepositoryPath=./repo \
   -DpomFile=~/.m2/repository/ojdbc/ojdbc/14/ojdbc-14.pom
Community
  • 1
  • 1
veeseekay
  • 59
  • 3
0

You can use eclipse to generate a runnable Jar : Export/Runable Jar file

user1942990
  • 64
  • 1
  • 6