All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.gradle.internal.ClassLoadersCachingBuildService.kt Maven / Gradle / Ivy

There is a newer version: 2.1.0-RC
Show newest version
/*
 * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
 * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
 */

package org.jetbrains.kotlin.gradle.internal

import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.logging.Logging
import org.gradle.api.provider.Property
import org.gradle.api.services.BuildService
import org.gradle.api.services.BuildServiceParameters
import org.gradle.api.tasks.Internal
import org.jetbrains.kotlin.gradle.tasks.withType
import org.jetbrains.kotlin.gradle.utils.SingleActionPerProject
import org.jetbrains.kotlin.gradle.utils.registerClassLoaderScopedBuildService
import java.io.File
import java.net.URLClassLoader
import java.util.concurrent.ConcurrentHashMap

internal interface UsesClassLoadersCachingBuildService : Task {
    @get:Internal
    val classLoadersCachingService: Property
}

/**
 * A [BuildService] for caching [ClassLoader] instances
 */
internal abstract class ClassLoadersCachingBuildService : BuildService {
    private val logger = Logging.getLogger(javaClass)

    fun getClassLoader(
        classpath: List,
        parentClassLoaderProvider: ParentClassLoaderProvider = DefaultParentClassLoaderProvider()
    ): ClassLoader {
        return classLoaders.computeIfAbsent(ClassLoaderCacheKey(classpath, parentClassLoaderProvider)) {
            logger.debug("Creating a new classloader for classpath $classpath")
            URLClassLoader(classpath.map { it.toURI().toURL() }.toTypedArray(), parentClassLoaderProvider.getClassLoader())
        }
    }

    companion object {
        // The service could be used by multiple tasks in parallel, so the map have to be synchronized
        private val classLoaders = ConcurrentHashMap()

        fun registerIfAbsent(project: Project) =
            project.gradle.registerClassLoaderScopedBuildService(ClassLoadersCachingBuildService::class).also { serviceProvider ->
                SingleActionPerProject.run(project, UsesClassLoadersCachingBuildService::class.java.name) {
                    project.tasks.withType().configureEach { task ->
                        task.usesService(serviceProvider)
                    }
                }
            }
    }
}

private data class ClassLoaderCacheKey(
    val classpath: List,
    val parentClassLoaderProvider: ParentClassLoaderProvider,
)

/**
 * A provider of the parent [ClassLoader] for a newly created ClassLoader instances.
 *
 * An implementation must override `equals` and `hashCode`! It's used as a part of a [Map] key
 */
internal fun interface ParentClassLoaderProvider {
    fun getClassLoader(): ClassLoader
}

private class DefaultParentClassLoaderProvider : ParentClassLoaderProvider {
    override fun getClassLoader(): ClassLoader = javaClass.classLoader

    override fun hashCode() = javaClass.hashCode()

    override fun equals(other: Any?) = other is DefaultParentClassLoaderProvider && other.javaClass == javaClass
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy