Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to check which gradle dependencies contains given class?

Recently we had a version mismatch problem with a class org.apache.commons.beanutils.PropertyUtilsBean. We thought that mismatch is just between some dependency that brings commons-beanutils in versions 1.8 and 1.9.3 but after tracking and excluding each transitive dependency we were still facing an issue.

It turns out that the the PropertyUtilsBean was also packed inside the commons-digester3-3.2-with-deps instead declared as dependency to commons-beanutils.

Is it possible in gradle to search all dependencies (including transitive ones) for specific fully qualified classname? That way we could resolve such problems on the spot.

like image 985
literg Avatar asked Apr 06 '18 11:04

literg


2 Answers

I tried it and it is possible using some custom gradle build logic:

Kotlin DSL

tasks {
  val searchClass by creating {
    doLast {
      configurations.forEach {    // check all configurations
        if (it.isCanBeResolved) { 
          try {
            val classLoader = configToClassloader(it)
            // replace here class you are looking for
            val cl = Class.forName("arrow.core.Either", false, classLoader)
            println("found in Configuration $it")
            println(cl.protectionDomain.codeSource.location)
          } catch (e: Exception) {}
        }
      }
    }
  }
}

// Helper function: convert a gradle configuration to ClassLoader
fun configToClassloader(config: Configuration) = 
  URLClassLoader(
    config.files.map {
      it.toURI().toURL()
    }.toTypedArray())

This could be further enhanced by replacing the hard coded classname with some parameter mechanism.

Sample output:

> Task :searchClass
Configuration configuration ':domain:apiDependenciesMetadata'
file:/Users/abendt/.gradle/caches/modules-2/files-2.1/io.arrow-kt/arrow-core-data/0.9.0/a5b0228eebd5ee2f233f9aa9b9b624a32f84f328/arrow-core-data-0.9.0.jar   

Groovy DSL

def configToClassloader(config) {
  return new URLClassLoader(
          *config.files.collect {
              it.toURI().toURL()
          }.toArray())
}

task searchClass {
  doLast {
    configurations.forEach {    // check all configurations
        if (it.canBeResolved) {
            try {
                def classLoader = configToClassloader(it)
                // replace here class you are looking for
                def cl = Class.forName("arrow.core.Either", false, classLoader)
                println("found in Configuration $it")
                println(cl.protectionDomain.codeSource.location)
            } catch (e) {}
        }
    }
  }
}

Edit: I have recently created a Gradle Plugin that provides the described tasks: https://plugins.gradle.org/plugin/io.github.redgreencoding.findclass

like image 109
abendt Avatar answered Oct 05 '22 08:10

abendt


You could do this

task findJarsForClass {
   doLast {
      def findMe = 'org/apache/commons/beanutils/PropertyUtilsBean.class'
      def matches = configurations.runtime.findAll { f ->
         f.name.endsWith('.jar') && 
            !(zipTree(f).matching { include findMe }.empty) 
      }
      println "Found $findMe in ${matches*.name}"
   } 
}
like image 45
lance-java Avatar answered Oct 05 '22 10:10

lance-java