Force Maven to execute offline with local repository dependencies in a machine without Internet connection

9 minute read

Debugging an application which runs on a remote Virtual Machine can prove to be very hard, especially if your client has “exceptional” conditions due to security, company or any other psycological measures/policies. For example, your VM can be a Windows machine (yes, Windows servers are an exception themselves and impose many issues and difficulties), it may have no internet connection or accept no traffic other than http and RDP. Typically we as developers are called to confront such installations and make them work. As a paralellism, we are asked to fix a vehicle engine through the exhaust pipe without having access to the hood!

If you are not bored to death yet, you may proceed!

The problem

A few days ago we had to remotely debug a Java Enterprise application inside a VM. The interesting fact was that everything had to be offline. Thus IDE installation, Maven installation and compile a Java project using Maven with no internet connection. Everything had to go fully offline. Really. Trully. Nothing special! (for a non technical person) At first we thought that it would suffice if we just copy the local maven repository from a development machine to the VM. Unfortunately that was not the case and it was not as simple as it looked like.

After investing 3 hours in searching and numerous trial and error attempts we managed to compile the application using Maven. The steps described are performed in 2 phases:

  1. Preparation steps performed in a development machine with internet connection.
  2. Steps performed inside the VM with no internet connection (trully offline).

Inside your development machine

Step 1: Create a new local repository

This new local repository will be used only for the dependencies required by your application to be compiled in offline mode. It is unnecessary to copy your everyday repository with all other redundant dependencies which maybe numerous and great in size.

In order to create a new local repository, navigate to your Maven settings.xml file. By default you can find it in:

  • C:\Users\<your-username>\.m2\settings.xml: in Windows machines
  • /home/<your-username>/.m2/settings.xml: in Linux or *nix machines

Edit the line:

<!-- Point to a new empty directory -->
<localRepository>C:/apps/m2repo</localRepository>-->

Step 2: Run Maven to download dependencies for offline usage

In order to to download dependencies for offline usage, inside our project we execute the following:

mvn dependency:go-offline

Helpful link: StackOverflow #8851424.

Step 3: Run your usual Maven goals

You also have to run your usual Maven goals which you eventually you are going to execute again in the offline VM, like clean, test, package. The reason for that is that Maven did not download the .jars needed for these goals when you run mvn dependency:go-offline. In our case we run:

mvn clean test
mvn clean install
mvn clean package

You will notice that Maven downloads some extra .jars needed to execute its goals:

$ mvn clean install
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building jetsprinkler 2.5.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ jetsprinkler ---
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ jetsprinkler ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 11 resources
[INFO]
[INFO] --- maven-compiler-plugin:2.3.2:compile (default-compile) @ jetsprinkler ---
[INFO] Compiling 64 source files to C:\javaprojects\jetsprinkler\target\classes
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ jetsprinkler ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 8 resources
[INFO]
[INFO] --- maven-compiler-plugin:2.3.2:testCompile (default-testCompile) @ jetsprinkler ---
[INFO] Compiling 15 source files to C:\javaprojects\jetsprinkler\target\test-classes
[INFO]
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ jetsprinkler ---
[INFO] Surefire report directory: C:\javaprojects\jetsprinkler\target\surefire-reports
Downloading from public: https://nexus.manios.org/nexus/repository/public/org/apache/maven/surefire/surefire-junit4/2.12.4/surefire-junit4-2.12.4.pom
Downloaded from public: https://nexus.manios.org/nexus/repository/public/org/apache/maven/surefire/surefire-junit4/2.12.4/surefire-junit4-2.12.4.pom (2.4 kB at 4.2 kB/s)
Downloading from public: https://nexus.manios.org/nexus/repository/public/org/apache/maven/surefire/surefire-providers/2.12.4/surefire-providers-2.12.4.pom
Downloaded from public: https://nexus.manios.org/nexus/repository/public/org/apache/maven/surefire/surefire-providers/2.12.4/surefire-providers-2.12.4.pom (2.3 kB at 18 kB/s)
Downloading from public: https://nexus.manios.org/nexus/repository/public/org/apache/maven/surefire/surefire-junit4/2.12.4/surefire-junit4-2.12.4.jar
Downloaded from public: https://nexus.manios.org/nexus/repository/public/org/apache/maven/surefire/surefire-junit4/2.12.4/surefire-junit4-2.12.4.jar (37 kB at 187 kB/s)

-------------------------------------------------------
 T E S T S
-------------------------------------------------------

Step 4: Copy your local repository to the remote VM

We have to copy the contents of our new local repository (in our case C:/apps/m2repo) to the remote VM with no internet connection. Place the contents of the repository in the respective repository directory.

Inside the offline machine (with no internet connection)

The following steps will be run inside the target machine with no internet connection. Verify that you have cloned your source code somewhere.

Step 5: Remove dependency files that cause trouble in offline mode

When Maven resolves and downloads a dependency from the internet, it stores 2-3 extra files other than the .jars that contain the library, javadoc or the sources. For example for spring-core, the files inside the local repository are:

ls -l ~/.m2/repository/org/springframework/spring-core/4.1.7.RELEASE/
total 1004
-rw-r--r-- 1 bobos bobos     212 Jun 17 13:16 _remote.repositories
-rw-r--r-- 1 bobos bobos 1008584 Jun 17 13:16 spring-core-4.1.7.RELEASE.jar
-rw-r--r-- 1 bobos bobos      40 Jun 17 13:16 spring-core-4.1.7.RELEASE.jar.sha1
-rw-r--r-- 1 bobos bobos    2490 Jun 17 13:16 spring-core-4.1.7.RELEASE.pom
-rw-r--r-- 1 bobos bobos      40 Jun 17 13:16 spring-core-4.1.7.RELEASE.pom.sha1

An here is the peculiar fact:

Although we configure Maven to work offline, when it detects files with the extension *.repositories or *.sha1, it tries to connect to the internet! Consequently the Maven goal we run tries to download the dependency from the internet and it eventually fails after timeout (StackOverflow #33548395). To resolve this issue we have to recursively delete all files with the aforementioned suffixes. Hence, execute:

# cd to repository directory
cd ~/.m2/repository

# Then delete recursively all files
# with the extension `*.repositories` or `*.sha1`
find -iname "*.repositories" -exec rm -f {} \; && \
find -iname "*.sha1" -exec rm -f {} \;

This issue might also be resolved by tweaking the updatePolicy in Maven as mentioned in StackOverflow #33548395 but unfortunately we did not have the time to test it.

Step 6 (optional): Fix parent pom references

In our case, the project had a parent pom:

<parent>
    <groupId>com.airplaneman</groupId>
    <artifactId>super-pom</artifactId>
    <version>1.1.4</version>
</parent>

which could not be downloaded from an external Maven repository. We had the files offline, however this configuration could not work. To make it work we had to add the <relativePath> to the pom file. This is trully a relative path to the project.

Hence our project was placed in:

  • C:/projects/jetsprinkler

and the parent pom was located in

  • C:/Users/cmanios/.m2/repository/com/airplanman/super-pom/1.1.4/super-pom-1.1.4.pom

To reference the parent pom in a relative way, we resulted in the following configuration:

<parent>
    <groupId>com.airplaneman</groupId>
    <artifactId>super-pom</artifactId>
    <version>1.1.4</version>
    <relativePath>../../Users/cmanios/.m2/repository/com/airplanman/super-pom/1.1.4/super-pom-1.1.4.pom</relativePath>
</parent>

For more info regarding parent pom refer to:

Step 7: Configure Maven to work offline

Edit your Maven settings.xml file in the following lines:

<!-- (Optional) Set the path to your local repository -->
<localRepository>/path/to/local/repo</localRepository>

<!-- Run Maven in Offline mode -->
<offline>true</offline>

Step 8: Execute your goals with -o and -nsu flag

It is useful that you run your goals with -o and -nsu flags. According to documentation:

  • -o: Work offline
  • -nsu,--no-snapshot-updates: Suppress SNAPSHOT updates

Thus your goal will look like this:

mvn -nsu -o clean install

Troubleshooting

Cannot access central (https://repo.maven.apache.org/maven2) in offline mode and the artifact org.apache.maven.plugins…

You may step on this error:

[WARNING] The POM for org.apache.maven.plugins:maven-resources-plugin:jar:2.6 is missing, no dependency information available
[INFO] ---------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------
[ERROR] Plugin org.apache.maven.plugins:maven-resources-plugin:2.6 or one of its dependencies could not be resolved: Cannot access central (https://repo.maven.apache.org/maven2) in offline mode and the artifact org.apache.maven.plugins:maven-resources-plugin:jar:2.6 has not been downloaded from it before. -> [Help 1]

if you omit Step 3. Maven tries to download it’s plugins to run the goals but they do not exist.

Non-resolvable parent POM […] and ‘parent.relativePath’ points at no local POM

If you see an error similar to the following:

[INFO] Scanning for projects...
[DEBUG] Verifying availability of C:\Users\admin_manios\.m2\repository\com\airplanman\super-pom\1.1.4\super-pom-1.1.4.pom from [central (https://repo.maven.apache.org/maven2, default, releases)]
[ERROR] [ERROR] Some problems were encountered while processing the POMs:
[FATAL] Non-resolvable parent POM for airplanman.airsprinkler:jetengine:2.5.0-SNAPSHOT: Cannot access central (https://repo.maven.apache.org/maven2) in offline mode and the artifact com.airplaneman:super-pom:pom:1.1.4 has not been downloaded from it before. and 'parent.relativePath' points at no local POM @ line 10, column 10

then it means that either you did not copy the parent pom in your local repository for offline usage, or you did not set correctly the relativePath. Please refer to Step 6.

Maven attempts to download artifacts although we have configure it for offline mode

Either you have not downloaded all your dependencies as described in Step 2 or you have not deleted the files described in Step 5.

Conclusion

I hope this article helps you and you will not waste so much time to do the same. Have a nice day and a wonderful and fruitful life!

Comments