Skip to content

Commit 3ceefd3

Browse files
committed
Introducing NativeLoader
Introducing NativeLoader API intended to standardize how native libraries are loaded in dd-trace-java NativeLoader allows for using different file layout and file resolution strategies
1 parent 61c2d97 commit 3ceefd3

22 files changed

+1319
-0
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
plugins {
2+
`java-library`
3+
}
4+
5+
apply(from = "$rootDir/gradle/java.gradle")
6+
7+
dependencies {
8+
implementation(project(":components:environment"))
9+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package datadog.nativeloader;
2+
3+
import java.net.URL;
4+
import java.util.Objects;
5+
6+
public final class ClassLoaderResourcePathLocator implements PathLocator {
7+
private final ClassLoader classLoader;
8+
private final String baseResource;
9+
10+
public ClassLoaderResourcePathLocator(
11+
final ClassLoader classLoader,
12+
final String baseResource)
13+
{
14+
this.classLoader = classLoader;
15+
this.baseResource = baseResource;
16+
}
17+
18+
@Override
19+
public URL locate(String component, String path) {
20+
String fullPath = component == null ? "" : component;
21+
fullPath = this.baseResource == null ? fullPath : fullPath + "/" + this.baseResource;
22+
fullPath = fullPath.isEmpty() ? path : fullPath + "/" + path;
23+
24+
return this.classLoader.getResource(fullPath);
25+
}
26+
27+
@Override
28+
public int hashCode() {
29+
return Objects.hash(this.classLoader, this.baseResource);
30+
}
31+
32+
@Override
33+
public boolean equals(Object obj) {
34+
if (!(obj instanceof ClassLoaderResourcePathLocator)) return false;
35+
36+
ClassLoaderResourcePathLocator that = (ClassLoaderResourcePathLocator)obj;
37+
return this.classLoader.equals(that.classLoader) && Objects.equals(this.baseResource, that.baseResource);
38+
}
39+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package datadog.nativeloader;
2+
3+
import java.io.File;
4+
import java.net.MalformedURLException;
5+
import java.net.URL;
6+
import java.util.Arrays;
7+
import java.util.Objects;
8+
9+
public final class FileBasedPathLocator implements PathLocator {
10+
private final File[] libDirs;
11+
12+
public FileBasedPathLocator(File... libDirs) {
13+
this.libDirs = libDirs;
14+
}
15+
16+
@Override
17+
public URL locate(String component, String path) {
18+
String fullPath = component == null ? path : component + "/" + path;
19+
20+
for ( File libDir: this.libDirs ) {
21+
File libFile = new File(libDir, fullPath);
22+
if ( libFile.exists() ) return toUrl(libFile);
23+
}
24+
25+
return null;
26+
}
27+
28+
@SuppressWarnings("deprecation")
29+
private static final URL toUrl(File file) {
30+
try {
31+
return file.toURL();
32+
} catch ( MalformedURLException e ) {
33+
return null;
34+
}
35+
}
36+
37+
@Override
38+
public int hashCode() {
39+
return Objects.hash(this.libDirs);
40+
}
41+
42+
@Override
43+
public boolean equals(Object obj) {
44+
if (!(obj instanceof FileBasedPathLocator)) return false;
45+
46+
FileBasedPathLocator that = (FileBasedPathLocator)obj;
47+
return Arrays.equals(this.libDirs, that.libDirs);
48+
}
49+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package datadog.nativeloader;
2+
3+
import java.net.URL;
4+
5+
public final class FlatDirLibraryResolver implements LibraryResolver {
6+
public static final LibraryResolver INSTANCE = new FlatDirLibraryResolver();
7+
8+
private FlatDirLibraryResolver() {}
9+
10+
@Override
11+
public final URL resolve(PathLocator pathLocator, String component, PlatformSpec platformSpec, String libName) {
12+
String libFileName = PathUtils.libFileName(platformSpec, libName);
13+
14+
String osPath = PathUtils.osPartOf(platformSpec);
15+
String archPath = PathUtils.archPartOf(platformSpec);
16+
String libcPath = PathUtils.libcPartOf(platformSpec);
17+
18+
URL url;
19+
String regularPath = osPath + "-" + archPath;
20+
21+
if ( libcPath != null ) {
22+
String specializedPath = regularPath + "-" + libcPath;
23+
url = pathLocator.locate(component, specializedPath + "/" + libFileName);
24+
if ( url != null ) return url;
25+
}
26+
27+
url = pathLocator.locate(component, regularPath + "/" + libFileName);
28+
if ( url != null ) return url;
29+
30+
// fallback to searching at top-level, mostly concession to good out-of-box behavior
31+
// with java.library.path
32+
url = pathLocator.locate(component, libFileName);
33+
if ( url != null ) return url;
34+
35+
if ( component != null ) {
36+
url = pathLocator.locate(null, libFileName);
37+
if ( url != null ) return url;
38+
}
39+
40+
return null;
41+
}
42+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package datadog.nativeloader;
2+
3+
import java.io.File;
4+
5+
/**
6+
* Represents a resolved library
7+
* <ul>
8+
* <li>library may be preloaded - with no backing file</li>
9+
* <li>regular file - that doesn't require clean-up</li>
10+
* <li>temporary file - copying from another source - that does require clean-up</li>
11+
* </ul>
12+
*/
13+
public final class LibFile implements AutoCloseable {
14+
static final boolean NO_CLEAN_UP = false;
15+
static final boolean CLEAN_UP = true;
16+
17+
static final LibFile preloaded(String libName) {
18+
return new LibFile(libName, null, NO_CLEAN_UP);
19+
}
20+
21+
static final LibFile fromFile(String libName, File file) {
22+
return new LibFile(libName, file, NO_CLEAN_UP);
23+
}
24+
25+
static final LibFile fromTempFile(String libName, File file) {
26+
return new LibFile(libName, file, CLEAN_UP);
27+
}
28+
29+
final String libName;
30+
31+
final File file;
32+
final boolean needsCleanup;
33+
34+
LibFile(String libName, File file, boolean needsCleanup) {
35+
this.libName = libName;
36+
37+
this.file = file;
38+
this.needsCleanup = needsCleanup;
39+
}
40+
41+
public boolean isPreloaded() {
42+
return (this.file == null);
43+
}
44+
45+
public void load() throws LibraryLoadException {
46+
if ( this.isPreloaded() ) return;
47+
48+
try {
49+
Runtime.getRuntime().load(this.getAbsolutePath());
50+
} catch ( Throwable t ) {
51+
throw new LibraryLoadException(this.libName, t);
52+
}
53+
}
54+
55+
public final String getAbsolutePath() {
56+
return this.file.getAbsolutePath();
57+
}
58+
59+
@Override
60+
public void close() {
61+
if ( this.needsCleanup ) {
62+
NativeLoader.delete(this.file);
63+
}
64+
}
65+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package datadog.nativeloader;
2+
3+
/**
4+
* Exception raised when NativeLoader fails to resolve or load a library
5+
*/
6+
public class LibraryLoadException extends Exception {
7+
private static final long serialVersionUID = 1L;
8+
9+
public LibraryLoadException(String libName) {
10+
super(message(libName));
11+
}
12+
13+
public LibraryLoadException(String libName, Throwable cause) {
14+
this(message(libName), cause.getMessage(), cause);
15+
}
16+
17+
public LibraryLoadException(String libName, String message) {
18+
super(message(libName) + " - " + message);
19+
}
20+
21+
public LibraryLoadException(String libName, String message, Throwable cause) {
22+
super(message(libName) + " - " + message, cause);
23+
}
24+
25+
static final String message(String libName) {
26+
return "Unable to resolve library " + libName;
27+
}
28+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package datadog.nativeloader;
2+
3+
import java.net.URL;
4+
5+
public interface LibraryResolver {
6+
default boolean isPreloaded(
7+
PlatformSpec platform,
8+
String libName)
9+
{
10+
return false;
11+
}
12+
13+
URL resolve(
14+
PathLocator pathLocator,
15+
String component,
16+
PlatformSpec platformSpec,
17+
String libName);
18+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package datadog.nativeloader;
2+
3+
import java.net.URL;
4+
import java.util.Arrays;
5+
import java.util.HashSet;
6+
import java.util.Set;
7+
8+
public final class LibraryResolvers {
9+
private LibraryResolvers() {}
10+
11+
public static final LibraryResolver defaultLibraryResolver() {
12+
return flatDirs();
13+
}
14+
15+
public static final LibraryResolver withPreloaded(LibraryResolver baseResolver, String... preloadedLibNames) {
16+
Set<String> preloadedSet = new HashSet<>(Arrays.asList(preloadedLibNames));
17+
return new LibraryResolver() {
18+
@Override
19+
public boolean isPreloaded(PlatformSpec platform, String libName) {
20+
return preloadedSet.contains(libName);
21+
}
22+
23+
@Override
24+
public URL resolve(PathLocator pathLocator, String component, PlatformSpec platformSpec, String libName) {
25+
return baseResolver.resolve(pathLocator, component, platformSpec, libName);
26+
}
27+
};
28+
}
29+
30+
public static final LibraryResolver flatDirs() {
31+
return FlatDirLibraryResolver.INSTANCE;
32+
}
33+
34+
public static final LibraryResolver nestedDirs() {
35+
return NestedDirLibraryResolver.INSTANCE;
36+
}
37+
}

0 commit comments

Comments
 (0)