Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
f80bcda
Adding listener support to NativeLoader
dougqh Nov 4, 2025
54af840
Adding listener tests
dougqh Nov 5, 2025
035ba29
isEmpty -> isNop to better convey purpose
dougqh Nov 5, 2025
fa9428e
Adding listener tests
dougqh Nov 5, 2025
f7edec9
Adding listener tests
dougqh Nov 5, 2025
fbe0943
spotless
dougqh Nov 5, 2025
ed518d1
More end-to-end listener tests
dougqh Nov 5, 2025
b376954
More end-to-end listener testing
dougqh Nov 5, 2025
01ef204
More end-to-end listener tests
dougqh Nov 5, 2025
cd6affc
More end-to-end listener tests
dougqh Nov 5, 2025
625b8c3
spotless
dougqh Nov 5, 2025
2b2b9f9
Fixed errant text replacement in Javadoc
dougqh Nov 5, 2025
b00bbb6
Adding clarifying comments
dougqh Nov 5, 2025
140ae13
Tweaking listener API
dougqh Nov 6, 2025
9376016
More JavaDoc & spotless
dougqh Nov 6, 2025
20c3e02
More end-to-end listener tests
dougqh Nov 6, 2025
f6381ed
More end-to-end listener testing
dougqh Nov 6, 2025
e8d44db
spotless
dougqh Nov 6, 2025
0cbfd6e
Merge branch 'master' into dougqh/native-loader-listener
dougqh Nov 6, 2025
3bd0567
Adding coverage
dougqh Nov 6, 2025
1c0dccc
Merge branch 'dougqh/native-loader-listener' of github.com:DataDog/dd…
dougqh Nov 6, 2025
d77936d
API clean-up
dougqh Nov 6, 2025
9ae3be8
API clean-up
dougqh Nov 6, 2025
1f99359
Fixing up oversights in previous parameter reordering
dougqh Nov 6, 2025
244807f
More test coverage
dougqh Nov 6, 2025
1e458b2
More coverage
dougqh Nov 6, 2025
517e3b4
spotless
dougqh Nov 6, 2025
1f98b7f
More coverage
dougqh Nov 6, 2025
5bdaa88
spotless
dougqh Nov 6, 2025
0b8ab6c
Merge branch 'master' into dougqh/native-loader-listener
dougqh Nov 6, 2025
3e9234d
Merge branch 'master' into dougqh/native-loader-listener
dougqh Nov 10, 2025
5fff446
Merge branch 'master' into dougqh/native-loader-listener
dougqh Nov 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ public ClassLoaderResourcePathLocator(final ClassLoader classLoader, final Strin
}

@Override
public URL locate(String component, String path) {
return this.classLoader.getResource(PathUtils.concatPath(component, this.baseResource, path));
public URL locate(String optionalComponent, String path) {
return this.classLoader.getResource(
PathUtils.concatPath(optionalComponent, this.baseResource, path));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package datadog.nativeloader;

import java.net.URL;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;

final class CompositeLibraryLoadingListener extends SafeLibraryLoadingListener {
private final Collection<? extends LibraryLoadingListener> listeners;

CompositeLibraryLoadingListener(LibraryLoadingListener... listeners) {
this(Arrays.asList(listeners));
}

CompositeLibraryLoadingListener(Collection<? extends LibraryLoadingListener> listeners) {
this.listeners = listeners;
}

@Override
public boolean isNop() {
return this.listeners.isEmpty();
}

int size() {
return this.listeners.size();
}

@Override
public void onResolveDynamic(
PlatformSpec platformSpec,
String optionalComponent,
String libName,
boolean isPreloaded,
URL optionalUrl) {
for (LibraryLoadingListener listener : this.listeners) {
try {
listener.onResolveDynamic(
platformSpec, optionalComponent, libName, isPreloaded, optionalUrl);
} catch (Throwable ignored) {
}
}
}

@Override
public void onResolveDynamicFailure(
PlatformSpec platformSpec,
String optionalComponent,
String libName,
Throwable optionalCause) {
for (LibraryLoadingListener listener : this.listeners) {
try {
listener.onResolveDynamicFailure(platformSpec, optionalComponent, libName, optionalCause);
} catch (Throwable ignored) {
}
}
}

@Override
public void onLoad(
PlatformSpec platformSpec,
String optionalComponent,
String libName,
boolean isPreloaded,
Path optionalLibPath) {
for (LibraryLoadingListener listener : this.listeners) {
try {
listener.onLoad(platformSpec, optionalComponent, libName, isPreloaded, optionalLibPath);
} catch (Throwable ignored) {
}
}
}

@Override
public void onLoadFailure(
PlatformSpec platformSpec,
String optionalComponent,
String libName,
Throwable optionalCause) {
for (LibraryLoadingListener listener : this.listeners) {
try {
listener.onLoadFailure(platformSpec, optionalComponent, libName, optionalCause);
} catch (Throwable ignored) {
}
}
}

@Override
public void onTempFileCreated(
PlatformSpec platformSpec, String optionalComponent, String libName, Path tempFile) {
for (LibraryLoadingListener listener : this.listeners) {
try {
listener.onTempFileCreated(platformSpec, optionalComponent, libName, tempFile);
} catch (Throwable ignored) {
}
}
}

@Override
public void onTempFileCreationFailure(
PlatformSpec platformSpec,
String optionalComponent,
String libName,
Path tempDir,
String libExt,
Throwable optionalCause) {
for (LibraryLoadingListener listener : this.listeners) {
try {
listener.onTempFileCreationFailure(
platformSpec, optionalComponent, libName, tempDir, libExt, optionalCause);
} catch (Throwable ignored) {
}
}
}

@Override
public void onTempFileCleanup(
PlatformSpec platformSpec, String optionalComponent, String libName, Path tempPath) {
for (LibraryLoadingListener listener : this.listeners) {
try {
listener.onTempFileCleanup(platformSpec, optionalComponent, libName, tempPath);
} catch (Throwable ignored) {
}
}
}

@Override
public CompositeLibraryLoadingListener join(LibraryLoadingListener... listeners) {
ArrayList<LibraryLoadingListener> combinedListeners =
new ArrayList<>(this.listeners.size() + listeners.length);
combinedListeners.addAll(this.listeners);
combinedListeners.addAll(Arrays.asList(listeners));
return new CompositeLibraryLoadingListener(combinedListeners);
}

@Override
public String toString() {
return this.getClass().getSimpleName() + ":" + this.listeners.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ private FlatDirLibraryResolver() {}

@Override
public final URL resolve(
PathLocator pathLocator, String component, PlatformSpec platformSpec, String libName)
PathLocator pathLocator, PlatformSpec platformSpec, String optionalComponent, String libName)
throws Exception {
PathLocatorHelper pathLocatorHelper = new PathLocatorHelper(libName, pathLocator);

Expand All @@ -28,22 +28,22 @@ public final URL resolve(

if (libcPath != null) {
String specializedPath = regularPath + "-" + libcPath;
url = pathLocatorHelper.locate(component, specializedPath + "/" + libFileName);
url = pathLocatorHelper.locate(optionalComponent, specializedPath + "/" + libFileName);
if (url != null) return url;
}

url = pathLocatorHelper.locate(component, regularPath + "/" + libFileName);
url = pathLocatorHelper.locate(optionalComponent, regularPath + "/" + libFileName);
if (url != null) return url;

url = pathLocatorHelper.locate(component, osPath + "/" + libFileName);
url = pathLocatorHelper.locate(optionalComponent, osPath + "/" + libFileName);
if (url != null) return url;

// fallback to searching at top-level, mostly concession to good out-of-box behavior
// with java.library.path
url = pathLocatorHelper.locate(component, libFileName);
url = pathLocatorHelper.locate(optionalComponent, libFileName);
if (url != null) return url;

if (component != null) {
if (optionalComponent != null) {
url = pathLocatorHelper.locate(null, libFileName);
if (url != null) return url;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ public LibDirBasedPathLocator(File... libDirs) {
}

@Override
public URL locate(String component, String path) {
String fullPath = PathUtils.concatPath(component, path);
public URL locate(String optionalComponent, String path) {
String fullPath = PathUtils.concatPath(optionalComponent, path);

for (File libDir : this.libDirs) {
File libFile = new File(libDir, fullPath);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,66 +16,114 @@ public final class LibFile implements AutoCloseable {
static final boolean NO_CLEAN_UP = false;
static final boolean CLEAN_UP = true;

static final LibFile preloaded(String libName) {
return new LibFile(libName, null, NO_CLEAN_UP);
static final LibFile preloaded(
PlatformSpec platformSpec,
String optionalComponent,
String libName,
SafeLibraryLoadingListener listeners) {
return new LibFile(platformSpec, optionalComponent, libName, null, NO_CLEAN_UP, listeners);
}

static final LibFile fromFile(String libName, File file) {
return new LibFile(libName, file, NO_CLEAN_UP);
static final LibFile fromFile(
PlatformSpec platformSpec,
String optionalComponent,
String libName,
File optionalFile,
SafeLibraryLoadingListener listeners) {
return new LibFile(
platformSpec, optionalComponent, libName, optionalFile, NO_CLEAN_UP, listeners);
}

static final LibFile fromTempFile(String libName, File file) {
return new LibFile(libName, file, CLEAN_UP);
static final LibFile fromTempFile(
PlatformSpec platformSpec,
String optionalComponent,
String libName,
File optionalFile,
SafeLibraryLoadingListener listeners) {
return new LibFile(platformSpec, optionalComponent, libName, optionalFile, CLEAN_UP, listeners);
}

final PlatformSpec platformSpec;
final String optionalComponent;
final String libName;

final File file;
final File optionalFile;
final boolean needsCleanup;

LibFile(String libName, File file, boolean needsCleanup) {
final SafeLibraryLoadingListener listeners;

LibFile(
PlatformSpec platformSpec,
String optionalComponent,
String libName,
File optionalFile,
boolean needsCleanup,
SafeLibraryLoadingListener listeners) {
this.platformSpec = platformSpec;
this.optionalComponent = optionalComponent;
this.libName = libName;

this.file = file;
this.optionalFile = optionalFile;
this.needsCleanup = needsCleanup;

this.listeners = listeners;
}

/** Indicates if this library was "preloaded" */
public boolean isPreloaded() {
return (this.file == null);
return (this.optionalFile == null);
}

/** Loads the underlying library into the JVM */
public void load() throws LibraryLoadException {
if (this.isPreloaded()) return;
boolean isPreloaded = this.isPreloaded();
if (isPreloaded) {
this.listeners.onLoad(
this.platformSpec, this.optionalComponent, this.libName, isPreloaded, null);
return;
}

try {
Runtime.getRuntime().load(this.getAbsolutePath());

if (true) throw new RuntimeException("real load - worked?");
} catch (Throwable t) {
this.listeners.onLoadFailure(this.platformSpec, this.optionalComponent, this.libName, t);
throw new LibraryLoadException(this.libName, t);
}

this.listeners.onLoad(
this.platformSpec,
this.optionalComponent,
this.libName,
isPreloaded,
this.optionalFile.toPath());
}

/** Provides a File to the library -- returns null for pre-loaded libraries */
public final File toFile() {
return this.file;
return this.optionalFile;
}

/** Provides a Path to the library -- return null for pre-loaded libraries */
public final Path toPath() {
return this.file == null ? null : this.file.toPath();
return this.optionalFile == null ? null : this.optionalFile.toPath();
}

/** Provides the an absolute path to the library -- returns null for pre-loaded libraries */
public final String getAbsolutePath() {
return this.file == null ? null : this.file.getAbsolutePath();
return this.optionalFile == null ? null : this.optionalFile.getAbsolutePath();
}

/** Schedules clean-up of underlying file -- if the file is a temp file */
/** Schedules clean-up of underlying optionalFile -- if the file is a temp file */
@Override
public void close() {
if (this.needsCleanup) {
NativeLoader.delete(this.file);
boolean deleted = NativeLoader.delete(this.optionalFile);
if (deleted) {
this.listeners.onTempFileCleanup(
this.platformSpec, this.optionalComponent, this.libName, this.optionalFile.toPath());
}
}
}
}
Loading