diff --git a/opentracing-specialagent-util/src/main/java/io/opentracing/contrib/specialagent/InstrumentIntegration.java b/opentracing-specialagent-util/src/main/java/io/opentracing/contrib/specialagent/InstrumentIntegration.java new file mode 100644 index 000000000..ee49cbc2e --- /dev/null +++ b/opentracing-specialagent-util/src/main/java/io/opentracing/contrib/specialagent/InstrumentIntegration.java @@ -0,0 +1,127 @@ +package io.opentracing.contrib.specialagent; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class InstrumentIntegration { + private static String GLOBAL_CLASSPATH_OPT = "sa.integration.classpath"; + + private static final Logger logger = Logger.getLogger(InstrumentIntegration.class); + + private static InstrumentIntegration SINGLETON; + + private URLClassLoader GLOBAL; + + private Map extensionClassLoaders = new HashMap<>(); + + public static void init(final Map extensionClasspaths) { + if (SINGLETON != null) { + SINGLETON.clear(); + } + SINGLETON = new InstrumentIntegration(extensionClasspaths); + } + + public static InstrumentIntegration getInstance() { + if (SINGLETON == null) { + init(null); + } + return SINGLETON; + } + + private InstrumentIntegration(final Map extensionClasspaths) { + if (extensionClasspaths != null) { + final String[] globalClasspaths = extensionClasspaths.get(GLOBAL_CLASSPATH_OPT); + if (GLOBAL == null && globalClasspaths != null) { + GLOBAL = this.createClassLoader(globalClasspaths, null); + } + + for (final Map.Entry entry : extensionClasspaths.entrySet()) { + extensionClassLoaders.put(entry.getKey(), this.createClassLoader(entry.getValue(), GLOBAL)); + } + } + } + + private URLClassLoader createClassLoader(final String[] classpaths, ClassLoader parent) { + final URL[] urls = new URL[classpaths.length]; + for (int i = 0; i < classpaths.length; ++i) { + final String part = classpaths[i]; + try { + urls[i] = new URL("file", "", part.endsWith(".jar") || part.endsWith("/") ? part : part + "/"); + } catch (final MalformedURLException e) { + logger.log(Level.WARNING, part + "is not a valid URL"); + } + } + return new URLClassLoader(urls, parent); + } + + public List getExtensionInstances(Class clazz, String extensionClasspathOpt, String... classNames) { + final ClassLoader classLoader = this.extensionClassLoaders.get(extensionClasspathOpt); + final List result = new ArrayList<>(); + if (classNames != null) { + for (final String className : classNames) { + final T object = newInstance(classLoader, clazz, className); + if (object != null) + result.add(object); + } + } + return result; + } + + private T newInstance(final ClassLoader classLoader, final Class clazz, final String className) { + try { + Class decoratorClass = loadClass(classLoader, className); + if (decoratorClass == null) + decoratorClass = loadClass(clazz.getClassLoader(), className); + + if (decoratorClass == null) + return null; + + if (clazz.isAssignableFrom(decoratorClass)) + return clazz.cast(decoratorClass.newInstance()); + + logger.log(Level.WARNING, className + " is not a subclass of " + clazz.getName()); + } catch (final InstantiationException | IllegalAccessException e) { + logger.log(Level.SEVERE, e.getMessage(), e); + } + + return null; + } + + private Class loadClass(ClassLoader classLoader, String className) { + if (classLoader == null) + return null; + + try { + return classLoader.loadClass(className); + } catch (final ClassNotFoundException e) { + logger.log(Level.SEVERE, e.getMessage(), e); + } + + return null; + } + + private void clear() { + if (this.GLOBAL != null) { + try { + this.GLOBAL.close(); + } catch (IOException e) { + logger.finer("Closing classLoader fail: " + e.getMessage()); + } + this.GLOBAL = null; + } + for (URLClassLoader classLoader : this.extensionClassLoaders.values()) { + try { + classLoader.close(); + } catch (IOException e) { + logger.finer("Closing classLoader fail: " + e.getMessage()); + } + } + this.extensionClassLoaders.clear(); + } +} diff --git a/opentracing-specialagent-util/src/main/java/io/opentracing/contrib/specialagent/SpanDecoratorLoader.java b/opentracing-specialagent-util/src/main/java/io/opentracing/contrib/specialagent/SpanDecoratorLoader.java new file mode 100644 index 000000000..2d9ae5697 --- /dev/null +++ b/opentracing-specialagent-util/src/main/java/io/opentracing/contrib/specialagent/SpanDecoratorLoader.java @@ -0,0 +1,39 @@ +package io.opentracing.contrib.specialagent; + +import java.util.Collections; +import java.util.List; + +public class SpanDecoratorLoader { + public static final String DECORATOR_SEPARATOR = ","; + public static final String SPAN_DECORATORS_OPT_PREFIX = "sa.integration."; + public static final String SPAN_DECORATORS_CLASSPATH_OPT_SURFIX = ".spanDecorators.classpath"; + public static final String SPAN_DECORATORS_OPT_SURFIX = ".spanDecorators"; + + private String spanDecoratorClasspathOpt; + private String spanDecoratorsOpt; + private Class spanDecoratorClass; + + public static SpanDecoratorLoader newInstance(String ruleName, Class spanDecoratorClass) { + return new SpanDecoratorLoader<>(ruleName, spanDecoratorClass); + } + + private SpanDecoratorLoader(String ruleName, Class spanDecoratorClass) { + this.spanDecoratorClass = spanDecoratorClass; + this.spanDecoratorClasspathOpt = SPAN_DECORATORS_OPT_PREFIX + ruleName + SPAN_DECORATORS_CLASSPATH_OPT_SURFIX; + this.spanDecoratorsOpt = SPAN_DECORATORS_OPT_PREFIX + ruleName + SPAN_DECORATORS_OPT_SURFIX; + } + + public List getSpanDecorators(SD defultSpanDecorator) { + final String spanDecoratorsArgs = System.getProperty(spanDecoratorsOpt); + String[] spanDecoratorNames = null; + if (spanDecoratorsArgs != null) { + spanDecoratorNames = spanDecoratorsArgs.split(DECORATOR_SEPARATOR); + } + final List spanDecorators = InstrumentIntegration.getInstance().getExtensionInstances(this.spanDecoratorClass, + this.spanDecoratorClasspathOpt, spanDecoratorNames); + if (defultSpanDecorator != null && (spanDecorators == null || spanDecorators.isEmpty())) { + return Collections.singletonList(defultSpanDecorator); + } + return spanDecorators; + } +} diff --git a/opentracing-specialagent/src/main/java/io/opentracing/contrib/specialagent/SpecialAgent.java b/opentracing-specialagent/src/main/java/io/opentracing/contrib/specialagent/SpecialAgent.java index 0d6f463b4..65f2ace32 100644 --- a/opentracing-specialagent/src/main/java/io/opentracing/contrib/specialagent/SpecialAgent.java +++ b/opentracing-specialagent/src/main/java/io/opentracing/contrib/specialagent/SpecialAgent.java @@ -201,6 +201,8 @@ private static void load(final Manager manager, final File[] ruleFiles, final Is final HashMap integrationRuleNameToEnable = new HashMap<>(); final HashMap traceExporterNameToEnable = new HashMap<>(); final File[] classPaths = SpecialAgentUtil.parseConfiguration(properties, verbosePluginNames, integrationRuleNameToEnable, traceExporterNameToEnable); + final Map integrationClasspaths = SpecialAgentUtil.parseIntegrationConfiguration(properties); + InstrumentIntegration.init(integrationClasspaths); final boolean allIntegrationsEnabled = !integrationRuleNameToEnable.containsKey("*") || integrationRuleNameToEnable.remove("*"); if (logger.isLoggable(Level.FINER)) diff --git a/opentracing-specialagent/src/main/java/io/opentracing/contrib/specialagent/SpecialAgentUtil.java b/opentracing-specialagent/src/main/java/io/opentracing/contrib/specialagent/SpecialAgentUtil.java index 9ace800b4..a7717bf75 100644 --- a/opentracing-specialagent/src/main/java/io/opentracing/contrib/specialagent/SpecialAgentUtil.java +++ b/opentracing-specialagent/src/main/java/io/opentracing/contrib/specialagent/SpecialAgentUtil.java @@ -29,6 +29,7 @@ import java.security.CodeSource; import java.util.Arrays; import java.util.Enumeration; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; @@ -81,6 +82,20 @@ else if (key.equals("sa.include")) { return includedPlugins; } + static Map parseIntegrationConfiguration(final Map properties) { + final HashMap integrationClasspaths = new HashMap<>(); + for (final Map.Entry property : properties.entrySet()) { + final String key = property.getKey(); + final String value = property.getValue(); + + if (key.contains("integration") && key.endsWith("classpath")) { + final String[] classpaths = value.split(File.pathSeparator); + integrationClasspaths.put(key, classpaths); + } + } + return integrationClasspaths; + } + static JarFile createTempJarFile(final File dir) throws IOException { final Path dirPath = dir.toPath(); final Path zipPath = Files.createTempFile("specialagent", ".jar"); diff --git a/rule/apache-httpclient/src/main/java/io/opentracing/contrib/specialagent/rule/apache/httpclient/Configuration.java b/rule/apache-httpclient/src/main/java/io/opentracing/contrib/specialagent/rule/apache/httpclient/Configuration.java index 89e8d8945..214740047 100644 --- a/rule/apache-httpclient/src/main/java/io/opentracing/contrib/specialagent/rule/apache/httpclient/Configuration.java +++ b/rule/apache-httpclient/src/main/java/io/opentracing/contrib/specialagent/rule/apache/httpclient/Configuration.java @@ -15,77 +15,15 @@ package io.opentracing.contrib.specialagent.rule.apache.httpclient; -import java.io.File; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.ArrayList; import java.util.List; -import io.opentracing.contrib.specialagent.Level; -import io.opentracing.contrib.specialagent.Logger; +import io.opentracing.contrib.specialagent.SpanDecoratorLoader; public class Configuration { - public static final Logger logger = Logger.getLogger(Configuration.class); - public static final String SPAN_DECORATORS = "sa.integration.apache:httpclient.spanDecorators"; - public static final String SPAN_DECORATORS_CLASSPATH = "sa.integration.apache:httpclient.spanDecorators.classpath"; - public static final String DECORATOR_SEPARATOR = ","; + public static final List spanDecorators = parseSpanDecorators(); - public static final List spanDecorators = parseSpanDecorators(System.getProperty(SPAN_DECORATORS)); - private static ClassLoader decoratorClassLoader; - - static List parseSpanDecorators(final String spanDecoratorsArgs) { - final List result = new ArrayList<>(); - if (spanDecoratorsArgs != null) { - final String[] parts = spanDecoratorsArgs.split(DECORATOR_SEPARATOR); - for (final String part : parts) { - final ApacheClientSpanDecorator decorator = newSpanDecoratorInstance(part); - if (decorator != null) - result.add(decorator); - } - } - - if (result.isEmpty()) - result.add(new ApacheClientSpanDecorator.StandardTags()); - - return result; - } - - private static ApacheClientSpanDecorator newSpanDecoratorInstance(final String className) { - try { - final Class decoratorClass = getDecoratorClassLoader().loadClass(className); - if (ApacheClientSpanDecorator.class.isAssignableFrom(decoratorClass)) - return (ApacheClientSpanDecorator)decoratorClass.newInstance(); - - logger.log(Level.WARNING, className + " is not a subclass of " + ApacheClientSpanDecorator.class.getName()); - } - catch (final ClassNotFoundException | IllegalAccessException | InstantiationException e) { - logger.log(Level.SEVERE, e.getMessage(), e); - } - - return null; - } - - private static ClassLoader getDecoratorClassLoader() { - if (decoratorClassLoader != null) - return decoratorClassLoader; - - final String spanDecoratorsClassPath = System.getProperty(SPAN_DECORATORS_CLASSPATH); - if (spanDecoratorsClassPath == null || spanDecoratorsClassPath.isEmpty()) - return decoratorClassLoader = ApacheClientSpanDecorator.class.getClassLoader(); - - final String[] parts = spanDecoratorsClassPath.split(File.pathSeparator); - final URL[] urls = new URL[parts.length]; - for (int i = 0; i < parts.length; ++i) { - final String part = parts[i]; - try { - urls[i] = new URL("file", "", part.endsWith(".jar") || part.endsWith("/") ? part : part + "/"); - } - catch (final MalformedURLException e) { - logger.log(Level.WARNING, part + "is not a valid URL"); - } - } - - return decoratorClassLoader = new URLClassLoader(urls, ApacheClientSpanDecorator.class.getClassLoader()); + static List parseSpanDecorators() { + return SpanDecoratorLoader.newInstance("apache:httpclient", ApacheClientSpanDecorator.class) + .getSpanDecorators(new ApacheClientSpanDecorator.StandardTags()); } } \ No newline at end of file diff --git a/rule/apache-httpclient/src/test/java/io/opentracing/contrib/specialagent/rule/apache/httpclient/ConfigurationTest.java b/rule/apache-httpclient/src/test/java/io/opentracing/contrib/specialagent/rule/apache/httpclient/ConfigurationTest.java index b235920b2..76150c3a1 100644 --- a/rule/apache-httpclient/src/test/java/io/opentracing/contrib/specialagent/rule/apache/httpclient/ConfigurationTest.java +++ b/rule/apache-httpclient/src/test/java/io/opentracing/contrib/specialagent/rule/apache/httpclient/ConfigurationTest.java @@ -34,7 +34,10 @@ public void testImplicitSpanDecorators() { } private static void testDecorators(final String spanDecoratorsArgs, final Class ... expecteds) { - final List decorators = Configuration.parseSpanDecorators(spanDecoratorsArgs); + System.clearProperty("sa.integration.apache:httpclient.spanDecorators"); + if(spanDecoratorsArgs != null) + System.setProperty("sa.integration.apache:httpclient.spanDecorators", spanDecoratorsArgs); + final List decorators = Configuration.parseSpanDecorators(); assertEquals(expecteds.length, decorators.size()); final List> list = Arrays.asList(expecteds); for (final ApacheClientSpanDecorator decorator : decorators) diff --git a/rule/apache-httpclient/src/test/java/io/opentracing/contrib/specialagent/rule/apache/httpclient/HttpClientTest.java b/rule/apache-httpclient/src/test/java/io/opentracing/contrib/specialagent/rule/apache/httpclient/HttpClientTest.java index 415ed0187..eac22543d 100644 --- a/rule/apache-httpclient/src/test/java/io/opentracing/contrib/specialagent/rule/apache/httpclient/HttpClientTest.java +++ b/rule/apache-httpclient/src/test/java/io/opentracing/contrib/specialagent/rule/apache/httpclient/HttpClientTest.java @@ -45,7 +45,8 @@ public void before(final MockTracer tracer) { @Test public void test(final MockTracer tracer) { - System.setProperty(Configuration.SPAN_DECORATORS, "io.opentracing.contrib.specialagent.rule.apache.httpclient.ApacheClientSpanDecorator$StandardTags,io.opentracing.contrib.specialagent.rule.apache.httpclient.MockSpanDecorator"); + System.setProperty("sa.integration.apache:httpclient.spanDecorators", + "io.opentracing.contrib.specialagent.rule.apache.httpclient.ApacheClientSpanDecorator$StandardTags,io.opentracing.contrib.specialagent.rule.apache.httpclient.MockSpanDecorator"); final CloseableHttpClient httpClient = HttpClients.createDefault(); final String url = "http://localhost:12345";