From e6737ac04fc97c7a56d8c9f998f04ad3c350ed23 Mon Sep 17 00:00:00 2001 From: Felix Barnsteiner Date: Wed, 1 Jul 2020 10:37:38 +0200 Subject: [PATCH] Universal bootdelegation (#1259) --- CHANGELOG.asciidoc | 4 + .../co/elastic/apm/agent/bci/AgentMain.java | 3 +- .../agent/bci/OsgiBootDelegationEnabler.java | 78 ------------ .../configuration/CoreConfiguration.java | 43 ------- ...lastic.apm.agent.context.LifecycleListener | 1 - .../bci/OsgiBootDelegationEnablerTest.java | 109 ----------------- .../apm-bootdelegation-plugin/pom.xml | 19 +++ ...pDelegationClassLoaderInstrumentation.java | 112 ++++++++++++++++++ .../agent/bootdelegation}/package-info.java | 2 +- ...ic.apm.agent.bci.ElasticApmInstrumentation | 1 + apm-agent-plugins/apm-mule4-plugin/pom.xml | 26 ---- ...rrideClassLoaderLookupInstrumentation.java | 95 --------------- ...ic.apm.agent.bci.ElasticApmInstrumentation | 1 - .../wildfly/WildFlyLifecycleListener.java | 48 -------- ...lastic.apm.agent.context.LifecycleListener | 1 - apm-agent-plugins/pom.xml | 2 +- docs/configuration.asciidoc | 66 +---------- docs/setup-attach-api.asciidoc | 4 +- docs/setup-attach-cli.asciidoc | 4 +- docs/supported-technologies.asciidoc | 3 - elastic-apm-agent/pom.xml | 10 +- 21 files changed, 150 insertions(+), 482 deletions(-) delete mode 100644 apm-agent-core/src/main/java/co/elastic/apm/agent/bci/OsgiBootDelegationEnabler.java delete mode 100644 apm-agent-core/src/test/java/co/elastic/apm/agent/bci/OsgiBootDelegationEnablerTest.java create mode 100644 apm-agent-plugins/apm-bootdelegation-plugin/pom.xml create mode 100644 apm-agent-plugins/apm-bootdelegation-plugin/src/main/java/co/elastic/apm/agent/bootdelegation/BootstrapDelegationClassLoaderInstrumentation.java rename apm-agent-plugins/{apm-mule4-plugin/src/main/java/co/elastic/apm/agent/mule4 => apm-bootdelegation-plugin/src/main/java/co/elastic/apm/agent/bootdelegation}/package-info.java (95%) create mode 100644 apm-agent-plugins/apm-bootdelegation-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.bci.ElasticApmInstrumentation delete mode 100644 apm-agent-plugins/apm-mule4-plugin/pom.xml delete mode 100644 apm-agent-plugins/apm-mule4-plugin/src/main/java/co/elastic/apm/agent/mule4/Mule4OverrideClassLoaderLookupInstrumentation.java delete mode 100644 apm-agent-plugins/apm-mule4-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.bci.ElasticApmInstrumentation delete mode 100644 apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/wildfly/WildFlyLifecycleListener.java delete mode 100644 apm-agent-plugins/apm-servlet-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.context.LifecycleListener diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index a27c1894db..d049fc302d 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -28,6 +28,10 @@ endif::[] [float] ===== Features +* Experimental support for runtime attachment now also for OSGi containers, JBoss, and WildFly +* New mitigation of OSGi bootdelegation errors (`NoClassDefFoundError`). + You can remove any `org.osgi.framework.bootdelegation` related configuration. + This release also removes the configuration option `boot_delegation_packages`. * Overhaul of the `ExecutorService` instrumentation that avoids issues like ``ClassCastException``s - {pull}1206[#1206] * Support for `ForJoinPool` and `ScheduledExecutorService` (see <>) * Support for `ExecutorService#invokeAny` and `ExecutorService#invokeAll` diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/AgentMain.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/AgentMain.java index 77db9314cf..4c2403d013 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/AgentMain.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/AgentMain.java @@ -34,8 +34,7 @@ * This class is loaded by the system classloader, * and adds the rest of the agent to the bootstrap class loader search. *

- * This is required to instrument Java core classes like {@link Runnable} and to enable boot delegation in OSGi environments. - * See {@link OsgiBootDelegationEnabler}. + * This is required to instrument Java core classes like {@link Runnable}. *

*

* Note that this relies on the fact that the system classloader is a parent-first classloader and first asks the bootstrap classloader diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/OsgiBootDelegationEnabler.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/OsgiBootDelegationEnabler.java deleted file mode 100644 index 08ac50d6bb..0000000000 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/OsgiBootDelegationEnabler.java +++ /dev/null @@ -1,78 +0,0 @@ -/*- - * #%L - * Elastic APM Java agent - * %% - * Copyright (C) 2018 - 2020 Elastic and contributors - * %% - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * #L% - */ -package co.elastic.apm.agent.bci; - -import co.elastic.apm.agent.configuration.CoreConfiguration; -import co.elastic.apm.agent.context.AbstractLifecycleListener; -import co.elastic.apm.agent.impl.ElasticApmTracer; - -import javax.annotation.Nullable; - -/** - * Required in OSGi environments like Equinox, which is used in WebSphere. - * By adding the base package of the APM agent, - * the instrumented classes have access to the agent classes, - * without specifying {@code Import-Package} bundle headers. - *

- * Note: in Apache Felix the boot delegation only works for classes loaded by the bootstrap classloader, - * which means that the whole agent code needs to be added to the bootstrap classloader. - * See {@link AgentMain#init(String, java.lang.instrument.Instrumentation, boolean)} - *

- */ -public class OsgiBootDelegationEnabler extends AbstractLifecycleListener { - private static final String APM_BASE_PACKAGE = "co.elastic.apm.agent.*"; - // see https://confluence.atlassian.com/jirakb/using-javaagent-with-jira-790793295.html#UsingjavaagentwithJIRA-Resolution - private static final String ATLASSIAN_BOOTDELEGATION_DEFAULTS = "META-INF.services,com.yourkit,com.singularity.*,com.jprofiler," + - "com.jprofiler.*,org.apache.xerces,org.apache.xerces.*,org.apache.xalan,org.apache.xalan.*,sun.*,com.sun.jndi.*,com.icl.saxon," + - "com.icl.saxon.*,javax.servlet,javax.servlet.*,com.sun.xml.bind.*,jdk.internal.*"; - - @Override - public void init(ElasticApmTracer tracer) { - // may be problematic as it could override the defaults in a properties file - CoreConfiguration coreConfig = tracer.getConfig(CoreConfiguration.class); - String packagesToAppendToBootdelegationProperty = coreConfig.getPackagesToAppendToBootdelegationProperty(); - if (packagesToAppendToBootdelegationProperty != null) { - appendToSystemProperty("org.osgi.framework.bootdelegation", packagesToAppendToBootdelegationProperty); - } - if (coreConfig.useAtlassianNewBootDelegationConfig()) { - appendToSystemProperty("atlassian.org.osgi.framework.bootdelegation.extra", APM_BASE_PACKAGE); - } else { - appendToSystemProperty("atlassian.org.osgi.framework.bootdelegation", ATLASSIAN_BOOTDELEGATION_DEFAULTS, APM_BASE_PACKAGE); - } - } - - private static void appendToSystemProperty(String propertyName, String append) { - appendToSystemProperty(propertyName, null, append); - } - - private static void appendToSystemProperty(String propertyName, @Nullable String propertyValueDefault, String append) { - final String systemPackages = System.getProperty(propertyName, propertyValueDefault); - if (systemPackages != null) { - System.setProperty(propertyName, systemPackages + "," + append); - } else { - System.setProperty(propertyName, append); - } - } -} diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/configuration/CoreConfiguration.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/configuration/CoreConfiguration.java index 8468323213..5942b2756e 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/configuration/CoreConfiguration.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/configuration/CoreConfiguration.java @@ -482,34 +482,6 @@ public class CoreConfiguration extends ConfigurationOptionProvider { "the higher of both thresholds will determine which spans will be discarded.") .buildWithDefault(TimeDuration.of("0ms")); - private final ConfigurationOption appendPackagesToBootDelegationProperty = ConfigurationOption.stringOption() - .key("boot_delegation_packages") - .tags("added[1.7.0]") - .configurationCategory(CORE_CATEGORY) - .description("A comma-separated list of packages to be appended to the boot delegation system property. \n" + - "If set with an empty string, nothing will be appended to the boot delegation system property.\n" + - "Values to set in known environments:\n\n" + - "Nexus:\n\n" + - "----\n" + - "boot_delegation_packages=com.sun.*, javax.transaction, javax.transaction.*, javax.xml.crypto, javax.xml.crypto.*, sun.*," + - "co.elastic.apm.agent.*\n" + - "----\n\n" + - "Pentaho and RedHat JBoss Fuse:\n\n" + - "----\n" + - "boot_delegation_packages=org.apache.karaf.jaas.boot, org.apache.karaf.jaas.boot.principal, org.apache.karaf.management.boot, " + - "sun.*, com.sun.*, javax.transaction, javax.transaction.*, javax.xml.crypto, javax.xml.crypto.*, org.apache.xerces.jaxp.datatype, " + - "org.apache.xerces.stax, org.apache.xerces.parsers, org.apache.xerces.jaxp, org.apache.xerces.jaxp.validation, " + - "org.apache.xerces.dom, co.elastic.apm.agent.*\n" + - "----\n") - .buildWithDefault("co.elastic.apm.agent.*"); - - private final ConfigurationOption atlassianNewBootDelegation = ConfigurationOption.booleanOption() - .key("use_atlassian_new_boot_delegation") - .configurationCategory(CORE_CATEGORY) - .tags("internal") - .description("In new Atlassian OSGi there is a config to append to boot delegation packages instead of overriding the default.") - .buildWithDefault(false); - private final ConfigurationOption centralConfig = ConfigurationOption.booleanOption() .key("central_config") .tags("added[1.8.0]") @@ -690,21 +662,6 @@ public TimeDuration getTraceMethodsDurationThreshold() { return traceMethodsDurationThreshold.get(); } - public @Nullable String getPackagesToAppendToBootdelegationProperty() { - String value = appendPackagesToBootDelegationProperty.get(); - if (value != null) { - value = value.trim(); - if (value.isEmpty()) { - value = null; - } - } - return value; - } - - public boolean useAtlassianNewBootDelegationConfig() { - return atlassianNewBootDelegation.get(); - } - public Map getGlobalLabels() { return globalLabels.get(); } diff --git a/apm-agent-core/src/main/resources/META-INF/services/co.elastic.apm.agent.context.LifecycleListener b/apm-agent-core/src/main/resources/META-INF/services/co.elastic.apm.agent.context.LifecycleListener index 68a6c7e8d2..fbe189ad8f 100644 --- a/apm-agent-core/src/main/resources/META-INF/services/co.elastic.apm.agent.context.LifecycleListener +++ b/apm-agent-core/src/main/resources/META-INF/services/co.elastic.apm.agent.context.LifecycleListener @@ -1,5 +1,4 @@ co.elastic.apm.agent.configuration.StartupInfo -co.elastic.apm.agent.bci.OsgiBootDelegationEnabler co.elastic.apm.agent.bci.MatcherTimerLifecycleListener co.elastic.apm.agent.metrics.builtin.JvmMemoryMetrics co.elastic.apm.agent.metrics.builtin.SystemMetrics diff --git a/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/OsgiBootDelegationEnablerTest.java b/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/OsgiBootDelegationEnablerTest.java deleted file mode 100644 index e7f335f074..0000000000 --- a/apm-agent-core/src/test/java/co/elastic/apm/agent/bci/OsgiBootDelegationEnablerTest.java +++ /dev/null @@ -1,109 +0,0 @@ -/*- - * #%L - * Elastic APM Java agent - * %% - * Copyright (C) 2018 - 2020 Elastic and contributors - * %% - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * #L% - */ -package co.elastic.apm.agent.bci; - -import co.elastic.apm.agent.configuration.CoreConfiguration; -import co.elastic.apm.agent.impl.ElasticApmTracer; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.*; - -class OsgiBootDelegationEnablerTest { - - private ElasticApmTracer tracer; - private CoreConfiguration coreConfiguration; - - private final OsgiBootDelegationEnabler osgiBootDelegationEnabler = new OsgiBootDelegationEnabler(); - - @BeforeEach - void setup() { - coreConfiguration = spy(CoreConfiguration.class); - tracer = mock(ElasticApmTracer.class); - when(tracer.getConfig(CoreConfiguration.class)).thenReturn(coreConfiguration); - System.clearProperty("org.osgi.framework.bootdelegation"); - System.clearProperty("atlassian.org.osgi.framework.bootdelegation"); - System.clearProperty("atlassian.org.osgi.framework.bootdelegation.extra"); - } - - @Test - void testBootdelegation() { - osgiBootDelegationEnabler.init(tracer); - assertThat(System.getProperties()) - .containsEntry("org.osgi.framework.bootdelegation", "co.elastic.apm.agent.*") - .containsKey("atlassian.org.osgi.framework.bootdelegation") - .doesNotContainKey("atlassian.org.osgi.framework.bootdelegation.extra"); - assertThat(System.getProperty("atlassian.org.osgi.framework.bootdelegation")).matches(".+,co.elastic.apm.agent.*"); - } - - @Test - void testBootdelegationWithNewAtlassianConfig() { - when(coreConfiguration.useAtlassianNewBootDelegationConfig()).thenReturn(true); - osgiBootDelegationEnabler.init(tracer); - assertThat(System.getProperties()) - .containsEntry("org.osgi.framework.bootdelegation", "co.elastic.apm.agent.*") - .containsEntry("atlassian.org.osgi.framework.bootdelegation.extra", "co.elastic.apm.agent.*") - .doesNotContainKey("atlassian.org.osgi.framework.bootdelegation"); - } - - @Test - void testBootdelegationWithExistingProperty() { - System.setProperty("org.osgi.framework.bootdelegation", "foo.bar"); - osgiBootDelegationEnabler.init(tracer); - assertThat(System.getProperties()) - .containsEntry("org.osgi.framework.bootdelegation", "foo.bar,co.elastic.apm.agent.*") - .containsKey("atlassian.org.osgi.framework.bootdelegation") - .doesNotContainKey("atlassian.org.osgi.framework.bootdelegation.extra"); - } - - @Test - void testNewAtlassianBootdelegationWithExistingProperty() { - when(coreConfiguration.useAtlassianNewBootDelegationConfig()).thenReturn(true); - System.setProperty("atlassian.org.osgi.framework.bootdelegation.extra", "foo.bar"); - osgiBootDelegationEnabler.init(tracer); - assertThat(System.getProperties()) - .containsEntry("atlassian.org.osgi.framework.bootdelegation.extra", "foo.bar,co.elastic.apm.agent.*") - .doesNotContainKey("atlassian.org.osgi.framework.bootdelegation"); - } - - @Test - void testAtlassianBootdelegationWithExistingProperty() { - System.setProperty("atlassian.org.osgi.framework.bootdelegation", "foo.bar"); - osgiBootDelegationEnabler.init(tracer); - assertThat(System.getProperties()) - .containsEntry("atlassian.org.osgi.framework.bootdelegation", "foo.bar,co.elastic.apm.agent.*") - .doesNotContainKey("atlassian.org.osgi.framework.bootdelegation.extra"); - } - - @Test - void testEmptyBootdelegationWithExistingProperty() { - when(coreConfiguration.getPackagesToAppendToBootdelegationProperty()).thenReturn(null); - System.setProperty("org.osgi.framework.bootdelegation", "foo.bar"); - osgiBootDelegationEnabler.init(tracer); - assertThat(System.getProperties()) - .containsEntry("org.osgi.framework.bootdelegation", "foo.bar"); - } -} diff --git a/apm-agent-plugins/apm-bootdelegation-plugin/pom.xml b/apm-agent-plugins/apm-bootdelegation-plugin/pom.xml new file mode 100644 index 0000000000..3655231c67 --- /dev/null +++ b/apm-agent-plugins/apm-bootdelegation-plugin/pom.xml @@ -0,0 +1,19 @@ + + + + apm-agent-plugins + co.elastic.apm + 1.17.1-SNAPSHOT + + 4.0.0 + + apm-bootdelegation-plugin + ${project.groupId}:${project.artifactId} + + + ${project.basedir}/../.. + + + diff --git a/apm-agent-plugins/apm-bootdelegation-plugin/src/main/java/co/elastic/apm/agent/bootdelegation/BootstrapDelegationClassLoaderInstrumentation.java b/apm-agent-plugins/apm-bootdelegation-plugin/src/main/java/co/elastic/apm/agent/bootdelegation/BootstrapDelegationClassLoaderInstrumentation.java new file mode 100644 index 0000000000..14a6442452 --- /dev/null +++ b/apm-agent-plugins/apm-bootdelegation-plugin/src/main/java/co/elastic/apm/agent/bootdelegation/BootstrapDelegationClassLoaderInstrumentation.java @@ -0,0 +1,112 @@ +/*- + * #%L + * Elastic APM Java agent + * %% + * Copyright (C) 2018 - 2020 Elastic and contributors + * %% + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * #L% + */ +package co.elastic.apm.agent.bootdelegation; + +import co.elastic.apm.agent.bci.ElasticApmInstrumentation; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.NamedElement; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import net.bytebuddy.matcher.ElementMatchers; + +import javax.annotation.Nullable; +import java.util.Collection; +import java.util.Collections; + +import static net.bytebuddy.matcher.ElementMatchers.hasSuperType; +import static net.bytebuddy.matcher.ElementMatchers.is; +import static net.bytebuddy.matcher.ElementMatchers.nameContains; +import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.not; +import static net.bytebuddy.matcher.ElementMatchers.returns; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +/** + * Enables universal delegation to the bootstrap class loader for agent classes. + *

+ * Some frameworks like OSGi, JBoss modules or Mule have class loader that restrict the regular delegation to the bootstrap class loader + * to only a particular set of allowed packages. + * This is a generic solution that works for all of them. + *

+ *

+ * After all plugins have been migrated to indy plugins, + * this instrumentation can be removed. + * But having it in now allows to make runtime attachment more readily available sooner. + * Also, if indy plugins should not work out for some reason, + * we have already tested out this approach and thus have something to fall back to. + *

+ *

+ * This approach is inspired by {@code io.opentelemetry.auto.instrumentation.javaclassloader.ClassLoaderInstrumentation}, + * under Apache License 2.0 + *

+ */ +public class BootstrapDelegationClassLoaderInstrumentation extends ElasticApmInstrumentation { + + @Override + public ElementMatcher getTypeMatcherPreFilter() { + return ElementMatchers.nameContains("Loader"); + } + + @Override + public ElementMatcher getTypeMatcher() { + return not(nameStartsWith("java.")) + .and(not(nameStartsWith("jdk."))) + .and(not(nameStartsWith("com.sun."))) + .and(not(nameStartsWith("sun."))) + .and(not(nameContains("Bootstrap"))) + .and(hasSuperType(is(ClassLoader.class))); + } + + @Override + public ElementMatcher getMethodMatcher() { + return named("loadClass") + .and(returns(Class.class)) + .and( + takesArguments(String.class) + .or(takesArguments(String.class, boolean.class))); + } + + @Override + public Collection getInstrumentationGroupNames() { + return Collections.singletonList("bootdelegation"); + } + + @Advice.OnMethodExit(onThrowable = ClassNotFoundException.class) + private static void onExit(@Advice.Thrown(readOnly = false) @Nullable ClassNotFoundException thrown, + @Advice.Argument(0) String className, + @Advice.Return(readOnly = false) Class returnValue) { + // only if the class loader wasn't able to load the agent classes themselves we apply our magic + if (thrown != null && className.startsWith("co.elastic.apm.agent")) { + try { + returnValue = Class.forName(className, false, null); + thrown = null; + } catch (ClassNotFoundException e) { + thrown.addSuppressed(e); + } + } + } +} diff --git a/apm-agent-plugins/apm-mule4-plugin/src/main/java/co/elastic/apm/agent/mule4/package-info.java b/apm-agent-plugins/apm-bootdelegation-plugin/src/main/java/co/elastic/apm/agent/bootdelegation/package-info.java similarity index 95% rename from apm-agent-plugins/apm-mule4-plugin/src/main/java/co/elastic/apm/agent/mule4/package-info.java rename to apm-agent-plugins/apm-bootdelegation-plugin/src/main/java/co/elastic/apm/agent/bootdelegation/package-info.java index d74912d4a6..8183ab901a 100644 --- a/apm-agent-plugins/apm-mule4-plugin/src/main/java/co/elastic/apm/agent/mule4/package-info.java +++ b/apm-agent-plugins/apm-bootdelegation-plugin/src/main/java/co/elastic/apm/agent/bootdelegation/package-info.java @@ -23,6 +23,6 @@ * #L% */ @NonnullApi -package co.elastic.apm.agent.mule4; +package co.elastic.apm.agent.bootdelegation; import co.elastic.apm.agent.annotation.NonnullApi; diff --git a/apm-agent-plugins/apm-bootdelegation-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.bci.ElasticApmInstrumentation b/apm-agent-plugins/apm-bootdelegation-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.bci.ElasticApmInstrumentation new file mode 100644 index 0000000000..b9390a102c --- /dev/null +++ b/apm-agent-plugins/apm-bootdelegation-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.bci.ElasticApmInstrumentation @@ -0,0 +1 @@ +co.elastic.apm.agent.bootdelegation.BootstrapDelegationClassLoaderInstrumentation diff --git a/apm-agent-plugins/apm-mule4-plugin/pom.xml b/apm-agent-plugins/apm-mule4-plugin/pom.xml deleted file mode 100644 index 28259585f2..0000000000 --- a/apm-agent-plugins/apm-mule4-plugin/pom.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - 4.0.0 - - - apm-agent-plugins - co.elastic.apm - 1.17.1-SNAPSHOT - - - apm-mule4-plugin - ${project.groupId}:${project.artifactId} - - - ${project.basedir}/../.. - - - - - org.mule.runtime - mule-module-artifact - 4.1.1 - provided - - - diff --git a/apm-agent-plugins/apm-mule4-plugin/src/main/java/co/elastic/apm/agent/mule4/Mule4OverrideClassLoaderLookupInstrumentation.java b/apm-agent-plugins/apm-mule4-plugin/src/main/java/co/elastic/apm/agent/mule4/Mule4OverrideClassLoaderLookupInstrumentation.java deleted file mode 100644 index b6cba0648c..0000000000 --- a/apm-agent-plugins/apm-mule4-plugin/src/main/java/co/elastic/apm/agent/mule4/Mule4OverrideClassLoaderLookupInstrumentation.java +++ /dev/null @@ -1,95 +0,0 @@ -/*- - * #%L - * Elastic APM Java agent - * %% - * Copyright (C) 2018 - 2020 Elastic and contributors - * %% - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * #L% - */ -package co.elastic.apm.agent.mule4; - -import co.elastic.apm.agent.bci.ElasticApmInstrumentation; -import net.bytebuddy.asm.Advice; -import net.bytebuddy.description.NamedElement; -import net.bytebuddy.description.method.MethodDescription; -import net.bytebuddy.description.type.TypeDescription; -import net.bytebuddy.matcher.ElementMatcher; -import org.mule.runtime.module.artifact.api.classloader.DelegateOnlyLookupStrategy; -import org.mule.runtime.module.artifact.api.classloader.LookupStrategy; - -import javax.annotation.Nullable; -import java.util.Collection; -import java.util.Collections; - -import static net.bytebuddy.matcher.ElementMatchers.hasSuperType; -import static net.bytebuddy.matcher.ElementMatchers.isInterface; -import static net.bytebuddy.matcher.ElementMatchers.nameContains; -import static net.bytebuddy.matcher.ElementMatchers.named; -import static net.bytebuddy.matcher.ElementMatchers.not; -import static net.bytebuddy.matcher.ElementMatchers.takesArgument; - -public class Mule4OverrideClassLoaderLookupInstrumentation extends ElasticApmInstrumentation { - - @Override - public ElementMatcher getTypeMatcherPreFilter() { - return nameContains("ClassLoaderLookupPolicy"); - } - - @Override - public ElementMatcher getTypeMatcher() { - return not(isInterface()).and(hasSuperType(named("org.mule.runtime.module.artifact.api.classloader.ClassLoaderLookupPolicy"))); - } - - @Override - public ElementMatcher getMethodMatcher() { - return named("getPackageLookupStrategy").and(takesArgument(0, String.class)); - } - - @Override - public Collection getInstrumentationGroupNames() { - return Collections.singletonList("mule"); - } - - @Override - public boolean includeWhenInstrumentationIsDisabled() { - return true; - } - - @Override - public Class getAdviceClass() { - return Mule4OverrideClassLoaderLookupAdvice.class; - } - - public static class Mule4OverrideClassLoaderLookupAdvice { - @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) - public static void makeParentOnlyForAgentClasses(@Advice.Argument(0) @Nullable final String packageName, - @Advice.Return(readOnly = false) LookupStrategy lookupStrategy) { - - // Until instrumentation mechanism is initiated, agent classes get loaded from the launching class loader. - // Whenever flows are invoked with the visibility of this class loader's classpath, we can't let other agent - // classes to be loaded from System or Bootstrap class loader. - // Mule4OverrideClassLoaderLookupInstrumentation is a good indication of that, as it is guaranteed to be - // loaded before this instrumentation took place - if (packageName != null && packageName.startsWith("co.elastic.apm.agent") && - Mule4OverrideClassLoaderLookupInstrumentation.class.getClassLoader() == null) { - lookupStrategy = new DelegateOnlyLookupStrategy(ClassLoader.getSystemClassLoader()); - } - } - } -} diff --git a/apm-agent-plugins/apm-mule4-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.bci.ElasticApmInstrumentation b/apm-agent-plugins/apm-mule4-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.bci.ElasticApmInstrumentation deleted file mode 100644 index d44a15acee..0000000000 --- a/apm-agent-plugins/apm-mule4-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.bci.ElasticApmInstrumentation +++ /dev/null @@ -1 +0,0 @@ -co.elastic.apm.agent.mule4.Mule4OverrideClassLoaderLookupInstrumentation diff --git a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/wildfly/WildFlyLifecycleListener.java b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/wildfly/WildFlyLifecycleListener.java deleted file mode 100644 index b162079bd9..0000000000 --- a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/wildfly/WildFlyLifecycleListener.java +++ /dev/null @@ -1,48 +0,0 @@ -/*- - * #%L - * Elastic APM Java agent - * %% - * Copyright (C) 2018 - 2020 Elastic and contributors - * %% - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * #L% - */ -package co.elastic.apm.agent.servlet.wildfly; - -import co.elastic.apm.agent.context.AbstractLifecycleListener; -import co.elastic.apm.agent.context.LifecycleListener; -import co.elastic.apm.agent.impl.ElasticApmTracer; - -/** - * Makes the {@code co.elastic.apm} package visible from all modules - */ -public class WildFlyLifecycleListener extends AbstractLifecycleListener { - - private static final String JBOSS_MODULES_SYSTEM_PKGS = "jboss.modules.system.pkgs"; - private static final String APM_BASE_PACKAGE = "co.elastic.apm.agent"; - - @Override - public void init(ElasticApmTracer tracer) { - final String systemPackages = System.getProperty(JBOSS_MODULES_SYSTEM_PKGS); - if (systemPackages != null) { - System.setProperty(JBOSS_MODULES_SYSTEM_PKGS, systemPackages + "," + APM_BASE_PACKAGE); - } else { - System.setProperty(JBOSS_MODULES_SYSTEM_PKGS, APM_BASE_PACKAGE); - } - } -} diff --git a/apm-agent-plugins/apm-servlet-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.context.LifecycleListener b/apm-agent-plugins/apm-servlet-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.context.LifecycleListener deleted file mode 100644 index 7280ab0fe5..0000000000 --- a/apm-agent-plugins/apm-servlet-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.context.LifecycleListener +++ /dev/null @@ -1 +0,0 @@ -co.elastic.apm.agent.servlet.wildfly.WildFlyLifecycleListener diff --git a/apm-agent-plugins/pom.xml b/apm-agent-plugins/pom.xml index 21b39d5a7e..84e1f07bd9 100644 --- a/apm-agent-plugins/pom.xml +++ b/apm-agent-plugins/pom.xml @@ -44,7 +44,6 @@ apm-scala-concurrent-plugin apm-error-logging-plugin apm-jmx-plugin - apm-mule4-plugin apm-mongoclient-plugin apm-process-plugin apm-kafka-plugin @@ -52,6 +51,7 @@ apm-grpc apm-grails-plugin apm-dubbo-plugin + apm-bootdelegation-plugin diff --git a/docs/configuration.asciidoc b/docs/configuration.asciidoc index 1442bf3627..b1d072097d 100644 --- a/docs/configuration.asciidoc +++ b/docs/configuration.asciidoc @@ -118,7 +118,6 @@ Click on a key to get more information. ** <> ** <> ** <> -** <> ** <> ** <> ** <> @@ -696,7 +695,7 @@ you should add an additional entry to this list (make sure to also include the d ==== `disable_instrumentations` (added[1.0.0,Changing this value at runtime is possible since version 1.15.0]) A list of instrumentations which should be disabled. -Valid options are `annotations`, `apache-commons-exec`, `apache-httpclient`, `asynchttpclient`, `concurrent`, `dubbo`, `elasticsearch-restclient`, `exception-handler`, `executor`, `executor-collection`, `experimental`, `grails`, `grpc`, `hibernate-search`, `http-client`, `jax-rs`, `jax-ws`, `jdbc`, `jedis`, `jms`, `jsf`, `kafka`, `lettuce`, `log4j`, `logging`, `mongodb-client`, `mule`, `okhttp`, `opentracing`, `process`, `public-api`, `quartz`, `redis`, `redisson`, `render`, `scheduled`, `servlet-api`, `servlet-api-async`, `servlet-input-stream`, `slf4j`, `spring-mvc`, `spring-resttemplate`, `spring-service-name`, `spring-view-render`, `ssl-context`, `timer-task`, `urlconnection`. +Valid options are `annotations`, `apache-commons-exec`, `apache-httpclient`, `asynchttpclient`, `concurrent`, `dubbo`, `elasticsearch-restclient`, `exception-handler`, `executor`, `executor-collection`, `experimental`, `grails`, `grpc`, `hibernate-search`, `http-client`, `jax-rs`, `jax-ws`, `jdbc`, `jedis`, `jms`, `jsf`, `kafka`, `lettuce`, `log4j`, `logging`, `mongodb-client`, `okhttp`, `opentracing`, `process`, `public-api`, `quartz`, `redis`, `redisson`, `render`, `scala-future`, `scheduled`, `servlet-api`, `servlet-api-async`, `servlet-input-stream`, `slf4j`, `spring-mvc`, `spring-resttemplate`, `spring-service-name`, `spring-view-render`, `ssl-context`, `timer-task`, `urlconnection`. If you want to try out experimental features, set the value to an empty string. NOTE: Changing this value at runtime can slow down the application temporarily. @@ -1029,44 +1028,6 @@ The default unit for this option is `ms`. | `elastic.apm.trace_methods_duration_threshold` | `trace_methods_duration_threshold` | `ELASTIC_APM_TRACE_METHODS_DURATION_THRESHOLD` |============ -// This file is auto generated. Please make your changes in *Configuration.java (for example CoreConfiguration.java) and execute ConfigurationExporter -[float] -[[config-boot-delegation-packages]] -==== `boot_delegation_packages` (added[1.7.0]) - -A comma-separated list of packages to be appended to the boot delegation system property. -If set with an empty string, nothing will be appended to the boot delegation system property. -Values to set in known environments: - -Nexus: - ----- -boot_delegation_packages=com.sun.*, javax.transaction, javax.transaction.*, javax.xml.crypto, javax.xml.crypto.*, sun.*,co.elastic.apm.agent.* ----- - -Pentaho and RedHat JBoss Fuse: - ----- -boot_delegation_packages=org.apache.karaf.jaas.boot, org.apache.karaf.jaas.boot.principal, org.apache.karaf.management.boot, sun.*, com.sun.*, javax.transaction, javax.transaction.*, javax.xml.crypto, javax.xml.crypto.*, org.apache.xerces.jaxp.datatype, org.apache.xerces.stax, org.apache.xerces.parsers, org.apache.xerces.jaxp, org.apache.xerces.jaxp.validation, org.apache.xerces.dom, co.elastic.apm.agent.* ----- - - - - - -[options="header"] -|============ -| Default | Type | Dynamic -| `co.elastic.apm.agent.*` | String | false -|============ - - -[options="header"] -|============ -| Java System Properties | Property file | Environment -| `elastic.apm.boot_delegation_packages` | `boot_delegation_packages` | `ELASTIC_APM_BOOT_DELEGATION_PACKAGES` -|============ - // This file is auto generated. Please make your changes in *Configuration.java (for example CoreConfiguration.java) and execute ConfigurationExporter [float] [[config-central-config]] @@ -2564,7 +2525,7 @@ The default unit for this option is `ms`. # sanitize_field_names=password,passwd,pwd,secret,*key,*token*,*session*,*credit*,*card*,authorization,set-cookie # A list of instrumentations which should be disabled. -# Valid options are `annotations`, `apache-commons-exec`, `apache-httpclient`, `asynchttpclient`, `concurrent`, `dubbo`, `elasticsearch-restclient`, `exception-handler`, `executor`, `executor-collection`, `experimental`, `grails`, `grpc`, `hibernate-search`, `http-client`, `jax-rs`, `jax-ws`, `jdbc`, `jedis`, `jms`, `jsf`, `kafka`, `lettuce`, `log4j`, `logging`, `mongodb-client`, `mule`, `okhttp`, `opentracing`, `process`, `public-api`, `quartz`, `redis`, `redisson`, `render`, `scheduled`, `servlet-api`, `servlet-api-async`, `servlet-input-stream`, `slf4j`, `spring-mvc`, `spring-resttemplate`, `spring-service-name`, `spring-view-render`, `ssl-context`, `timer-task`, `urlconnection`. +# Valid options are `annotations`, `apache-commons-exec`, `apache-httpclient`, `asynchttpclient`, `concurrent`, `dubbo`, `elasticsearch-restclient`, `exception-handler`, `executor`, `executor-collection`, `experimental`, `grails`, `grpc`, `hibernate-search`, `http-client`, `jax-rs`, `jax-ws`, `jdbc`, `jedis`, `jms`, `jsf`, `kafka`, `lettuce`, `log4j`, `logging`, `mongodb-client`, `okhttp`, `opentracing`, `process`, `public-api`, `quartz`, `redis`, `redisson`, `render`, `scala-future`, `scheduled`, `servlet-api`, `servlet-api-async`, `servlet-input-stream`, `slf4j`, `spring-mvc`, `spring-resttemplate`, `spring-service-name`, `spring-view-render`, `ssl-context`, `timer-task`, `urlconnection`. # If you want to try out experimental features, set the value to an empty string. # # NOTE: Changing this value at runtime can slow down the application temporarily. @@ -2766,29 +2727,6 @@ The default unit for this option is `ms`. # # trace_methods_duration_threshold=0ms -# A comma-separated list of packages to be appended to the boot delegation system property. -# If set with an empty string, nothing will be appended to the boot delegation system property. -# Values to set in known environments: -# -# Nexus: -# -# ---- -# boot_delegation_packages=com.sun.*, javax.transaction, javax.transaction.*, javax.xml.crypto, javax.xml.crypto.*, sun.*,co.elastic.apm.agent.* -# ---- -# -# Pentaho and RedHat JBoss Fuse: -# -# ---- -# boot_delegation_packages=org.apache.karaf.jaas.boot, org.apache.karaf.jaas.boot.principal, org.apache.karaf.management.boot, sun.*, com.sun.*, javax.transaction, javax.transaction.*, javax.xml.crypto, javax.xml.crypto.*, org.apache.xerces.jaxp.datatype, org.apache.xerces.stax, org.apache.xerces.parsers, org.apache.xerces.jaxp, org.apache.xerces.jaxp.validation, org.apache.xerces.dom, co.elastic.apm.agent.* -# ---- -# -# -# This setting can not be changed at runtime. Changes require a restart of the application. -# Type: String -# Default value: co.elastic.apm.agent.* -# -# boot_delegation_packages=co.elastic.apm.agent.* - # When enabled, the agent will make periodic requests to the APM Server to fetch updated configuration. # # This setting can be changed at runtime diff --git a/docs/setup-attach-api.asciidoc b/docs/setup-attach-api.asciidoc index ae46971a04..e0e5b1526f 100644 --- a/docs/setup-attach-api.asciidoc +++ b/docs/setup-attach-api.asciidoc @@ -17,8 +17,8 @@ which is part of the JDK distribution and does not typically come with the JRE d [[setup-attach-api-caveats]] ==== Caveats -Currently, this does not work in combination with OSGi containers (which includes most application servers). -The symptom is that there will be a lot of exceptions like this: `java.lang.NoClassDefFoundError: co/elastic/apm/agent/servlet/ServletApiAdvice`. +The approach to mitigate ``NoClassDefFoundError``s when used in OSGi containers (which includes most application servers) is experimental. +Versions prior to 1.18.0 don't support OSGi containers. There can only be one agent instance with one configuration per JVM. So if you deploy multiple web applications to the same application server and call `ElasticApmAttacher.attach()` in each application, diff --git a/docs/setup-attach-cli.asciidoc b/docs/setup-attach-cli.asciidoc index fe6771b42b..9ada2a4f04 100644 --- a/docs/setup-attach-cli.asciidoc +++ b/docs/setup-attach-cli.asciidoc @@ -27,8 +27,8 @@ The OS user executing `apm-agent-attach-standalone.jar` and the OS user of the a When attaching to a J9 VM, you have to explicitly set the `--pid ` argument, or have `jps` installed. -Currently, this does not work in combination with OSGi containers (which includes most application servers). -The symptom is that there will be a lot of exceptions like this: `java.lang.NoClassDefFoundError: co/elastic/apm/agent/servlet/ServletApiAdvice`. +The approach to mitigate ``NoClassDefFoundError``s when used in OSGi containers (which includes most application servers) is experimental. +Versions prior to 1.18.0 don't support OSGi containers. [float] [[setup-attach-cli-download]] diff --git a/docs/supported-technologies.asciidoc b/docs/supported-technologies.asciidoc index 2eae341252..0625b1910f 100644 --- a/docs/supported-technologies.asciidoc +++ b/docs/supported-technologies.asciidoc @@ -474,9 +474,6 @@ If you are seeing gaps in the span timeline and want to include additional metho [float] [[supported-technologies-caveats]] === Caveats -* Certain OSGi containers need the following configuration setting in case you see exceptions like - `java.lang.NoClassDefFoundError: co/elastic/apm/jdbc/StatementInstrumentation`: - `org.osgi.framework.bootdelegation=co.elastic.apm.agent.*` * Other JVM languages, like Scala, Kotlin and Groovy have not been tested yet. * The agent does currently not support running on JVMs with an enabled `SecurityManager`. You may see exceptions like this: `java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "getProtectionDomain")`. diff --git a/elastic-apm-agent/pom.xml b/elastic-apm-agent/pom.xml index 168cf5692a..04b5832881 100644 --- a/elastic-apm-agent/pom.xml +++ b/elastic-apm-agent/pom.xml @@ -44,6 +44,11 @@ apm-asynchttpclient-plugin ${project.version} + + ${project.groupId} + apm-bootdelegation-plugin + ${project.version} + ${project.groupId} apm-error-logging-plugin @@ -149,11 +154,6 @@ apm-mongoclient-plugin ${project.version} - - ${project.groupId} - apm-mule4-plugin - ${project.version} - ${project.groupId} apm-okhttp-plugin