I'm using Google Or-Tools library over a Java-Spring-Boot app, Windows 10 OS and Intellij IDE. To make it work over intellij I did the following:
Install Microsoft Visual C++ Redistributable for Visual Studio 2019 (required according to the installation instructions).
Downloaded and extracted the OR-Tools library for Java (included 2 jars and a 1 dll file).
In Intellij, I added those jars as module dependencies (under a folder called lib).
Added the lib library path to VM options in Intellij run configurations.
Loaded the library statically in my code:
static {System.loadLibrary("jniortools");}
Now I can run the project successfully form Intellij. Next I would like to pack everything to a spring boot jar that can run over any windows machine.
My folders structure is:
My pom file is pretty basic, a few dependencies with a standard spring-boot-maven-plugin:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
As I'm trying to pack the code using mvn install I'm getting package com.google.ortools.sat does not exist
.
How can I make sure maven packs those 3rd party jars to the executable spring-boot jar?
UPDATE
I added to my pom file:
<dependency>
<groupId>com.google.ortools</groupId>
<artifactId>ortools</artifactId>
<version>LATEST</version>
<type>jar</type>
<scope>system</scope>
<systemPath>${project.basedir}/lib/com.google.ortools.jar</systemPath>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>LATEST</version>
<type>jar</type>
<scope>system</scope>
<systemPath>${project.basedir}/lib/protobuf.jar</systemPath>
</dependency>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.7</version>
<executions>
<execution>
<phase>generate-test-sources</phase>
<configuration>
<tasks>
<mkdir dir="${project.basedir}/target/lib"/>
<echo message="Creating lib folder..."/>
<copy todir="${project.basedir}/target/lib">
<fileset dir="${project.basedir}/lib">
<include name="**/**"/>
</fileset>
</copy>
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
In addition adding to library path:
static {
try {
String orToolsDllLibrary = System.getProperty("user.dir") + "\\lib";
addLibraryPath(orToolsDllLibrary);
System.loadLibrary("jniortools");
} catch (Exception e) {
e.printStackTrace();
}
}
public static void addLibraryPath(String pathToAdd) throws Exception {
final Field usrPathsField = ClassLoader.class.getDeclaredField("usr_paths");
usrPathsField.setAccessible(true);
//get array of paths
final String[] paths = (String[]) usrPathsField.get(null);
//check if the path to add is already present
for (String path : paths) {
if (path.equals(pathToAdd)) {
return;
}
}
//add the new path
final String[] newPaths = Arrays.copyOf(paths, paths.length + 1);
newPaths[newPaths.length - 1] = pathToAdd;
usrPathsField.set(null, newPaths);
}
And now when running command java -jar myApp-0.0.1-SNAPSHOT.jar
getting an exception:
Caused by: java.lang.NoClassDefFoundError: com/google/ortools/sat/CpSolver
at java.base/java.lang.Class.getDeclaredMethods0(Native Method) ~[na:na]
at java.base/java.lang.Class.privateGetDeclaredMethods(Class.java:3167) ~[na:na]
at java.base/java.lang.Class.getDeclaredMethods(Class.java:2310) ~[na:na]
at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:463) ~[spring-core-5.2.6.RELEASE.jar!/:5.2.6.RELEASE]
... 29 common frames omitted
Caused by: java.lang.ClassNotFoundException: com.google.ortools.sat.CpSolver
at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:471) ~[na:na]
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:588) ~[na:na]
at org.springframework.boot.loader.LaunchedURLClassLoader.loadClass(LaunchedURLClassLoader.java:129) ~[solver-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521) ~[na:na]
... 33 common frames omitted
I am not sure how you added the library to your project? You don't seem to have done it through Maven, did you?
In the past I took the approach of adding it via using system
scope in Maven (see here. This would give you something like this in your pom:
<dependency>
<groupId>com.google.ortools</groupId>
<artifactId>ortools</artifactId>
<version>1.0</version>
<scope>system</scope>
<systemPath>${basedir}/src/main/resources/lib/com.google.ortools.jar</systemPath>
</dependency>
However, this approach can also be a pain especially if you have to work multi-platform. Recently, I found this repo and that made my life much easier dealing with OR-tools. Hope this helps.
UPDATE: I strongly recommend using the updated method below as it is much less of a headache:
<repositories>
...
<repository>
<id>bintray</id>
<url>https://dl.bintray.com/magneticflux/maven</url>
</repository>
....
</repositories>
<dependencies>
<dependency>
<groupId>com.skaggsm.ortools</groupId>
<artifactId>ortools-natives-all</artifactId>
<version>7.7.7810</version>
</dependency>
<!-- OR-tools needs protobuf -->
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.12.2</version>
</dependency>
</dependencies>
Then you can do a static load of the library:
static {
OrToolsHelper.loadLibrary()
}
Make sure to work with JDK >= 11 as elaborated here.