-
Notifications
You must be signed in to change notification settings - Fork 312
Introducing NativeLoader #9625
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
dougqh
wants to merge
59
commits into
master
Choose a base branch
from
dougqh/library-loader
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Introducing NativeLoader #9625
Changes from all commits
Commits
Show all changes
59 commits
Select commit
Hold shift + click to select a range
3ceefd3
Introducing NativeLoader
dougqh bf8a301
spotless
dougqh 67fc018
Eliminate var-args warning
dougqh e59a283
Remove unused import
dougqh 77b5c42
Added preloaded test
dougqh 972f5c3
Javadoc and misc clean-up
dougqh 8534a4d
Removed errant comment
dougqh cfb4285
Tweaking Javadoc
dougqh 63eefcd
More tests & javadoc
dougqh 814e4c1
Solidifying exception handling
dougqh 621c6ab
CapturingPathResolver -> CapturingPathLocator
dougqh d564cb5
nativeloader -> native-loader
dougqh 7aadebc
Adding FunctionalInterface
dougqh ad2866f
Removing public from function
dougqh 7655ce0
Merge branch 'master' into dougqh/library-loader
dougqh 92a0def
Forbidden API fix
dougqh 840d32a
Merge branch 'dougqh/library-loader' of github.com:DataDog/dd-trace-j…
dougqh 34ca3b3
Overloading withPreloaded to take a Set
dougqh bf4f1fe
Merge branch 'master' into dougqh/library-loader
dougqh 2b899aa
Hooking into arch detection code relocated to :components:environment
dougqh 4b3ba71
Code coverage - PathUtils
dougqh 8d6b2d0
spotless
dougqh 3c12ae6
More PathUtils coverage
dougqh e0b7b85
PathLocatorHelper coverage
dougqh ae4aad6
Coverage PlatformSpec
dougqh 0e8c405
Adding ability to record multiple locate requests to CapturingPathLoc…
dougqh dadb822
Coverage of LibraryResolvers
dougqh fe7ae49
Coverage PathLocators
dougqh 45a71f3
Coverage LibDirBasedPathLocator
dougqh ccf73f0
PathLocators coverage
dougqh d04985d
Fixed bug with subResource loading in ClassLoaderResourcePathLocator
dougqh 6b79e46
More coverage
dougqh 8d04747
NativeLoader.Builder coverage
dougqh 065b3cf
spotless
dougqh 6d66b39
ClassLoaderBasedPathLocator coverage
dougqh 79f0c89
LibFile coverage
dougqh a76f79d
Coverage NativeLoader.Builder - added tempDir tests
dougqh 00fa98a
Testing tempDir functionality
dougqh 6540109
Coverage - Testing PlatformSpec override variations of resolveDynamic
dougqh d4743f1
Temp file coverage
dougqh 0336fd0
spotless
dougqh 8a747b2
Update components/native-loader/src/main/java/datadog/nativeloader/Fl…
dougqh 7ac11d7
Addressing review comments - lowering visibility on a few classes
dougqh ef880c5
Addressing review comments - explicit equals call in test
dougqh 87800f0
Addressing review comments - be more explicit about equivalence tests
dougqh eab2dbb
Merge branch 'dougqh/library-loader' of github.com:DataDog/dd-trace-j…
dougqh 589a1d2
fixing merge conflict
dougqh 28d3578
spotless
dougqh 65e47bf
Addressing review comments - LibraryResolver test clean up
dougqh 940a69a
Addressing review comments - removing redundant final on methods
dougqh 36a219b
Addressing review comments - removing final on methods
dougqh 8e3280a
spotless
dougqh 17627f1
Addressing review comments - check a not preloaded case for Builder
dougqh 6b586ba
spotless
dougqh dcb5749
Adding explanatory comment for review question
dougqh 818a1be
Tweaking assertions for readability
dougqh 8401635
Improving path concatenation handling
dougqh 25af560
Removed test jar file
dougqh fa0f8a8
Merge branch 'master' into dougqh/library-loader
dougqh File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
plugins { | ||
`java-library` | ||
} | ||
|
||
apply(from = "$rootDir/gradle/java.gradle") | ||
|
||
dependencies { | ||
implementation(project(":components:environment")) | ||
} |
34 changes: 34 additions & 0 deletions
34
...ents/native-loader/src/main/java/datadog/nativeloader/ClassLoaderResourcePathLocator.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package datadog.nativeloader; | ||
|
||
import java.net.URL; | ||
import java.util.Objects; | ||
|
||
/** ClassLoaderResourcePathLocator locates library paths inside a {@link ClassLoader} */ | ||
final class ClassLoaderResourcePathLocator implements PathLocator { | ||
private final ClassLoader classLoader; | ||
private final String baseResource; | ||
|
||
public ClassLoaderResourcePathLocator(final ClassLoader classLoader, final String baseResource) { | ||
this.classLoader = classLoader; | ||
this.baseResource = baseResource; | ||
} | ||
|
||
@Override | ||
public URL locate(String component, String path) { | ||
return this.classLoader.getResource(PathUtils.concatPath(component, this.baseResource, path)); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(this.classLoader, this.baseResource); | ||
} | ||
|
||
@Override | ||
public boolean equals(Object obj) { | ||
if (!(obj instanceof ClassLoaderResourcePathLocator)) return false; | ||
|
||
ClassLoaderResourcePathLocator that = (ClassLoaderResourcePathLocator) obj; | ||
return this.classLoader.equals(that.classLoader) | ||
&& Objects.equals(this.baseResource, that.baseResource); | ||
} | ||
} |
55 changes: 55 additions & 0 deletions
55
components/native-loader/src/main/java/datadog/nativeloader/FlatDirLibraryResolver.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package datadog.nativeloader; | ||
|
||
import java.net.URL; | ||
|
||
/** | ||
* FlatDirLibraryResolver - uses flat directories to provide more specific libraries to load <code> | ||
* {os}-{arch}-{libc/musl}</code> | ||
*/ | ||
public final class FlatDirLibraryResolver implements LibraryResolver { | ||
public static final FlatDirLibraryResolver INSTANCE = new FlatDirLibraryResolver(); | ||
|
||
private FlatDirLibraryResolver() {} | ||
|
||
@Override | ||
public final URL resolve( | ||
PathLocator pathLocator, String component, PlatformSpec platformSpec, String libName) | ||
throws Exception { | ||
PathLocatorHelper pathLocatorHelper = new PathLocatorHelper(libName, pathLocator); | ||
|
||
String libFileName = PathUtils.libFileName(platformSpec, libName); | ||
|
||
String osPath = PathUtils.osPartOf(platformSpec); | ||
String archPath = PathUtils.archPartOf(platformSpec); | ||
String libcPath = PathUtils.libcPartOf(platformSpec); | ||
|
||
URL url; | ||
String regularPath = osPath + "-" + archPath; | ||
|
||
if (libcPath != null) { | ||
String specializedPath = regularPath + "-" + libcPath; | ||
url = pathLocatorHelper.locate(component, specializedPath + "/" + libFileName); | ||
if (url != null) return url; | ||
} | ||
|
||
url = pathLocatorHelper.locate(component, regularPath + "/" + libFileName); | ||
if (url != null) return url; | ||
|
||
url = pathLocatorHelper.locate(component, 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); | ||
if (url != null) return url; | ||
|
||
if (component != null) { | ||
url = pathLocatorHelper.locate(null, libFileName); | ||
if (url != null) return url; | ||
} | ||
|
||
pathLocatorHelper.tryThrow(); | ||
|
||
return null; | ||
} | ||
} |
65 changes: 65 additions & 0 deletions
65
components/native-loader/src/main/java/datadog/nativeloader/IntrospectPlatformSpec.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
package datadog.nativeloader; | ||
|
||
import datadog.environment.OperatingSystem; | ||
import datadog.environment.OperatingSystem.Architecture; | ||
|
||
/* | ||
* Default PlatformSpec used in dd-trace-java -- wraps detection code in component:environment | ||
*/ | ||
final class IntrospectPlatformSpec extends PlatformSpec { | ||
static final PlatformSpec INSTANCE = new IntrospectPlatformSpec(); | ||
|
||
@Override | ||
public boolean isLinux() { | ||
return OperatingSystem.isLinux(); | ||
} | ||
|
||
@Override | ||
public boolean isMac() { | ||
return OperatingSystem.isMacOs(); | ||
} | ||
|
||
@Override | ||
public boolean isWindows() { | ||
return OperatingSystem.isWindows(); | ||
} | ||
|
||
@Override | ||
public boolean isMusl() { | ||
return OperatingSystem.isMusl(); | ||
} | ||
|
||
@Override | ||
public boolean isAarch64() { | ||
return isArch(Architecture.ARM64); | ||
} | ||
|
||
@Override | ||
public boolean isArm32() { | ||
return isArch(Architecture.ARM); | ||
} | ||
|
||
@Override | ||
public boolean isX86_32() { | ||
return isArch(Architecture.X86); | ||
} | ||
|
||
@Override | ||
public boolean isX86_64() { | ||
return isArch(Architecture.X64); | ||
} | ||
|
||
static final boolean isArch(OperatingSystem.Architecture arch) { | ||
return (OperatingSystem.architecture() == arch); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return IntrospectPlatformSpec.class.hashCode(); | ||
} | ||
|
||
@Override | ||
public boolean equals(Object obj) { | ||
return (obj instanceof IntrospectPlatformSpec); | ||
} | ||
} |
49 changes: 49 additions & 0 deletions
49
components/native-loader/src/main/java/datadog/nativeloader/LibDirBasedPathLocator.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package datadog.nativeloader; | ||
|
||
import java.io.File; | ||
import java.net.MalformedURLException; | ||
import java.net.URL; | ||
import java.util.Arrays; | ||
|
||
/** LibDirBasedPathLocator locates libraries inside a list of library directories */ | ||
final class LibDirBasedPathLocator implements PathLocator { | ||
private final File[] libDirs; | ||
|
||
public LibDirBasedPathLocator(File... libDirs) { | ||
this.libDirs = libDirs; | ||
} | ||
|
||
@Override | ||
public URL locate(String component, String path) { | ||
String fullPath = PathUtils.concatPath(component, path); | ||
|
||
for (File libDir : this.libDirs) { | ||
File libFile = new File(libDir, fullPath); | ||
if (libFile.exists()) return toUrl(libFile); | ||
} | ||
|
||
return null; | ||
} | ||
|
||
@SuppressWarnings("deprecation") | ||
private static final URL toUrl(File file) { | ||
try { | ||
return file.toURL(); | ||
} catch (MalformedURLException e) { | ||
return null; | ||
} | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Arrays.hashCode(this.libDirs); | ||
} | ||
|
||
@Override | ||
public boolean equals(Object obj) { | ||
if (!(obj instanceof LibDirBasedPathLocator)) return false; | ||
|
||
LibDirBasedPathLocator that = (LibDirBasedPathLocator) obj; | ||
return Arrays.equals(this.libDirs, that.libDirs); | ||
} | ||
} |
81 changes: 81 additions & 0 deletions
81
components/native-loader/src/main/java/datadog/nativeloader/LibFile.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package datadog.nativeloader; | ||
|
||
import java.io.File; | ||
import java.nio.file.Path; | ||
|
||
/** | ||
* 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 | ||
* </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 fromFile(String libName, File file) { | ||
return new LibFile(libName, file, NO_CLEAN_UP); | ||
} | ||
|
||
static final LibFile fromTempFile(String libName, File file) { | ||
return new LibFile(libName, file, CLEAN_UP); | ||
} | ||
|
||
final String libName; | ||
|
||
final File file; | ||
final boolean needsCleanup; | ||
|
||
LibFile(String libName, File file, boolean needsCleanup) { | ||
this.libName = libName; | ||
|
||
this.file = file; | ||
this.needsCleanup = needsCleanup; | ||
} | ||
|
||
/** Indicates if this library was "preloaded" */ | ||
public boolean isPreloaded() { | ||
return (this.file == null); | ||
} | ||
|
||
/** Loads the underlying library into the JVM */ | ||
public void load() throws LibraryLoadException { | ||
if (this.isPreloaded()) return; | ||
|
||
try { | ||
Runtime.getRuntime().load(this.getAbsolutePath()); | ||
} catch (Throwable t) { | ||
throw new LibraryLoadException(this.libName, t); | ||
} | ||
} | ||
|
||
/** Provides a File to the library -- returns null for pre-loaded libraries */ | ||
public final File toFile() { | ||
return this.file; | ||
} | ||
|
||
/** Provides a Path to the library -- return null for pre-loaded libraries */ | ||
public final Path toPath() { | ||
return this.file == null ? null : this.file.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(); | ||
} | ||
|
||
/** Schedules clean-up of underlying file -- if the file is a temp file */ | ||
@Override | ||
public void close() { | ||
if (this.needsCleanup) { | ||
NativeLoader.delete(this.file); | ||
} | ||
} | ||
} |
29 changes: 29 additions & 0 deletions
29
components/native-loader/src/main/java/datadog/nativeloader/LibraryLoadException.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package datadog.nativeloader; | ||
|
||
/** Exception raised when NativeLoader fails to resolve or load a library */ | ||
public class LibraryLoadException extends Exception { | ||
static final String UNSUPPORTED_OS = "Unsupported OS"; | ||
static final String UNSUPPORTED_ARCH = "Unsupported arch"; | ||
|
||
private static final long serialVersionUID = 1L; | ||
|
||
public LibraryLoadException(String libName) { | ||
super(message(libName)); | ||
} | ||
|
||
public LibraryLoadException(String libName, Throwable cause) { | ||
this(message(libName), cause.getMessage(), cause); | ||
} | ||
|
||
public LibraryLoadException(String libName, String message) { | ||
super(message(libName) + " - " + message); | ||
} | ||
|
||
public LibraryLoadException(String libName, String message, Throwable cause) { | ||
super(message(libName) + " - " + message, cause); | ||
} | ||
|
||
static final String message(String libName) { | ||
return "Unable to resolve library " + libName; | ||
} | ||
} |
20 changes: 20 additions & 0 deletions
20
components/native-loader/src/main/java/datadog/nativeloader/LibraryResolver.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package datadog.nativeloader; | ||
|
||
import java.net.URL; | ||
|
||
/** | ||
* LibraryResolver encapsulates a library resolution strategy | ||
* | ||
* <p>The LibraryResolver should use the provided {@link PathLocator} to locate the desired | ||
* resources. The LibraryResolver may try multiple locations to find the best possible library to | ||
* use. | ||
*/ | ||
@FunctionalInterface | ||
public interface LibraryResolver { | ||
default boolean isPreloaded(PlatformSpec platform, String libName) { | ||
return false; | ||
} | ||
|
||
URL resolve(PathLocator pathLocator, String component, PlatformSpec platformSpec, String libName) | ||
throws Exception; | ||
} |
44 changes: 44 additions & 0 deletions
44
components/native-loader/src/main/java/datadog/nativeloader/LibraryResolvers.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package datadog.nativeloader; | ||
|
||
import java.net.URL; | ||
import java.util.Arrays; | ||
import java.util.HashSet; | ||
import java.util.Set; | ||
|
||
public final class LibraryResolvers { | ||
dougqh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
private LibraryResolvers() {} | ||
|
||
public static final LibraryResolver defaultLibraryResolver() { | ||
return flatDirs(); | ||
} | ||
|
||
public static final LibraryResolver withPreloaded( | ||
LibraryResolver baseResolver, String... preloadedLibNames) { | ||
dougqh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return withPreloaded(baseResolver, new HashSet<>(Arrays.asList(preloadedLibNames))); | ||
} | ||
|
||
public static final LibraryResolver withPreloaded( | ||
LibraryResolver baseResolver, Set<String> preloadedLibNames) { | ||
return new LibraryResolver() { | ||
@Override | ||
public boolean isPreloaded(PlatformSpec platform, String libName) { | ||
return preloadedLibNames.contains(libName); | ||
} | ||
|
||
@Override | ||
public URL resolve( | ||
PathLocator pathLocator, String component, PlatformSpec platformSpec, String libName) | ||
throws Exception { | ||
return baseResolver.resolve(pathLocator, component, platformSpec, libName); | ||
} | ||
}; | ||
} | ||
|
||
public static final LibraryResolver flatDirs() { | ||
return FlatDirLibraryResolver.INSTANCE; | ||
} | ||
|
||
public static final LibraryResolver nestedDirs() { | ||
return NestedDirLibraryResolver.INSTANCE; | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thought: Does it make sense to investigate later an FFM API version of the native part for post JDK22 users? This might, however, require to deal with Arena / Scope.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't really looked into it, but yeah, I could see that making sense at some point.