diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index a5b3512628..e4ac908a78 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -29,7 +29,8 @@ dependencies { implementation(gradleApi()) implementation(localGroovy()) - implementation("io.opentelemetry.instrumentation.muzzle-generation:io.opentelemetry.instrumentation.muzzle-generation.gradle.plugin:0.1.0-SNAPSHOT") + implementation("io.opentelemetry.instrumentation.muzzle-generation:io.opentelemetry.instrumentation.muzzle-generation.gradle.plugin:0.2.0-SNAPSHOT") + implementation("io.opentelemetry.instrumentation.muzzle-check:io.opentelemetry.instrumentation.muzzle-check.gradle.plugin:0.2.0-SNAPSHOT") implementation("org.eclipse.aether:aether-connector-basic:1.1.0") implementation("org.eclipse.aether:aether-transport-http:1.1.0") diff --git a/buildSrc/src/main/kotlin/io.opentelemetry.instrumentation.muzzle-check.gradle.kts b/buildSrc/src/main/kotlin/io.opentelemetry.instrumentation.muzzle-check.gradle.kts deleted file mode 100644 index ea91c96da3..0000000000 --- a/buildSrc/src/main/kotlin/io.opentelemetry.instrumentation.muzzle-check.gradle.kts +++ /dev/null @@ -1,359 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.instrumentation.gradle.muzzle.AcceptableVersions -import io.opentelemetry.instrumentation.gradle.muzzle.BogusClassLoader -import io.opentelemetry.instrumentation.gradle.muzzle.MuzzleDirective -import io.opentelemetry.instrumentation.gradle.muzzle.MuzzleExtension -import org.apache.maven.repository.internal.MavenRepositorySystemUtils -import org.eclipse.aether.RepositorySystem -import org.eclipse.aether.RepositorySystemSession -import org.eclipse.aether.artifact.Artifact -import org.eclipse.aether.artifact.DefaultArtifact -import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory -import org.eclipse.aether.repository.LocalRepository -import org.eclipse.aether.repository.RemoteRepository -import org.eclipse.aether.resolution.VersionRangeRequest -import org.eclipse.aether.resolution.VersionRangeResult -import org.eclipse.aether.spi.connector.RepositoryConnectorFactory -import org.eclipse.aether.spi.connector.transport.TransporterFactory -import org.eclipse.aether.transport.http.HttpTransporterFactory -import org.eclipse.aether.version.Version -import java.net.URL -import java.net.URLClassLoader -import java.util.stream.StreamSupport - -plugins { - `java-library` -} - -// Select a random set of versions to test -val RANGE_COUNT_LIMIT = 10 - -val muzzleConfig = extensions.create("muzzle") - -val muzzleTooling by configurations.creating { - isCanBeConsumed = false - isCanBeResolved = true -} -val muzzleBootstrap by configurations.creating { - isCanBeConsumed = false - isCanBeResolved = true -} - -val compileMuzzle by tasks.registering { - dependsOn(muzzleBootstrap) - dependsOn(muzzleTooling) - dependsOn(tasks.named("classes")) -} - -val muzzle by tasks.registering { - group = "Muzzle" - description = "Run instrumentation muzzle on compile time dependencies" - dependsOn(compileMuzzle) -} - -tasks.register("printMuzzleReferences") { - group = "Muzzle" - description = "Print references created by instrumentation muzzle" - dependsOn(compileMuzzle) - doLast { - val instrumentationCL = createInstrumentationClassloader() - val assertionMethod = instrumentationCL - .loadClass( - "io.opentelemetry.javaagent.tooling.muzzle.matcher.MuzzleGradlePluginUtil") - .getMethod("printMuzzleReferences", ClassLoader::class.java) - assertionMethod.invoke(null, instrumentationCL) - } -} - -val projectRepositories = mutableListOf().apply { - // Manually add mavenCentral until https://github.com/gradle/gradle/issues/17295 - // Adding mavenLocal is much more complicated but hopefully isn't required for normal usage of - // Muzzle. - add( - RemoteRepository.Builder( - "MavenCentral", "default", "https://repo.maven.apache.org/maven2/") - .build()) - for (repository in repositories) { - if (repository is MavenArtifactRepository) { - add( - RemoteRepository.Builder( - repository.getName(), - "default", - repository.url.toString()) - .build()) - } - } -}.toList() - -val hasRelevantTask = gradle.startParameter.taskNames.any { - // removing leading ':' if present - val taskName = it.removePrefix(":") - val projectPath = project.path.substring(1) - // Either the specific muzzle task in this project or the top level, full-project - // muzzle task. - // Either the specific muzzle task in this project or the top level, full-project - // muzzle task. - taskName == "${projectPath}:muzzle" || taskName == "muzzle" -} - -if (hasRelevantTask) { - val system = newRepositorySystem() - val session = newRepositorySystemSession(system) - - afterEvaluate { - var runAfter = muzzle - - for (muzzleDirective in muzzleConfig.directives.get()) { - logger.info("configured ${muzzleDirective}") - - if (muzzleDirective.coreJdk.get()) { - runAfter = addMuzzleTask(muzzleDirective, null, runAfter) - } else { - for (singleVersion in muzzleDirectiveToArtifacts(muzzleDirective, system, session)) { - runAfter = addMuzzleTask(muzzleDirective, singleVersion, runAfter) - } - if (muzzleDirective.assertInverse.get()) { - for (inverseDirective in inverseOf(muzzleDirective, system, session)) { - for (singleVersion in muzzleDirectiveToArtifacts(inverseDirective, system, session)) { - runAfter = addMuzzleTask(inverseDirective, singleVersion, runAfter) - } - } - } - } - } - } -} - -fun createInstrumentationClassloader(): ClassLoader { - logger.info("Creating instrumentation classpath for: ${name}") - val runtimeClasspath = sourceSets.main.get().runtimeClasspath - return classpathLoader(runtimeClasspath, createMuzzleCheckLoader()) -} - -fun classpathLoader(classpath: FileCollection, parent: ClassLoader): ClassLoader { - val urls: Array = StreamSupport.stream(classpath.spliterator(), false) - .map { - logger.info("--${it}") - it.toURI().toURL() - } - .toArray(::arrayOfNulls) - return URLClassLoader(urls, parent) -} - -fun createMuzzleCheckLoader(): ClassLoader { - logger.info("creating classpath for auto-tooling") - return classpathLoader(muzzleTooling, ClassLoader.getPlatformClassLoader()) -} - -fun newRepositorySystem(): RepositorySystem { - return MavenRepositorySystemUtils.newServiceLocator().apply { - addService(RepositoryConnectorFactory::class.java, BasicRepositoryConnectorFactory::class.java) - addService(TransporterFactory::class.java, HttpTransporterFactory::class.java) - }.run { - getService(RepositorySystem::class.java) - } -} - -fun newRepositorySystemSession(system: RepositorySystem): RepositorySystemSession { - val muzzleRepo = file("${buildDir}/muzzleRepo") - val localRepo = LocalRepository(muzzleRepo) - return MavenRepositorySystemUtils.newSession().apply { - setLocalRepositoryManager(system.newLocalRepositoryManager(this, localRepo)) - } -} - -fun addMuzzleTask(muzzleDirective: MuzzleDirective, versionArtifact: Artifact?, runAfter: TaskProvider) - : TaskProvider { - val taskName = if (versionArtifact == null) { - "muzzle-Assert${muzzleDirective}" - } else { - StringBuilder("muzzle-Assert").apply { - if (muzzleDirective.assertPass.get()) { - append("Pass") - } else { - append("Fail") - } - append('-') - .append(versionArtifact.groupId) - .append('-') - .append(versionArtifact.artifactId) - .append('-') - .append(versionArtifact.version) - if (!muzzleDirective.name.get().isEmpty()) { - append(muzzleDirective.nameSlug) - } - }.run { toString() } - } - val config = configurations.create(taskName) - if (versionArtifact != null) { - val dep = (dependencies.create(versionArtifact.run { "${groupId}:${artifactId}:${version}" }) as ModuleDependency).apply { - isTransitive = true - exclude("com.sun.jdmk", "jmxtools") - exclude("com.sun.jmx", "jmxri") - for (excluded in muzzleDirective.excludedDependencies.get()) { - val (group, module) = excluded.split(':') - exclude(group, module) - } - } - config.dependencies.add(dep) - - for (additionalDependency in muzzleDirective.additionalDependencies.get()) { - val additional = if (additionalDependency.count { it == ':' } < 2) { - // Dependency definition without version, use the artifact's version. - "${additionalDependency}:${versionArtifact.version}" - } else { - additionalDependency - } - val additionalDep = (dependencies.create(additional) as ModuleDependency).apply { - isTransitive = true - } - config.dependencies.add(additionalDep) - } - } - - val muzzleTask = tasks.register(taskName) { - dependsOn(configurations.named("runtimeClasspath")) - doLast { - val instrumentationCL = createInstrumentationClassloader() - val ccl = Thread.currentThread().contextClassLoader - val bogusLoader = BogusClassLoader() - val userCL = createClassLoaderForTask(config) - Thread.currentThread().contextClassLoader = bogusLoader - try { - // find all instrumenters, get muzzle, and assert - val assertionMethod = instrumentationCL - .loadClass("io.opentelemetry.javaagent.tooling.muzzle.matcher.MuzzleGradlePluginUtil") - .getMethod( - "assertInstrumentationMuzzled", - ClassLoader::class.java, - ClassLoader::class.java, - Boolean::class.javaPrimitiveType) - assertionMethod.invoke( - null, - instrumentationCL, - userCL, - muzzleDirective.assertPass.get()) - } finally { - Thread.currentThread().contextClassLoader = ccl - } - - for (thread in Thread.getAllStackTraces().keys) { - if (thread.contextClassLoader === bogusLoader - || thread.contextClassLoader === instrumentationCL - || thread.contextClassLoader === userCL) { - throw GradleException( - "Task ${taskName} has spawned a thread: ${thread} with classloader ${thread.contextClassLoader}. " + - "This will prevent GC of dynamic muzzle classes. Aborting muzzle run.") - } - } - } - } - - runAfter.configure { finalizedBy(muzzleTask) } - return muzzleTask -} - -fun createClassLoaderForTask(muzzleTaskConfiguration: Configuration): ClassLoader { - val userUrls = objects.fileCollection() - logger.info("Creating task classpath") - userUrls.from(muzzleTaskConfiguration.resolvedConfiguration.files) - return classpathLoader(userUrls.plus(muzzleBootstrap), ClassLoader.getPlatformClassLoader()) -} - -fun inverseOf(muzzleDirective: MuzzleDirective, system: RepositorySystem, session: RepositorySystemSession): Set { - val inverseDirectives = mutableSetOf() - - val allVersionsArtifact = DefaultArtifact( - muzzleDirective.group.get(), - muzzleDirective.module.get(), - muzzleDirective.classifier.get(), - "jar", - "[,)") - val directiveArtifact = DefaultArtifact( - muzzleDirective.group.get(), - muzzleDirective.module.get(), - muzzleDirective.classifier.get(), - "jar", - muzzleDirective.versions.get()) - - val repos = projectRepositories - val allRangeRequest = VersionRangeRequest().apply { - repositories = repos - artifact = allVersionsArtifact - } - val allRangeResult = system.resolveVersionRange(session, allRangeRequest) - - val rangeRequest = VersionRangeRequest().apply { - repositories = repos - artifact = directiveArtifact - } - val rangeResult = system.resolveVersionRange(session, rangeRequest) - - allRangeResult.versions.removeAll(rangeResult.versions) - - for (version in filterVersions(allRangeResult, muzzleDirective.normalizedSkipVersions)) { - val inverseDirective = objects.newInstance(MuzzleDirective::class).apply { - group.set(muzzleDirective.group) - module.set(muzzleDirective.module) - classifier.set(muzzleDirective.classifier) - versions.set(version) - assertPass.set(!muzzleDirective.assertPass.get()) - excludedDependencies.set(muzzleDirective.excludedDependencies) - } - inverseDirectives.add(inverseDirective) - } - - return inverseDirectives -} - -fun filterVersions(range: VersionRangeResult, skipVersions: Set) = sequence { - val predicate = AcceptableVersions(skipVersions) - if (predicate.test(range.lowestVersion)) { - yield(range.lowestVersion.toString()) - } - if (predicate.test(range.highestVersion)) { - yield(range.highestVersion.toString()) - } - - val copy: List = range.versions.shuffled() - for (version in copy) { - if (predicate.test(version)) { - yield(version.toString()) - } - } -}.distinct().take(RANGE_COUNT_LIMIT) - -fun muzzleDirectiveToArtifacts(muzzleDirective: MuzzleDirective, system: RepositorySystem, session: RepositorySystemSession) = sequence { - val directiveArtifact: Artifact = DefaultArtifact( - muzzleDirective.group.get(), - muzzleDirective.module.get(), - muzzleDirective.classifier.get(), - "jar", - muzzleDirective.versions.get()) - - val rangeRequest = VersionRangeRequest().apply { - repositories = projectRepositories - artifact = directiveArtifact - } - val rangeResult = system.resolveVersionRange(session, rangeRequest) - - val allVersionArtifacts = filterVersions(rangeResult, muzzleDirective.normalizedSkipVersions) - .map { - DefaultArtifact( - muzzleDirective.group.get(), - muzzleDirective.module.get(), - muzzleDirective.classifier.get(), - "jar", - it) - } - - allVersionArtifacts.ifEmpty { - throw GradleException("No muzzle artifacts found for $muzzleDirective") - } - - yieldAll(allVersionArtifacts) -} diff --git a/buildSrc/src/main/kotlin/io/opentelemetry/instrumentation/gradle/muzzle/AcceptableVersions.kt b/buildSrc/src/main/kotlin/io/opentelemetry/instrumentation/gradle/muzzle/AcceptableVersions.kt deleted file mode 100644 index cf62994afc..0000000000 --- a/buildSrc/src/main/kotlin/io/opentelemetry/instrumentation/gradle/muzzle/AcceptableVersions.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.gradle.muzzle - -import org.eclipse.aether.version.Version -import java.util.Locale -import java.util.function.Predicate - -internal class AcceptableVersions(private val skipVersions: Collection) : - Predicate { - - override fun test(version: Version?): Boolean { - if (version == null) { - return false - } - val versionString = version.toString().toLowerCase(Locale.ROOT) - if (skipVersions.contains(versionString)) { - return false - } - val draftVersion = versionString.contains("rc") - || versionString.contains(".cr") - || versionString.contains("alpha") - || versionString.contains("beta") - || versionString.contains("-b") - || versionString.contains(".m") - || versionString.contains("-m") - || versionString.contains("-dev") - || versionString.contains("-ea") - || versionString.contains("-atlassian-") - || versionString.contains("public_draft") - || versionString.contains("snapshot") - || GIT_SHA_PATTERN.matches(versionString) - return !draftVersion - } - - companion object { - private val GIT_SHA_PATTERN = Regex("^.*-[0-9a-f]{7,}$") - } -} diff --git a/buildSrc/src/main/kotlin/io/opentelemetry/instrumentation/gradle/muzzle/BogusClassLoader.kt b/buildSrc/src/main/kotlin/io/opentelemetry/instrumentation/gradle/muzzle/BogusClassLoader.kt deleted file mode 100644 index f298adc089..0000000000 --- a/buildSrc/src/main/kotlin/io/opentelemetry/instrumentation/gradle/muzzle/BogusClassLoader.kt +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.gradle.muzzle - -import java.security.SecureClassLoader - -internal class BogusClassLoader : SecureClassLoader() { - override fun toString(): String { - return "bogus" - } -} diff --git a/buildSrc/src/main/kotlin/io/opentelemetry/instrumentation/gradle/muzzle/MuzzleDirective.kt b/buildSrc/src/main/kotlin/io/opentelemetry/instrumentation/gradle/muzzle/MuzzleDirective.kt deleted file mode 100644 index 539634cdae..0000000000 --- a/buildSrc/src/main/kotlin/io/opentelemetry/instrumentation/gradle/muzzle/MuzzleDirective.kt +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.gradle.muzzle - -import org.gradle.api.provider.ListProperty -import org.gradle.api.provider.Property -import org.gradle.api.provider.SetProperty -import java.util.stream.Collectors - -@Deprecated("Should be removed when we start using external muzzle check plugin") -abstract class MuzzleDirective { - - abstract val name: Property - abstract val group: Property - abstract val module: Property - abstract val classifier: Property - abstract val versions: Property - abstract val skipVersions: SetProperty - abstract val additionalDependencies: ListProperty - abstract val excludedDependencies: ListProperty - abstract val assertPass: Property - abstract val assertInverse: Property - internal abstract val coreJdk: Property // use coreJdk() function below to enable - - init { - name.convention("") - classifier.convention("") - skipVersions.convention(emptySet()) - additionalDependencies.convention(listOf()) - excludedDependencies.convention(listOf()) - assertPass.convention(false) - assertInverse.convention(false) - coreJdk.convention(false) - } - - fun coreJdk() { - coreJdk.set(true) - } - - /** - * Adds extra dependencies to the current muzzle test. - * - * @param compileString An extra dependency in the gradle canonical form: - * '::'. - */ - fun extraDependency(compileString: String?) { - additionalDependencies.add(compileString!!) - } - - /** - * Adds transitive dependencies to exclude from the current muzzle test. - * - * @param excludeString A dependency in the gradle canonical form: ':' - */ - fun excludeDependency(excludeString: String?) { - excludedDependencies.add(excludeString!!) - } - - fun skip(vararg version: String?) { - skipVersions.addAll(*version) - } - - internal val nameSlug: String - get() = NORMALIZE_NAME_SLUG.replace(name.get().trim(), "-") - - internal val normalizedSkipVersions: Set - get() = skipVersions.getOrElse(setOf()).stream() - .map(String::toLowerCase) - .collect(Collectors.toSet()) - - override fun toString(): String { - val sb = StringBuilder() - if (coreJdk.getOrElse(false)) { - if (assertPass.getOrElse(false)) { - sb.append("Pass") - } else { - sb.append("Fail") - } - sb.append("-core-jdk") - } else { - if (assertPass.getOrElse(false)) { - sb.append("pass") - } else { - sb.append("fail") - } - sb.append(group.get()) - .append(':') - .append(module.get()) - .append(':') - .append(versions.get()) - if (classifier.isPresent) { - sb.append(':').append(classifier.get()) - } - } - return sb.toString() - } - - companion object { - private val NORMALIZE_NAME_SLUG = Regex("[^a-zA-Z0-9]+") - } -} diff --git a/buildSrc/src/main/kotlin/io/opentelemetry/instrumentation/gradle/muzzle/MuzzleExtension.kt b/buildSrc/src/main/kotlin/io/opentelemetry/instrumentation/gradle/muzzle/MuzzleExtension.kt deleted file mode 100644 index badbf0c380..0000000000 --- a/buildSrc/src/main/kotlin/io/opentelemetry/instrumentation/gradle/muzzle/MuzzleExtension.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.gradle.muzzle - -import org.gradle.api.Action -import org.gradle.api.model.ObjectFactory -import org.gradle.api.provider.ListProperty -import javax.inject.Inject - -@Deprecated("Should be removed when we start using external muzzle check plugin") -abstract class MuzzleExtension @Inject constructor(private val objectFactory: ObjectFactory) { - - internal abstract val directives: ListProperty - - fun pass(action: Action) { - val pass = objectFactory.newInstance(MuzzleDirective::class.java) - action.execute(pass) - pass.assertPass.set(true) - directives.add(pass) - } - - fun fail(action: Action) { - val fail = objectFactory.newInstance(MuzzleDirective::class.java) - action.execute(fail) - fail.assertPass.set(false) - directives.add(fail) - } -} diff --git a/buildSrc/src/test/java/io/opentelemetry/instrumentation/gradle/muzzle/MuzzlePluginTest.java b/buildSrc/src/test/java/io/opentelemetry/instrumentation/gradle/muzzle/MuzzlePluginTest.java deleted file mode 100644 index 2f2a6e4c94..0000000000 --- a/buildSrc/src/test/java/io/opentelemetry/instrumentation/gradle/muzzle/MuzzlePluginTest.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.gradle.muzzle; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.Collections; -import org.eclipse.aether.version.Version; -import org.junit.jupiter.api.Test; - -class MuzzlePluginTest { - - @Test - void rangeRequest() { - AcceptableVersions predicate = new AcceptableVersions(Collections.emptyList()); - - assertThat(predicate.test(new TestVersion("10.1.0-rc2+19-8e20bb26"))).isFalse(); - assertThat(predicate.test(new TestVersion("2.4.5.BUILD-SNAPSHOT"))).isFalse(); - } - - static class TestVersion implements Version { - - private final String version; - - TestVersion(String version) { - this.version = version; - } - - @Override - public int compareTo(Version o) { - return toString().compareTo(o.toString()); - } - - @Override - public String toString() { - return version; - } - } -} diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/matcher/MuzzleGradlePluginUtil.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/matcher/MuzzleGradlePluginUtil.java deleted file mode 100644 index dcd71449db..0000000000 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/matcher/MuzzleGradlePluginUtil.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.tooling.muzzle.matcher; - -import static java.lang.System.lineSeparator; - -import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; -import io.opentelemetry.javaagent.extension.muzzle.ClassRef; -import io.opentelemetry.javaagent.extension.muzzle.FieldRef; -import io.opentelemetry.javaagent.extension.muzzle.MethodRef; -import io.opentelemetry.javaagent.extension.muzzle.Source; -import io.opentelemetry.javaagent.tooling.muzzle.ClassLoaderMatcher; -import io.opentelemetry.javaagent.tooling.muzzle.Mismatch; -import io.opentelemetry.javaagent.tooling.muzzle.ReferenceMatcher; -import java.util.List; -import java.util.Map; -import java.util.ServiceLoader; - -/** Entry point for the muzzle gradle plugin. */ -// Runs in special classloader so tedious to provide access to the Gradle logger. -@SuppressWarnings("SystemOut") -@Deprecated -public final class MuzzleGradlePluginUtil { - private static final String INDENT = " "; - - /** - * Verifies that all instrumentations present in the {@code agentClassLoader} can be safely - * applied to the passed {@code userClassLoader}. - * - *

This method throws whenever one of the following step fails (and {@code assertPass} is - * true): - * - *

    - *
  1. {@code userClassLoader} is not matched by the {@link - * InstrumentationModule#classLoaderMatcher()} method - *
  2. {@link ReferenceMatcher} of any instrumentation module finds any mismatch - *
  3. any helper class defined in {@link InstrumentationModule#getMuzzleHelperClassNames()} - * fails to be injected into {@code userClassLoader} - *
- * - *

When {@code assertPass = false} this method behaves in an opposite way: failure in any of - * the first two steps is expected (helper classes are not injected at all). - * - *

This method is repeatedly called by the {@code :muzzle} gradle task - each tested dependency - * version passes different {@code userClassLoader}. - */ - public static void assertInstrumentationMuzzled( - ClassLoader agentClassLoader, ClassLoader userClassLoader, boolean assertPass) { - - ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); - Thread.currentThread().setContextClassLoader(agentClassLoader); - - Map> allMismatches; - try { - allMismatches = ClassLoaderMatcher.matchesAll(userClassLoader, assertPass); - - allMismatches.forEach( - (moduleName, mismatches) -> { - boolean passed = mismatches.isEmpty(); - - if (passed && !assertPass) { - System.err.println("MUZZLE PASSED " + moduleName + " BUT FAILURE WAS EXPECTED"); - throw new IllegalStateException( - "Instrumentation unexpectedly passed Muzzle validation"); - } else if (!passed && assertPass) { - System.err.println("FAILED MUZZLE VALIDATION: " + moduleName + " mismatches:"); - - for (Mismatch mismatch : mismatches) { - System.err.println("-- " + mismatch); - } - throw new IllegalStateException("Instrumentation failed Muzzle validation"); - } - }); - } finally { - Thread.currentThread().setContextClassLoader(contextClassLoader); - } - - int validatedModulesCount = allMismatches.size(); - if (validatedModulesCount == 0) { - String errorMessage = "Did not found any InstrumentationModule to validate!"; - System.err.println(errorMessage); - throw new IllegalStateException(errorMessage); - } - } - - /** - * Prints all references from all instrumentation modules present in the passed {@code - * instrumentationClassLoader}. - * - *

Called by the {@code printMuzzleReferences} gradle task. - */ - public static void printMuzzleReferences(ClassLoader instrumentationClassLoader) { - for (InstrumentationModule instrumentationModule : - ServiceLoader.load(InstrumentationModule.class, instrumentationClassLoader)) { - try { - System.out.println(instrumentationModule.getClass().getName()); - for (ClassRef ref : instrumentationModule.getMuzzleReferences().values()) { - System.out.print(prettyPrint(ref)); - } - } catch (RuntimeException e) { - String message = - "Unexpected exception printing references for " - + instrumentationModule.getClass().getName(); - System.out.println(message); - throw new IllegalStateException(message, e); - } - } - } - - private static String prettyPrint(ClassRef ref) { - StringBuilder builder = new StringBuilder(INDENT).append(ref).append(lineSeparator()); - if (!ref.getSources().isEmpty()) { - builder.append(INDENT).append(INDENT).append("Sources:").append(lineSeparator()); - for (Source source : ref.getSources()) { - builder - .append(INDENT) - .append(INDENT) - .append(INDENT) - .append("at: ") - .append(source) - .append(lineSeparator()); - } - } - for (FieldRef field : ref.getFields()) { - builder.append(INDENT).append(INDENT).append(field).append(lineSeparator()); - } - for (MethodRef method : ref.getMethods()) { - builder.append(INDENT).append(INDENT).append(method).append(lineSeparator()); - } - return builder.toString(); - } - - private MuzzleGradlePluginUtil() {} -}