Skip to content
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

Allow specifying a comma separated list of extensions #6137

Merged
merged 4 commits into from
Jun 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion examples/extension/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ To add the extension to the instrumentation agent:
-Dotel.javaagent.extensions=build/libs/opentelemetry-java-instrumentation-extension-demo-1.0-all.jar
-jar myapp.jar
```
Note: to load multiple extensions, you can specify a directory path for the `otel.javaagent.extensions` value.
Note: to load multiple extensions, you can specify a comma-separated list of extension jars or directories (that
contain extension jars) for the `otel.javaagent.extensions` value.

## Embed extensions in the OpenTelemetry Agent

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import java.nio.channels.ReadableByteChannel;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
Expand Down Expand Up @@ -114,27 +115,38 @@ private static URLClassLoader getDelegate(ClassLoader parent, URL extensionUrl)
return new ExtensionClassLoader(new URL[] {extensionUrl}, parent);
}

private static List<URL> parseLocation(String locationName, File javaagentFile) {
// visible for testing
static List<URL> parseLocation(String locationName, File javaagentFile) {
if (locationName == null) {
return Collections.emptyList();
}

List<URL> result = new ArrayList<>();
for (String location : locationName.split(",")) {
parseLocation(location, javaagentFile, result);
}

if (locationName == null) {
return result;
return result;
}

private static void parseLocation(String locationName, File javaagentFile, List<URL> locations) {
if (locationName.isEmpty()) {
return;
}

File location = new File(locationName);
if (isJar(location)) {
addFileUrl(result, location);
addFileUrl(locations, location);
} else if (location.isDirectory()) {
File[] files = location.listFiles(ExtensionClassLoader::isJar);
if (files != null) {
for (File file : files) {
if (!file.getAbsolutePath().equals(javaagentFile.getAbsolutePath())) {
addFileUrl(result, file);
if (isJar(file) && !file.getAbsolutePath().equals(javaagentFile.getAbsolutePath())) {
addFileUrl(locations, file);
}
}
}
}
return result;
}

private static boolean isJar(File f) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.tooling;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.io.File;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.jar.Attributes;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

public class ExtensionClassLoaderTest {
private static final File AGENT_FILE = new File("/agent.jar");

@Test
void testParseLocation(@TempDir Path outputDir) throws Exception {
String jarPath1 = createJar("test-1.jar", outputDir);
String jarPath2 = createJar("test-2.jar", outputDir);
// test that non-jar file is skipped
Files.createFile(outputDir.resolve("test.txt"));
{
List<URL> result = ExtensionClassLoader.parseLocation(jarPath1, AGENT_FILE);
assertEquals(1, result.size());
}
{
// empty paths are ignored
List<URL> result = ExtensionClassLoader.parseLocation("," + jarPath1 + ",,,", AGENT_FILE);
assertEquals(1, result.size());
}
{
List<URL> result = ExtensionClassLoader.parseLocation(jarPath1 + "," + jarPath2, AGENT_FILE);
assertEquals(2, result.size());
}
{
List<URL> result = ExtensionClassLoader.parseLocation(outputDir.toString(), AGENT_FILE);
assertEquals(2, result.size());
}
{
List<URL> result =
ExtensionClassLoader.parseLocation(
outputDir + "," + jarPath1 + "," + jarPath2, AGENT_FILE);
assertEquals(4, result.size());
}
{
List<URL> result = ExtensionClassLoader.parseLocation("/anydir", AGENT_FILE);
assertEquals(0, result.size());
}
{
List<URL> result = ExtensionClassLoader.parseLocation("/anyfile.jar", AGENT_FILE);
assertEquals(0, result.size());
}
{
List<URL> result = ExtensionClassLoader.parseLocation(jarPath1 + ",/anyfile.jar", AGENT_FILE);
assertEquals(1, result.size());
}
}

private static String createJar(String name, Path directory) throws Exception {
Path jarPath = directory.resolve(name);
createJar(jarPath);
return jarPath.toAbsolutePath().toString();
}

private static void createJar(Path path) throws Exception {
Manifest manifest = new Manifest();
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
try (JarOutputStream jar = new JarOutputStream(Files.newOutputStream(path), manifest)) {
// empty jar
}
}
}