Remove deprecated ExporterClassLoader and exporter factories
DELETE
javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/spi/exporter/MetricExporterFactory.java
-/*
- * Copyright The OpenTelemetry Authors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package io.opentelemetry.javaagent.spi.exporter;
-
-import io.opentelemetry.sdk.metrics.export.MetricExporter;
-import java.util.Properties;
-import java.util.Set;
-
-/**
- * A {@link MetricExporterFactory} acts as the bootstrap for a {@link MetricExporter}
- * implementation. An exporter must register its implementation of a {@link MetricExporterFactory}
- * through the Java SPI framework.
- *
- * @deprecated Use {@code io.opentelemetry.sdk.autoconfigure.spi.ConfigurableMetricExporterProvider}
- * from the {@code opentelemetry-sdk-extension-autoconfigure} instead.
- */
-@Deprecated
-public interface MetricExporterFactory {
- /**
- * Creates an instance of a {@link MetricExporter} based on the provided configuration.
- *
- * @param config The configuration
- * @return An implementation of a {@link MetricExporter}
- */
- MetricExporter fromConfig(Properties config);
-
- /**
- * Returns names of metric exporters supported by this factory.
- *
- * <p>Multiple names are useful for enabling a pair of span and metric exporters using the same
- * name, while still having separate names for enabling them individually.
- *
- * @return The exporter names supported by this factory
- */
- Set<String> getNames();
-}

DELETE
javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/spi/exporter/SpanExporterFactory.java
-/*
- * Copyright The OpenTelemetry Authors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package io.opentelemetry.javaagent.spi.exporter;
-
-import io.opentelemetry.sdk.trace.export.SpanExporter;
-import java.util.Properties;
-import java.util.Set;
-
-/**
- * A {@link SpanExporterFactory} acts as the bootstrap for a {@link SpanExporter} implementation. An
- * exporter must register its implementation of a {@link SpanExporterFactory} through the Java SPI
- * framework.
- *
- * @deprecated Use {@code io.opentelemetry.sdk.autoconfigure.spi.ConfigurableSpanExporterProvider}
- * from the {@code opentelemetry-sdk-extension-autoconfigure} instead.
- */
-@Deprecated
-public interface SpanExporterFactory {
- /**
- * Creates an instance of a {@link SpanExporter} based on the provided configuration.
- *
- * @param config The configuration
- * @return An implementation of a {@link SpanExporter}
- */
- SpanExporter fromConfig(Properties config);
-
- /**
- * Returns names of span exporters supported by this factory.
- *
- * <p>Multiple names are useful for enabling a pair of span and metric exporters using the same
- * name, while still having separate names for enabling them individually.
- *
- * @return The exporter names supported by this factory
- */
- Set<String> getNames();
-}

MODIFY
javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentTracerProviderConfigurer.java
javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/AgentTracerProviderConfigurer.java
package io.opentelemetry.javaagent.tooling;
import com.google.auto.service.AutoService;
-import io.opentelemetry.api.metrics.GlobalMeterProvider;
import io.opentelemetry.exporter.logging.LoggingSpanExporter;
import io.opentelemetry.instrumentation.api.config.Config;
-import io.opentelemetry.javaagent.spi.exporter.MetricExporterFactory;
-import io.opentelemetry.javaagent.spi.exporter.SpanExporterFactory;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.traces.SdkTracerProviderConfigurer;
-import io.opentelemetry.sdk.metrics.SdkMeterProvider;
-import io.opentelemetry.sdk.metrics.export.IntervalMetricReader;
-import io.opentelemetry.sdk.metrics.export.MetricExporter;
import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder;
-import io.opentelemetry.sdk.trace.SpanProcessor;
-import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor;
-import io.opentelemetry.sdk.trace.export.SpanExporter;
-import java.io.File;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.ServiceLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -34,8 +19,6 @@ import org.slf4j.LoggerFactory;
public class AgentTracerProviderConfigurer implements SdkTracerProviderConfigurer {
private static final Logger logger = LoggerFactory.getLogger(AgentTracerProviderConfigurer.class);
- static final String EXPORTER_JAR_CONFIG = "otel.javaagent.experimental.exporter.jar";
-
private static final String ADD_THREAD_DETAILS = "otel.javaagent.add-thread-details";
@Override
@@ -50,7 +33,6 @@ public class AgentTracerProviderConfigurer implements SdkTracerProviderConfigure
sdkTracerProviderBuilder.addSpanProcessor(new AddThreadDetailsSpanProcessor());
}
- maybeConfigureExporterJar(sdkTracerProviderBuilder);
maybeEnableLoggingExporter(sdkTracerProviderBuilder);
}
@@ -66,81 +48,4 @@ public class AgentTracerProviderConfigurer implements SdkTracerProviderConfigure
private static boolean loggingExporterIsNotAlreadyConfigured() {
return !Config.get().getString("otel.traces.exporter", "").equalsIgnoreCase("logging");
}
-
- private static void maybeConfigureExporterJar(SdkTracerProviderBuilder sdkTracerProviderBuilder) {
- Config config = Config.get();
- String exporterJar = config.getString(EXPORTER_JAR_CONFIG);
- if (exporterJar == null) {
- return;
- }
- installExportersFromJar(exporterJar, config, sdkTracerProviderBuilder);
- }
-
- // TODO remove in 1.6
- private static synchronized void installExportersFromJar(
- String exporterJar, Config config, SdkTracerProviderBuilder builder) {
- logger.warn(
- "{} is deprecated and will be removed soon! Please use {}",
- EXPORTER_JAR_CONFIG,
- ExtensionClassLoader.EXTENSIONS_CONFIG);
- URL url;
- try {
- url = new File(exporterJar).toURI().toURL();
- } catch (MalformedURLException e) {
- logger.warn("Filename could not be parsed: {}. Exporter is not installed", exporterJar);
- logger.warn("No valid exporter found. Tracing will run but spans are dropped");
- return;
- }
- ExporterClassLoader exporterLoader =
- new ExporterClassLoader(url, OpenTelemetryInstaller.class.getClassLoader());
-
- SpanExporterFactory spanExporterFactory =
- getExporterFactory(SpanExporterFactory.class, exporterLoader);
-
- if (spanExporterFactory != null) {
- installSpanExporter(spanExporterFactory, config, builder);
- } else {
- logger.warn("No span exporter found in {}", exporterJar);
- logger.warn("No valid exporter found. Tracing will run but spans are dropped");
- }
-
- MetricExporterFactory metricExporterFactory =
- getExporterFactory(MetricExporterFactory.class, exporterLoader);
- if (metricExporterFactory != null) {
- installMetricExporter(metricExporterFactory, config);
- }
- }
-
- private static <F> F getExporterFactory(Class<F> service, ExporterClassLoader exporterLoader) {
- ServiceLoader<F> serviceLoader = ServiceLoader.load(service, exporterLoader);
- Iterator<F> i = serviceLoader.iterator();
- if (i.hasNext()) {
- F factory = i.next();
- if (i.hasNext()) {
- logger.warn(
- "Exporter JAR defines more than one {}. Only the first one found will be used",
- service.getName());
- }
- return factory;
- }
- return null;
- }
-
- private static void installSpanExporter(
- SpanExporterFactory spanExporterFactory, Config config, SdkTracerProviderBuilder builder) {
- SpanExporter spanExporter = spanExporterFactory.fromConfig(config.asJavaProperties());
- SpanProcessor spanProcessor = BatchSpanProcessor.builder(spanExporter).build();
- builder.addSpanProcessor(spanProcessor);
- logger.info("Installed span exporter: {}", spanExporter.getClass().getName());
- }
-
- private static void installMetricExporter(
- MetricExporterFactory metricExporterFactory, Config config) {
- MetricExporter metricExporter = metricExporterFactory.fromConfig(config.asJavaProperties());
- IntervalMetricReader.builder()
- .setMetricExporter(metricExporter)
- .setMetricProducers(Collections.singleton((SdkMeterProvider) GlobalMeterProvider.get()))
- .buildAndStart();
- logger.info("Installed metric exporter: {}", metricExporter.getClass().getName());
- }
}

DELETE
javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ExporterClassLoader.java
-/*
- * Copyright The OpenTelemetry Authors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package io.opentelemetry.javaagent.tooling;
-
-import static io.opentelemetry.javaagent.tooling.ShadingRemapper.rule;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.util.Enumeration;
-import java.util.jar.JarFile;
-import java.util.jar.Manifest;
-import net.bytebuddy.jar.asm.ClassReader;
-import net.bytebuddy.jar.asm.ClassWriter;
-import net.bytebuddy.jar.asm.commons.ClassRemapper;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-@Deprecated
-public class ExporterClassLoader extends URLClassLoader {
-
- private static final Logger logger = LoggerFactory.getLogger(ExporterClassLoader.class);
-
- // We need to prefix the names to prevent the gradle shadowJar relocation rules from touching
- // them. It's possible to do this by excluding this class from shading, but it may cause issue
- // with transitive dependencies down the line.
- private static final ShadingRemapper remapper =
- new ShadingRemapper(
- rule("#io.opentelemetry.api", "#io.opentelemetry.javaagent.shaded.io.opentelemetry.api"),
- rule(
- "#io.opentelemetry.context",
- "#io.opentelemetry.javaagent.shaded.io.opentelemetry.context"),
- rule(
- "#io.opentelemetry.extension.aws",
- "#io.opentelemetry.javaagent.shaded.io.opentelemetry.extension.aws"),
- rule("#java.util.logging.Logger", "#io.opentelemetry.javaagent.bootstrap.PatchLogger"),
- rule("#org.slf4j", "#io.opentelemetry.javaagent.slf4j"));
-
- private final Manifest manifest;
-
- public ExporterClassLoader(URL url, ClassLoader parent) {
- super(new URL[] {url}, parent);
- this.manifest = getManifest(url);
- }
-
- @Override
- public Enumeration<URL> getResources(String name) throws IOException {
- // A small hack to prevent other exporters from being loaded by this classloader if they
- // should happen to appear on the classpath.
- if (name.equals("META-INF/services/io.opentelemetry.javaagent.spi.exporter.SpanExporterFactory")
- || name.equals(
- "META-INF/services/io.opentelemetry.javaagent.spi.exporter.MetricExporterFactory")) {
- return findResources(name);
- }
- return super.getResources(name);
- }
-
- @Override
- protected Class<?> findClass(String name) throws ClassNotFoundException {
- // Use resource loading to get the class as a stream of bytes, then use ASM to transform it.
- InputStream in = getResourceAsStream(name.replace('.', '/') + ".class");
- if (in == null) {
- throw new ClassNotFoundException(name);
- }
- try {
- byte[] bytes = remapClassBytes(in);
- definePackageIfNeeded(name);
- return defineClass(name, bytes, 0, bytes.length);
- } catch (IOException e) {
- throw new ClassNotFoundException(name, e);
- } finally {
- try {
- in.close();
- } catch (IOException e) {
- logger.debug(e.getMessage(), e);
- }
- }
- }
-
- private void definePackageIfNeeded(String className) {
- String packageName = getPackageName(className);
- if (packageName == null) {
- // default package
- return;
- }
- if (isPackageDefined(packageName)) {
- // package has already been defined
- return;
- }
- try {
- definePackage(packageName);
- } catch (IllegalArgumentException e) {
- // this exception is thrown when the package has already been defined, which is possible due
- // to race condition with the check above
- if (!isPackageDefined(packageName)) {
- // this shouldn't happen however
- logger.error(e.getMessage(), e);
- }
- }
- }
-
- private boolean isPackageDefined(String packageName) {
- return getPackage(packageName) != null;
- }
-
- private void definePackage(String packageName) {
- if (manifest == null) {
- definePackage(packageName, null, null, null, null, null, null, null);
- } else {
- definePackage(packageName, manifest, null);
- }
- }
-
- 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);
- return cw.toByteArray();
- }
-
- private static String getPackageName(String className) {
- int index = className.lastIndexOf('.');
- return index == -1 ? null : className.substring(0, index);
- }
-
- private static Manifest getManifest(URL url) {
- try (JarFile jarFile = new JarFile(url.toURI().getPath())) {
- return jarFile.getManifest();
- } catch (IOException | URISyntaxException e) {
- logger.warn(e.getMessage(), e);
- }
- return null;
- }
-}

MODIFY
javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ignore/GlobalIgnoredTypesConfigurer.java
javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ignore/GlobalIgnoredTypesConfigurer.java
@@ -10,7 +10,6 @@ import io.opentelemetry.instrumentation.api.config.Config;
import io.opentelemetry.javaagent.bootstrap.AgentClassLoader;
import io.opentelemetry.javaagent.extension.ignore.IgnoredTypesBuilder;
import io.opentelemetry.javaagent.extension.ignore.IgnoredTypesConfigurer;
-import io.opentelemetry.javaagent.tooling.ExporterClassLoader;
import io.opentelemetry.javaagent.tooling.ExtensionClassLoader;
@AutoService(IgnoredTypesConfigurer.class)
@@ -109,7 +108,6 @@ public class GlobalIgnoredTypesConfigurer implements IgnoredTypesConfigurer {
.ignoreClassLoader("org.apache.cxf.common.util.ASMHelper$TypeHelperClassLoader")
.ignoreClassLoader("sun.misc.Launcher$ExtClassLoader")
.ignoreClassLoader(AgentClassLoader.class.getName())
- .ignoreClassLoader(ExporterClassLoader.class.getName())
.ignoreClassLoader(ExtensionClassLoader.class.getName());
builder

DELETE
javaagent-tooling/src/test/groovy/io/opentelemetry/javaagent/tooling/ExporterClassLoaderTest.groovy
-/*
- * Copyright The OpenTelemetry Authors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package io.opentelemetry.javaagent.tooling
-
-import groovy.transform.CompileStatic
-import io.opentelemetry.javaagent.spi.exporter.MetricExporterFactory
-import io.opentelemetry.javaagent.spi.exporter.SpanExporterFactory
-import io.opentelemetry.sdk.metrics.export.MetricExporter
-import io.opentelemetry.sdk.trace.export.SpanExporter
-import spock.lang.Specification
-
-import java.nio.charset.StandardCharsets
-import java.util.jar.Attributes
-import java.util.jar.JarEntry
-import java.util.jar.JarFile
-import java.util.jar.JarOutputStream
-import java.util.jar.Manifest
-
-class ExporterClassLoaderTest extends Specification {
-
- // Verifies https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/542
- def "does not look in parent classloader for metric exporters"() {
- setup:
- def parentClassloader = new ParentClassLoader([createJarWithClasses(MetricExporterFactoryParent)] as URL[])
- def childClassloader = new ExporterClassLoader(createJarWithClasses(MetricExporterFactoryChild), parentClassloader)
-
- when:
- ServiceLoader<MetricExporterFactory> serviceLoader = ServiceLoader.load(MetricExporterFactory, childClassloader)
-
- then:
- serviceLoader.size() == 1
-
- and:
- childClassloader.manifest != null
-
- when:
- MetricExporterFactory instance = serviceLoader.iterator().next()
- Class clazz = instance.getClass()
-
- then:
- clazz.getClassLoader() == childClassloader
- }
-
- def "does not look in parent classloader for span exporters"() {
- setup:
- def parentClassloader = new ParentClassLoader([createJarWithClasses(SpanExporterFactoryParent)] as URL[])
- def childClassloader = new ExporterClassLoader(createJarWithClasses(SpanExporterFactoryChild), parentClassloader)
-
- when:
- ServiceLoader<SpanExporterFactory> serviceLoader = ServiceLoader.load(SpanExporterFactory, childClassloader)
-
- then:
- serviceLoader.size() == 1
-
- and:
- childClassloader.manifest != null
-
- when:
- SpanExporterFactory instance = serviceLoader.iterator().next()
- Class clazz = instance.getClass()
-
- then:
- clazz.getClassLoader() == childClassloader
- }
-
- // Verifies that loading of exporter jar succeeds when there is a space in path to exporter jar
- def "load jar with space in path"() {
- setup:
- def parentClassloader = new ParentClassLoader()
- // " .jar" is used to make path to jar contain a space
- def childClassloader = new ExporterClassLoader(createJarWithClasses(" .jar", MetricExporterFactoryChild), parentClassloader)
-
- when:
- ServiceLoader<MetricExporterFactory> serviceLoader = ServiceLoader.load(MetricExporterFactory, childClassloader)
-
- then:
- serviceLoader.size() == 1
-
- and:
- childClassloader.manifest != null
-
- when:
- MetricExporterFactory instance = serviceLoader.iterator().next()
- Class clazz = instance.getClass()
-
- then:
- clazz.getClassLoader() == childClassloader
-
- and:
- clazz.getPackage().getImplementationVersion() == "test-implementation-version"
- }
-
- static class MetricExporterFactoryParent implements MetricExporterFactory {
-
- @Override
- MetricExporter fromConfig(Properties config) {
- return null
- }
-
- @Override
- Set<String> getNames() {
- return null
- }
- }
-
- static class MetricExporterFactoryChild implements MetricExporterFactory {
-
- @Override
- MetricExporter fromConfig(Properties config) {
- return null
- }
-
- @Override
- Set<String> getNames() {
- return null
- }
- }
-
- static class SpanExporterFactoryParent implements SpanExporterFactory {
-
- @Override
- SpanExporter fromConfig(Properties config) {
- return null
- }
-
- @Override
- Set<String> getNames() {
- return null
- }
- }
-
- static class SpanExporterFactoryChild implements SpanExporterFactory {
-
- @Override
- SpanExporter fromConfig(Properties config) {
- return null
- }
-
- @Override
- Set<String> getNames() {
- return null
- }
- }
-
- static URL createJarWithClasses(final Class<?>... classes) {
- createJarWithClasses(".jar", classes)
- }
-
- static URL createJarWithClasses(final String suffix, final Class<?>... classes)
- throws IOException {
- File tmpJar = File.createTempFile(UUID.randomUUID().toString() + "-", suffix)
- tmpJar.deleteOnExit()
-
- JarOutputStream target = new JarOutputStream(new FileOutputStream(tmpJar))
- for (Class<?> clazz : classes) {
- addToJar(clazz, clazz.getInterfaces()[0], target)
- }
-
- Manifest manifest = new Manifest()
- Attributes attributes = manifest.getMainAttributes()
- attributes.put(Attributes.Name.MANIFEST_VERSION, "1.0")
- attributes.put(Attributes.Name.SPECIFICATION_TITLE, "test-specification-title")
- attributes.put(Attributes.Name.SPECIFICATION_VERSION, "test-specification-version")
- attributes.put(Attributes.Name.SPECIFICATION_VENDOR, "test-specification-vendor")
- attributes.put(Attributes.Name.IMPLEMENTATION_TITLE, "test-implementation-title")
- attributes.put(Attributes.Name.IMPLEMENTATION_VERSION, "test-implementation-version")
- attributes.put(Attributes.Name.IMPLEMENTATION_VENDOR, "test-implementation-vendor")
-
- JarEntry manifestEntry = new JarEntry(JarFile.MANIFEST_NAME)
- target.putNextEntry(manifestEntry)
- manifest.write(target)
- target.closeEntry()
-
- target.close()
-
- return tmpJar.toURI().toURL()
- }
-
- //This is mostly copy-pasted from IntegrationTestUtils, but we need to save service files as well
- private static void addToJar(final Class<?> clazz, final Class<?> serviceInterface, final JarOutputStream jarOutputStream)
- throws IOException {
- String resourceName = getResourceName(clazz.getName())
-
- ClassLoader loader = clazz.getClassLoader()
- if (null == loader) {
- // bootstrap resources can be fetched through the system loader
- loader = ClassLoader.getSystemClassLoader()
- }
-
- InputStream inputStream = null
- try {
- JarEntry entry = new JarEntry(resourceName)
- jarOutputStream.putNextEntry(entry)
- inputStream = loader.getResourceAsStream(resourceName)
-
- byte[] buffer = new byte[1024]
- while (true) {
- int count = inputStream.read(buffer)
- if (count == -1) {
- break
- }
- jarOutputStream.write(buffer, 0, count)
- }
- jarOutputStream.closeEntry()
-
- JarEntry serviceEntry = new JarEntry("META-INF/services/" + serviceInterface.getName())
- jarOutputStream.putNextEntry(serviceEntry)
- jarOutputStream.write(clazz.getName().getBytes(StandardCharsets.UTF_8))
- jarOutputStream.closeEntry()
- } finally {
- if (inputStream != null) {
- inputStream.close()
- }
- }
- }
-
- /** com.foo.Bar -> com/foo/Bar.class */
- private static String getResourceName(final String className) {
- return className.replace('.', '/') + ".class"
- }
-
- @CompileStatic
- private static class ParentClassLoader extends URLClassLoader {
-
- ParentClassLoader() {
- super()
- }
-
- ParentClassLoader(URL[] urls) {
- super(urls)
- }
-
- @Override
- Package getPackage(String name) {
- // ExporterClassLoader uses getPackage to check whether package has already been
- // defined. As getPackage also searches packages from parent class loader we return
- // null here to ensure that package is defined in ExporterClassLoader.
- null
- }
-
- @Override
- Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
- // test classes are available in system class loader filter them so that
- // they would be loaded by ExporterClassLoader
- if (name.startsWith(ExporterClassLoaderTest.getName())) {
- throw new ClassNotFoundException(name)
- }
- return super.loadClass(name, resolve)
- }
- }
-}

Remove deprecated ExporterClassLoader and exporter factories (#4217)