1 // Copyright 2010-2021 Google LLC
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 //     http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 package com.google.ortools;
15 
16 import com.sun.jna.Platform;
17 import java.io.IOException;
18 import java.net.URI;
19 import java.net.URISyntaxException;
20 import java.net.URL;
21 import java.nio.file.FileSystem;
22 import java.nio.file.FileSystemAlreadyExistsException;
23 import java.nio.file.FileSystems;
24 import java.nio.file.FileVisitResult;
25 import java.nio.file.Files;
26 import java.nio.file.Path;
27 import java.nio.file.SimpleFileVisitor;
28 import java.nio.file.attribute.BasicFileAttributes;
29 import java.util.Collections;
30 import java.util.Objects;
31 
32 /** Load native libraries needed for using ortools-java.*/
33 public class Loader {
34   private static final String RESOURCE_PATH = "ortools-" + Platform.RESOURCE_PREFIX + "/";
35 
36   /** Try to locate the native libraries directory.*/
getNativeResourceURI()37   private static URI getNativeResourceURI() throws IOException {
38     ClassLoader loader = Loader.class.getClassLoader();
39     URL resourceURL = loader.getResource(RESOURCE_PATH);
40     Objects.requireNonNull(resourceURL,
41         String.format("Resource %s was not found in ClassLoader %s", RESOURCE_PATH, loader));
42 
43     URI resourceURI;
44     try {
45       resourceURI = resourceURL.toURI();
46     } catch (URISyntaxException e) {
47       throw new IOException(e);
48     }
49     return resourceURI;
50   }
51 
52   @FunctionalInterface
53   private interface PathConsumer<T extends IOException> {
accept(Path path)54     void accept(Path path) throws T;
55   }
56 
57   /**
58    * Extract native resources in a temp directory.
59    * @param resourceURI Native resource location.
60    * @return The directory path containing all extracted libraries.
61    */
unpackNativeResources(URI resourceURI)62   private static Path unpackNativeResources(URI resourceURI) throws IOException {
63     Path tempPath;
64     tempPath = Files.createTempDirectory("ortools-java");
65     tempPath.toFile().deleteOnExit();
66 
67     PathConsumer<?> visitor;
68     visitor = (Path sourcePath) -> Files.walkFileTree(sourcePath, new SimpleFileVisitor<Path>() {
69       @Override
70       public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
71         Path newPath = tempPath.resolve(sourcePath.getParent().relativize(file).toString());
72         Files.copy(file, newPath);
73         newPath.toFile().deleteOnExit();
74         return FileVisitResult.CONTINUE;
75       }
76 
77       @Override
78       public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
79           throws IOException {
80         Path newPath = tempPath.resolve(sourcePath.getParent().relativize(dir).toString());
81         Files.copy(dir, newPath);
82         newPath.toFile().deleteOnExit();
83         return FileVisitResult.CONTINUE;
84       }
85     });
86 
87     FileSystem fs;
88     try {
89       fs = FileSystems.newFileSystem(resourceURI, Collections.emptyMap());
90     } catch (FileSystemAlreadyExistsException e) {
91       fs = FileSystems.getFileSystem(resourceURI);
92       if (fs == null) {
93         throw new IllegalArgumentException();
94       }
95     }
96     Path p = fs.provider().getPath(resourceURI);
97     visitor.accept(p);
98     return tempPath;
99   }
100 
101   /** Unpack and Load the native libraries needed for using ortools-java.*/
102   private static boolean loaded = false;
103 
loadNativeLibraries()104   public static synchronized void loadNativeLibraries() {
105     if (!loaded) {
106       try {
107         URI resourceURI = getNativeResourceURI();
108         Path tempPath = unpackNativeResources(resourceURI);
109         // Load the native library
110         System.load(tempPath.resolve(RESOURCE_PATH)
111                         .resolve(System.mapLibraryName("jniortools"))
112                         .toString());
113         loaded = true;
114       } catch (IOException e) {
115         throw new RuntimeException(e);
116       }
117     }
118   }
119 }
120