Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 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
@@ -0,0 +1,133 @@
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,
LibraryLoadException 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 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,
Path optionalTempFile,
Throwable optionalCause) {
for (LibraryLoadingListener listener : this.listeners) {
try {
listener.onTempFileCreationFailure(
platformSpec,
optionalComponent,
libName,
tempDir,
libExt,
optionalTempFile,
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 @@ -7,75 +7,122 @@
* Represents a resolved library
*
* <ul>
* <li>library may be preloaded - with no backing file
* <li>regular file - that doesn't require clean-up
* <li>temporary file - copying from another source - that does require clean-up
* <li>library may be preloaded - with no backing optionalFile
* <li>regular optionalFile - that doesn't require clean-up
* <li>temporary optionalFile - copying from another source - that does require clean-up
* </ul>
*/
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());

this.listeners.onLoad(
this.platformSpec,
this.optionalComponent,
this.libName,
isPreloaded,
this.optionalFile.toPath());
} catch (Throwable t) {
throw new LibraryLoadException(this.libName, t);
LibraryLoadException ex = new LibraryLoadException(this.libName, t);
this.listeners.onLoadFailure(this.platformSpec, this.optionalComponent, this.libName, ex);
throw ex;
}
}

/** 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());
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package datadog.nativeloader;

import java.net.URL;
import java.nio.file.Path;

public interface LibraryLoadingListener {
default void onResolveDynamic(
PlatformSpec platformSpec,
String optionalComponent,
String libName,
boolean isPreloaded,
URL optionalUrl) {}

default void onResolveDynamicFailure(
PlatformSpec platformSpec,
String optionalComponent,
String libName,
LibraryLoadException optionalCause) {}

default void onLoad(
PlatformSpec platformSpec,
String optionalComponent,
String libName,
boolean isPreloaded,
Path optionalLibPath) {}

default void onLoadFailure(
PlatformSpec platformSpec,
String optionalComponent,
String libName,
LibraryLoadException optionalCause) {}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll probably end up tweaking the methods for temp file notification if/when we add in pluggable temp file managers.

default void onTempFileCreated(
PlatformSpec platformSpec, String optionalComponent, String libName, Path tempFile) {}

default void onTempFileCreationFailure(
PlatformSpec platformSpec,
String optionalComponent,
String libName,
Path tempDir,
String libExt,
Path optionalTempFile,
Throwable optionalCause) {}

default void onTempFileCleanup(
PlatformSpec platformSpec, String optionalComponent, String libName, Path tempFile) {}
}

abstract class SafeLibraryLoadingListener implements LibraryLoadingListener {
public abstract SafeLibraryLoadingListener join(LibraryLoadingListener... listeners);

public abstract boolean isNop();
}
Loading
Loading