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

com.jetbrains.pluginverifier.plugin.SizeLimitedPluginDetailsCache.kt Maven / Gradle / Ivy

/*
 * Copyright 2000-2020 JetBrains s.r.o. and other contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
 */

package com.jetbrains.pluginverifier.plugin

import com.jetbrains.pluginverifier.repository.PluginInfo
import com.jetbrains.pluginverifier.repository.WithIdePlugin
import com.jetbrains.pluginverifier.repository.cache.ResourceCacheEntry
import com.jetbrains.pluginverifier.repository.cache.ResourceCacheEntryResult
import com.jetbrains.pluginverifier.repository.cache.createSizeLimitedResourceCache
import com.jetbrains.pluginverifier.repository.cleanup.SizeWeight
import com.jetbrains.pluginverifier.repository.provider.ProvideResult
import com.jetbrains.pluginverifier.repository.provider.ResourceProvider
import com.jetbrains.pluginverifier.repository.repositories.bundled.BundledPluginInfo
import com.jetbrains.pluginverifier.repository.repositories.dependency.DependencyPluginInfo
import com.jetbrains.pluginverifier.repository.repositories.local.LocalPluginInfo

/**
 * This cache is intended to open and cache [PluginDetails] for
 * use by multiple threads. It is necessary because the details creation may be expensive
 * as it requires downloading the plugin, reading its class files and registering a file lock.
 *
 * The cache must be [closed] [close] on the application shutdown to free all the details.
 */
class SizeLimitedPluginDetailsCache(
  cacheSize: Int,
  pluginFileProvider: PluginFileProvider,
  pluginDetailsProvider: PluginDetailsProvider
) : PluginDetailsCache {

  private val internalCache = createSizeLimitedResourceCache(
    cacheSize,
    PluginDetailsResourceProvider(pluginFileProvider, pluginDetailsProvider),
    { it.close() },
    "PluginDetailsCache"
  )

  /**
   * Provides the [PluginDetails] of the given [pluginInfo] wrapped in a [Result].
   */
  @Throws(InterruptedException::class)
  override fun getPluginDetailsCacheEntry(pluginInfo: PluginInfo): PluginDetailsCache.Result {
    return when (val internalResult = internalCache.getResourceCacheEntry(pluginInfo)) {
      is ResourceCacheEntryResult.Found -> {
        val internalEntry = internalResult.resourceCacheEntry
        val pluginDetailsProviderResult = internalEntry.resource
        @Suppress("UNCHECKED_CAST")
        when (pluginDetailsProviderResult) {
          is PluginDetailsProvider.Result.Provided ->
            PluginDetailsCache.Result.Provided(internalEntry as ResourceCacheEntry)

          is PluginDetailsProvider.Result.InvalidPlugin ->
            PluginDetailsCache.Result.InvalidPlugin(internalEntry as ResourceCacheEntry)

          is PluginDetailsProvider.Result.Failed ->
            PluginDetailsCache.Result.Failed(pluginDetailsProviderResult.reason, pluginDetailsProviderResult.error)
        }
      }
      is ResourceCacheEntryResult.Failed -> PluginDetailsCache.Result.Failed(internalResult.message, internalResult.error)
      is ResourceCacheEntryResult.NotFound -> PluginDetailsCache.Result.FileNotFound(internalResult.message)
    }
  }

  override fun close() = internalCache.close()

}

/**
 * Bridge utility class that maps [PluginDetailsProvider] to [ResourceProvider].
 */
private class PluginDetailsResourceProvider(
  val pluginFileProvider: PluginFileProvider,
  val pluginDetailsProvider: PluginDetailsProvider
) : ResourceProvider {

  override fun provide(key: PluginInfo) = when (key) {
    is LocalPluginInfo -> ProvideResult.Provided(pluginDetailsProvider.providePluginDetails(key, key.idePlugin))
    is BundledPluginInfo -> ProvideResult.Provided(pluginDetailsProvider.providePluginDetails(key, key.idePlugin))
    is DependencyPluginInfo -> provideDependencyDetails(key)
    else -> provideFileAndDetails(key)
  }

  private fun provideFileAndDetails(pluginInfo: PluginInfo): ProvideResult {
    return pluginFileProvider
      .getPluginFile(pluginInfo)
      .provideDetails(pluginInfo)
  }

  private fun provideDependencyDetails(dependency: DependencyPluginInfo): ProvideResult {
    val unwrappedPluginInfo = dependency.pluginInfo
    val unwrappedPlugin = (unwrappedPluginInfo as? WithIdePlugin)?.idePlugin
    return if (unwrappedPlugin != null) {
      pluginDetailsProvider.providePluginDetails(unwrappedPluginInfo, unwrappedPlugin).provided
    } else {
      pluginFileProvider
        .getPluginFile(unwrappedPluginInfo)
        .provideDetails(dependency)
    }
  }

  private fun PluginFileProvider.Result.provideDetails(pluginInfo: PluginInfo): ProvideResult {
    return when (this) {
      is PluginFileProvider.Result.Found -> {
        pluginDetailsProvider
          .providePluginDetails(pluginInfo, pluginFileLock)
          .provided
      }

      is PluginFileProvider.Result.NotFound -> ProvideResult.NotFound(reason)
      is PluginFileProvider.Result.Failed -> ProvideResult.Failed(reason, error)
    }
  }

  private val PluginDetailsProvider.Result.provided
    get() = ProvideResult.Provided(this)

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy