1

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:

  1. Install Microsoft Visual C++ Redistributable for Visual Studio 2019 (required according to the installation instructions).

  2. Downloaded and extracted the OR-Tools library for Java (included 2 jars and a 1 dll file).

  3. In Intellij, I added those jars as module dependencies (under a folder called lib).

  4. Added the lib library path to VM options in Intellij run configurations.

  5. 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:

enter image description here

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
forhas
  • 11,551
  • 21
  • 77
  • 111

2 Answers2

0

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.

forhas
  • 11,551
  • 21
  • 77
  • 111
k88
  • 1,858
  • 2
  • 12
  • 33
0

I tried:

First add the jniortools library to java.library.path pragmatically:

static {
    String orToolsDllLibrary = System.getProperty("user.dir") + "\\lib";
    addLibraryPath(orToolsDllLibrary);
    System.loadLibrary("jniortools");
}


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);
}

In pom file:

<dependencies>
....

    <dependency>
        <groupId>com.google.protobuf</groupId>
        <artifactId>protobuf-java</artifactId>
        <version>3.12.2</version>
    </dependency>
    <dependency>
        <groupId>com.google</groupId>
        <artifactId>ortools</artifactId>
        <version>0.0.2</version>
    </dependency>
    
    ....
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-install-plugin</artifactId>
            <version>2.5.2</version>
            <executions>
                <execution>
                    <id>install-external</id>
                    <phase>process-resources</phase>
                    <configuration>
                        <file>${basedir}/lib/com.google.ortools.jar</file>
                        <repositoryLayout>default</repositoryLayout>
                        <groupId>com.google</groupId>
                        <artifactId>ortools</artifactId>
                        <version>0.0.2</version>
                        <packaging>jar</packaging>
                        <generatePom>true</generatePom>
                    </configuration>
                    <goals>
                        <goal>install-file</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-antrun-plugin</artifactId>
            <version>1.7</version>
            <executions>
                <execution>
                    <phase>generate-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>
forhas
  • 11,551
  • 21
  • 77
  • 111