org.eclipse.osgi.storage.NativeCodeFinder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aspectjtools Show documentation
Show all versions of aspectjtools Show documentation
Tools from the AspectJ project
/*******************************************************************************
* Copyright (c) 2005, 2016 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.osgi.storage;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.eclipse.osgi.container.ModuleRevision;
import org.eclipse.osgi.container.ModuleWire;
import org.eclipse.osgi.container.ModuleWiring;
import org.eclipse.osgi.internal.debug.Debug;
import org.eclipse.osgi.internal.framework.FilterImpl;
import org.eclipse.osgi.internal.hookregistry.ClassLoaderHook;
import org.eclipse.osgi.storage.BundleInfo.Generation;
import org.eclipse.osgi.storage.bundlefile.BundleEntry;
import org.eclipse.osgi.storage.bundlefile.BundleFile;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.namespace.HostNamespace;
import org.osgi.framework.namespace.NativeNamespace;
import org.osgi.framework.wiring.BundleRevision;
public class NativeCodeFinder {
public static final String REQUIREMENT_NATIVE_PATHS_ATTRIBUTE = "native.paths"; //$NON-NLS-1$
private static final String[] EMPTY_STRINGS = new String[0];
public static final String EXTERNAL_LIB_PREFIX = "external:"; //$NON-NLS-1$
private final Generation generation;
private final Debug debug;
// This is only used to keep track of when the same native library is loaded more than once
private final Collection loadedNativeCode = new ArrayList<>(1);
public NativeCodeFinder(Generation generation) {
this.generation = generation;
this.debug = generation.getBundleInfo().getStorage().getConfiguration().getDebug();
}
/*
* Maps an already mapped library name to additional library file extensions.
* This is needed on platforms like AIX where .a and .so can be used as library file
* extensions, but System.mapLibraryName only returns a single string.
*/
public String[] mapLibraryNames(String mappedLibName) {
int extIndex = mappedLibName.lastIndexOf('.');
List LIB_EXTENSIONS = generation.getBundleInfo().getStorage().getConfiguration().LIB_EXTENSIONS;
if (LIB_EXTENSIONS.isEmpty() || extIndex < 0)
return EMPTY_STRINGS;
String libNameBase = mappedLibName.substring(0, extIndex);
String[] results = new String[LIB_EXTENSIONS.size()];
for (int i = 0; i < results.length; i++)
results[i] = libNameBase + LIB_EXTENSIONS.get(i);
return results;
}
String findLibrary(String libname) {
String path = findLibrary0(libname);
if (path != null) {
synchronized (loadedNativeCode) {
if (loadedNativeCode.contains(path) || generation.getBundleInfo().getStorage().getConfiguration().COPY_NATIVES) {
// we must copy the library to a temp space to allow another class loader to load the library
String temp = generation.getBundleInfo().getStorage().copyToTempLibrary(generation, path);
if (temp != null)
path = temp;
} else {
loadedNativeCode.add(path);
}
}
}
return path;
}
private String findLibrary0(String libname) {
String path = null;
List hooks = generation.getBundleInfo().getStorage().getConfiguration().getHookRegistry().getClassLoaderHooks();
for (ClassLoaderHook hook : hooks) {
path = hook.findLocalLibrary(generation, libname);
if (path != null) {
return path;
}
}
String mappedName = System.mapLibraryName(libname);
String[] altMappedNames = mapLibraryNames(mappedName);
// first check Bundle-NativeCode header
path = findBundleNativeCode(libname, mappedName, altMappedNames);
// next check eclipse specific support
return path != null ? path : findEclipseNativeCode(libname, mappedName, altMappedNames);
}
private String findEclipseNativeCode(String libname, String mappedName, String[] altMappedNames) {
if (libname.length() == 0)
return null;
if (libname.charAt(0) == '/' || libname.charAt(0) == '\\')
libname = libname.substring(1);
String result = searchEclipseVariants(mappedName);
if (result != null)
return result;
for (int i = 0; i < altMappedNames.length && result == null; i++)
result = searchEclipseVariants(altMappedNames[i]);
return result;
}
private String searchEclipseVariants(String path) {
List ECLIPSE_LIB_VARIANTS = generation.getBundleInfo().getStorage().getConfiguration().ECLIPSE_LIB_VARIANTS;
for (String variant : ECLIPSE_LIB_VARIANTS) {
BundleFile baseBundleFile = generation.getBundleFile();
BundleEntry libEntry = baseBundleFile.getEntry(variant + path);
if (libEntry != null) {
File libFile = baseBundleFile.getFile(variant + path, true);
if (libFile == null)
return null;
// see bug 88697 - HP requires libraries to have executable permissions
if (org.eclipse.osgi.service.environment.Constants.OS_HPUX.equals(generation.getBundleInfo().getStorage().getConfiguration().getOS())) {
try {
// use the string array method in case there is a space in the path
Runtime.getRuntime().exec(new String[] {"chmod", "755", libFile.getAbsolutePath()}).waitFor(); //$NON-NLS-1$ //$NON-NLS-2$
} catch (Exception e) {
e.printStackTrace();
}
}
return libFile.getAbsolutePath();
}
}
return null;
}
private String findBundleNativeCode(String libname, String mappedName, String[] altMappedNames) {
String path = null;
if (debug.DEBUG_LOADER)
Debug.println(" mapped library name: " + mappedName); //$NON-NLS-1$
List nativePaths = getNativePaths();
if (nativePaths.isEmpty()) {
return null;
}
path = findNativePath(nativePaths, mappedName);
if (path == null) {
for (int i = 0; i < altMappedNames.length && path == null; i++)
path = findNativePath(nativePaths, altMappedNames[i]);
}
if (path == null) {
if (debug.DEBUG_LOADER)
Debug.println(" library does not exist: " + mappedName); //$NON-NLS-1$
path = findNativePath(nativePaths, libname);
}
if (debug.DEBUG_LOADER)
Debug.println(" returning library: " + path); //$NON-NLS-1$
return path;
}
private List getNativePaths() {
ModuleRevision revision = generation.getRevision();
ModuleWiring wiring = revision.getWiring();
if (wiring == null) {
// unresolved? should not be possible
return Collections.emptyList();
}
if ((revision.getTypes() & BundleRevision.TYPE_FRAGMENT) != 0) {
List hosts = wiring.getRequiredModuleWires(HostNamespace.HOST_NAMESPACE);
if (hosts == null) {
// unresolved or invalid? should not be possible
return Collections.emptyList();
}
if (!hosts.isEmpty()) {
// just use the first host wiring
wiring = hosts.get(0).getProviderWiring();
}
}
List nativeCode = wiring.getRequiredModuleWires(NativeNamespace.NATIVE_NAMESPACE);
if (nativeCode == null || nativeCode.isEmpty()) {
return Collections.emptyList();
}
// just taking the first paths for the revision, we sorted correctly when transforming to the requirement
for (ModuleWire moduleWire : nativeCode) {
if (moduleWire.getRequirement().getRevision().equals(revision)) {
@SuppressWarnings("unchecked")
List result = (List) moduleWire.getRequirement().getAttributes()
.get(REQUIREMENT_NATIVE_PATHS_ATTRIBUTE);
if (result != null)
return result;
// this must be a multi-clause Bundle-NativeCode header, need to check for the correct one in the index
try {
FilterImpl filter = FilterImpl.newInstance(moduleWire.getRequirement().getDirectives().get(NativeNamespace.REQUIREMENT_FILTER_DIRECTIVE));
int index = -1;
Map capabilityAttrs = moduleWire.getCapability().getAttributes();
for (FilterImpl child : filter.getChildren()) {
index++;
if (child.matches(capabilityAttrs)) {
break;
}
}
if (index != -1) {
@SuppressWarnings("unchecked")
List indexResult = (List) moduleWire.getRequirement().getAttributes()
.get(REQUIREMENT_NATIVE_PATHS_ATTRIBUTE + '.' + index);
if (indexResult != null)
return indexResult;
}
} catch (InvalidSyntaxException e) {
throw new RuntimeException(e);
}
}
}
return Collections.emptyList();
}
private String findNativePath(List nativePaths, String libname) {
int slash = libname.lastIndexOf('/');
if (slash >= 0)
libname = libname.substring(slash + 1);
for (String nativePath : nativePaths) {
slash = nativePath.lastIndexOf('/');
String path = slash < 0 ? nativePath : nativePath.substring(slash + 1);
if (path.equals(libname)) {
if (nativePath.startsWith(NativeCodeFinder.EXTERNAL_LIB_PREFIX)) {
// references an external library; do variable substitution
String externalPath = generation.getBundleInfo().getStorage().getConfiguration().substituteVars(nativePath.substring(NativeCodeFinder.EXTERNAL_LIB_PREFIX.length()));
File nativeFile = new File(externalPath);
return nativeFile.getAbsolutePath();
}
// this is a normal library contained within the bundle
File nativeFile = generation.getBundleFile().getFile(nativePath, true);
if (nativeFile != null)
return nativeFile.getAbsolutePath();
}
}
return null;
}
}