-
Notifications
You must be signed in to change notification settings - Fork 867
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
First POC for external extension loading #2881
Conversation
javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ExtensionClassLoader.java
Show resolved
Hide resolved
...gent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/RemappingUrlStreamHandler.java
Outdated
Show resolved
Hide resolved
javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ExtensionClassLoader.java
Show resolved
Hide resolved
private static byte[] remapClassBytes(InputStream in) throws IOException { | ||
ClassWriter cw = new ClassWriter(0); | ||
ClassReader cr = new ClassReader(in); | ||
cr.accept(new ClassRemapper(cw, remapper), ClassReader.EXPAND_FRAMES); |
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.
out of curiosity is EXPAND_FRAMES
really needed for remapping?
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.
No idea, this was copy-paste from ExporterClassLoader
...gent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/RemappingUrlStreamHandler.java
Outdated
Show resolved
Hide resolved
I think this PR is now ready for review and then merge. It is still an open question, should we go with URLHandler or move everything into classloader itself. But in anyway I think we should have one way to do that, both in this new classloader and in AgentClassloder. Thus discussion in #2912 is very relevant here as well (/cc @laurit ) |
Constructor constructor = | ||
loaderClass.getDeclaredConstructor(URL.class, String.class, ClassLoader.class); | ||
return (ClassLoader) constructor.newInstance(bootstrapUrl, innerJarFilename, agentParent); | ||
Constructor<ClassLoader> constructor = |
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.
is suspect that reflection isn't really necessary here, could probably just use new AgentClassLoader
ClassLoader agentClassLoader = | ||
constructor.newInstance(bootstrapUrl, innerJarFilename, agentParent); | ||
|
||
Class<?> extensionClassLoaderClass = |
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.
reflection here could be avoided by either doing this inside AgentInstaller
or moving ExtensionClassLoader
out of inst/
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 will touch this code again when doing support for multiple extension jars.
URL extension = | ||
parseLocation( | ||
System.getProperty( | ||
"otel.javaagent.experimental.extensions", |
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.
System property is named extensions but seems like it only accepts 1 file. Should it be possible to only add 1 extension or multiple?
If multiple do they all live in the same class loader or would each get its own?
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.
As class comment says, we want to support scanning the folder for several jars. In that case every jar should get a separate classloader, I think. This is a goal for a subsequent PR.
...gent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/RemappingUrlStreamHandler.java
Outdated
Show resolved
Hide resolved
javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentInstaller.java
Outdated
Show resolved
Hide resolved
} | ||
|
||
public static ClassLoader getInstance(ClassLoader parent) { | ||
// TODO add support for old deprecated property otel.javaagent.experimental.exporter.jar |
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.
Dunno if it's worth it vs just keeping ExporterClassLoader
for a little bit
extension = | ||
parseLocation( | ||
System.getProperty( | ||
"otel.javaagent.experimental.initializer.jar", |
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.
Similarly, I'd probably just leave our current code until we get rid of the property instead of complicating this class with backwards compatibility.
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.
When I add support for several jars, this will not be a big complication...
javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ExtensionClassLoader.java
Show resolved
Hide resolved
javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ExtensionClassLoader.java
Outdated
Show resolved
Hide resolved
private final JarFile delegateJarFile; | ||
private final JarEntry entry; | ||
|
||
private InputStream cachedResult; |
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.
Is only one URLConnection
ever instantiated? Not sure when openConnection
is called during classload
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.
Certainly not one, why do you ask?
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.
If not once, I guess this cache is never used? Should it be a static cache?
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.
A, that's the catch :) it IS read/called at least 2 times by URLClassoader
. Old AgentClassLoader
also had an optimisation for this double reading.
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.
Caching Inputstream
this way is weird. As streams can be read only once without using mark/reset then this caching would make sense only when someone opens the stream and doesn't use it at all and then reopens the stream and actually reads it. Perhaps this isn't really needed any more? If it is needed then caching byte array and returning a new ByteArrayInputStream
from getInputStream
would be nicer.
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.
handler.getInputStream()
is called twice:
- In
jdk.internal.loader.URLClassPath.Loader#getResource(java.lang.String, boolean)
, where exactly "someone opens the stream and doesn't use it at all" happens - in
jdk.internal.loader.Resource#cachedInputStream
, when stream is actually used afterwards.
But I agree with you that caching byte arrays makes more sense, will do it.
...gent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/RemappingUrlStreamHandler.java
Outdated
Show resolved
Hide resolved
examples/extension/smoke-tests/src/test/java/com/example/javaagent/smoketest/SmokeTest.java
Outdated
Show resolved
Hide resolved
javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ExtensionClassLoader.java
Show resolved
Hide resolved
...gent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/RemappingUrlStreamHandler.java
Outdated
Show resolved
Hide resolved
javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/RemappingUrlConnection.java
Show resolved
Hide resolved
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.
A bit uncertain of the caching but basically looks good
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.
javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentInstaller.java
Outdated
Show resolved
Hide resolved
examples/extension/custom/src/main/java/com/example/javaagent/DemoIdGenerator.java
Outdated
Show resolved
Hide resolved
return null; | ||
} | ||
|
||
// That will NOT remap the content of files under META-INF/services |
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.
is this a problem?
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.
Not for now
javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ExtensionClassLoader.java
Show resolved
Hide resolved
We discussed this with Lauri and agreed that it is Ok for now to have different ways to do things. I will review it later. |
👍 |
javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/RemappingUrlConnection.java
Outdated
Show resolved
Hide resolved
...gent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/RemappingUrlStreamHandler.java
Outdated
Show resolved
Hide resolved
static void setupSpec() { | ||
backend = | ||
new GenericContainer<>( | ||
"open-telemetry-docker-dev.bintray.io/java/smoke-fake-backend:latest") |
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.
Smoketests use ghcr.io/open-telemetry/java-test-containers:smoke-fake-backend-20210324.684269693
🥳 |
Alternative approach to #2774