All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.izforge.izpack.util.Librarian Maven / Gradle / Ivy

There is a newer version: 5.2.3
Show newest version
/*
 * IzPack - Copyright 2001-2012 Julien Ponge, All Rights Reserved.
 *
 * http://izpack.org/
 * http://izpack.codehaus.org/
 *
 * Copyright 2002 Elmar Grom
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.izforge.izpack.util;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * This class handles loading of native libraries. There must only be one instance of
 * Librarian per Java runtime, therefore this class is implemented as a 'Singleton'.
 * 
*
* Librarian is capable of loading native libraries from a variety of different * source locations. However, you should place your library files in the 'native' directory. The * primary reason for supporting different source locations is to facilitate testing in a * development environment, without the need to actually packing the application into a *.jar file. * * @author Elmar Grom */ public class Librarian implements CleanupClient { /** * The logger. */ private static final Logger logger = Logger.getLogger(Librarian.class.getName()); /** * Used to identify jar URL protocols */ private static final String JAR_PROTOCOL = "jar"; /** * Used to identify file URL protocols */ private static final String FILE_PROTOCOL = "file"; /** * The default directory for native library files. */ private static final String NATIVE = "/com/izforge/izpack/bin/native/"; /** * A list that is used to track all libraries that have been loaded. This list is used to ensure * that each library is loaded only once. */ private List trackList = new ArrayList(); /** * A list of references to clients that use libraries that were extracted from a *.jar file. * This is needed because the clients need to be called for freeing their libraries. */ private List clients = new ArrayList(); /** * A list of fully qualified library names. This is needed to delete the temporary library files * after use. The index of each name corresponds to the index of the respective client in the * clients list. */ private List temporaryFileNames = new ArrayList(); /** * The extension to use for native libraries. */ private String extension = ""; /** * Constructs a Librarian. * * @param factory the factory * @param housekeeper the house keeper */ public Librarian(TargetFactory factory, Housekeeper housekeeper) { housekeeper.registerForCleanup(this); extension = '.' + factory.getNativeLibraryExtension(); } /** * Loads a library. * * @param name the library name * @param client the native library client * @throws UnsatisfiedLinkError if the library cannot be loaded */ public synchronized void loadLibrary(String name, NativeLibraryClient client) throws UnsatisfiedLinkError { name = strip(name); if (!trackList.contains(name)) { // no attempt has been made to load the library yet boolean loaded = loadArchSpecificLibrary(name, client); if (!loaded) { String name64 = name + "_x64"; loaded = loadArchSpecificLibrary(name64, client); } if (loaded) { trackList.add(name); } else { throw new UnsatisfiedLinkError("Failed to load library: " + name); } } } /*--------------------------------------------------------------------------*/ /** * This method attempts to remove all native libraries that have been temporarily created from * the system. * This method calls LibraryRemover which starts a new process which * waits a little bit for exit of this process and tries than to delete the given files. * If the version is 1.5.x or higher this process should be exit in one second, else * the native libraries will be not deleted. * Tests with the different methods produces hinds that the * FreeLibraryAndExitThread (handle, 0) call in the dlls are the * reason for VM crashes (version 1.5.x). May be this is a bug in the VM. * But never seen a docu that this behavior is compatible with a VM. * Since more than a year all 1.5 versions produce this crash. Therfore we make * now a work around for it. * But the idea to exit the thread for removing the file locking to give the * possibility to delete the dlls are really nice. Therefore we use it with * VMs which are compatible with it. (Klaus Bartz 2006.06.20) */ @Override public void cleanUp() { // This method will be used the SelfModifier stuff of uninstall // instead of killing the thread in the dlls which provokes a // segmentation violation with a 1.5 (also known as 5.0) VM. if (!temporaryFileNames.isEmpty()) { try { LibraryRemover.invoke(temporaryFileNames); } catch (IOException exception) { logger.log(Level.WARNING, "Cleanup failed for native libraries: " + exception.getMessage(), exception); } } clients.clear(); } /** * Returns the resource URL for the named library. * * @param name the library name * @return the library's resource URL, or null if it is not found */ protected URL getResourcePath(String name) { String resource = NATIVE + "izpack/" + name + extension; URL url = getClass().getResource(resource); if (url == null) { resource = NATIVE + "3rdparty/" + name + extension; url = getClass().getResource(resource); } return url; } /** * Loads the requested library. If the library is already loaded, this method returns * immediately, without an attempt to load the library again. *
* Invocation Example: This assumes that the call is made from the class that links with * the library. If this is not the case, this must be replaced by the reference * of the class that links with the library.
*
* * Librarian.getInstance ().loadLibrary ("MyLibrary", this); *
*
* Loading of a native library file works as follows:
*
    *
  • If the library is already loaded there is nothing to do. *
  • An attempt is made to load the library by its name. If there is no system path set to * the library, this attempt will fail. *
  • If the client is located on the local file system, an attempt is made to load the * library from the local files system as well. *
  • If the library is located inside a *.jar file, it is extracted to 'java.io.tmpdir' and * an attempt is made to load it from there. *
*
*
* Loading from the local file system and from the *.jar file is attempted for the following * potential locations of the library in this order:
*
    *
  1. The same directory where the client is located *
  2. The native library directory *
* * @param name the name of the library. A file extension and path are not needed, in fact if * supplied, both is stripped off. A specific extension is appended. * @param client the object that made the load request * @return true if the */ private boolean loadArchSpecificLibrary(String name, NativeLibraryClient client) { boolean result = false; if (loadFromDLLPath(name, client) || loadSystemLibrary(name, client) || loadFromClassPath(name, client)) { result = true; } return result; } /** * Attempts to load a library from the DLL_PATH system property. * * @param name the library name * @param client the native library client * @return true if the library was loaded successfully, otherwise false */ private boolean loadFromDLLPath(String name, NativeLibraryClient client) { String property = System.getProperty("DLL_PATH"); if (property != null) { String path = property + "/" + name + extension; path = path.replace('/', File.separatorChar); return load(path, client); } return false; } /** * Attempts to load a library from the classpath. * * @param name the library name * @param client the native library client * @return true if the library was loaded successfully, otherwise false */ private boolean loadFromClassPath(String name, NativeLibraryClient client) { boolean result = false; URL url = getResourcePath(name); if (url != null) { String protocol = url.getProtocol(); if (protocol.equalsIgnoreCase(FILE_PROTOCOL)) { // its a local file try { String path = new File(url.toURI()).getPath(); result = load(path, client); } catch (URISyntaxException exception) { logger.log(Level.WARNING, "Failed to load library: " + name + ": " + exception.getMessage(), exception); } } else if (protocol.equalsIgnoreCase(JAR_PROTOCOL)) { // its a jar file. Extract and load it from 'java.io.tmpdir' result = loadJarLibrary(name, url, client); } } return result; } /** * Attempts to load a library from a jar. * * @param name the library name * @param url the library URL within the jar * @param client the native library client * @return true if the library was loaded successfully, otherwise false */ private boolean loadJarLibrary(String name, URL url, NativeLibraryClient client) { boolean result = false; File file = null; InputStream in = null; FileOutputStream out = null; String path = null; try { file = File.createTempFile(name, extension, FileUtils.getTempDirectory()); in = url.openStream(); out = new FileOutputStream(file); IOUtils.copy(in, out); path = file.getAbsolutePath(); } catch (IOException exception) { logger.log(Level.WARNING, "Failed to load library: " + name + ": " + exception.getMessage(), exception); } finally { IOUtils.closeQuietly(in); IOUtils.closeQuietly(out); } if (path != null) { result = load(path, client); } if (!result) { FileUtils.deleteQuietly(file); } else { temporaryFileNames.add(path); file.deleteOnExit(); } return result; } /** * Loads a system library. * * @param name the library name * @param client the native library client * @return true if the library was loaded successfully, otherwise false */ private boolean loadSystemLibrary(String name, NativeLibraryClient client) { try { System.loadLibrary(name); clients.add(client); return true; } catch (Throwable exception) { logger.log(Level.FINE, "Failed to load library: " + name + ": " + exception.getMessage(), exception); } return false; } /** * Loads a library given its path. * * @param path the library path * @param client the native library client * @return true if the library was loaded successfully, otherwise false */ private boolean load(String path, NativeLibraryClient client) { boolean result = false; try { System.load(path); clients.add(client); result = true; } catch (Throwable exception) { logger.log(Level.FINE, "Failed to load library: " + path + ": " + exception.getMessage(), exception); } return result; } /** * Strips the extension of the library name, if it has one. * * @param name the name of the library * @return the name without an extension */ private String strip(String name) { int extensionStart = name.lastIndexOf('.'); int nameStart = name.lastIndexOf('/'); if (nameStart < 0) { nameStart = name.lastIndexOf('\\'); } nameStart++; String shortName; if (extensionStart > 0) { shortName = name.substring(nameStart, extensionStart); } else { shortName = name.substring(nameStart, name.length()); } return (shortName); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy