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