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

com.jetbrains.pluginverifier.repository.downloader.DownloadProvider.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.repository.downloader

import com.jetbrains.plugin.structure.base.utils.*
import com.jetbrains.pluginverifier.repository.cleanup.fileSize
import com.jetbrains.pluginverifier.repository.provider.ProvideResult
import com.jetbrains.pluginverifier.repository.provider.ResourceProvider
import org.apache.commons.io.FileUtils
import java.io.IOException
import java.nio.file.Files
import java.nio.file.Path

/**
 * [ResourceProvider] responsible for downloading files and directories
 * using provided [downloader] and saving them to the [destinationDirectory]
 * using the [fileNameWithoutExtensionMapper].
 */
class DownloadProvider(
  private val destinationDirectory: Path,
  private val downloader: Downloader,
  private val fileNameWithoutExtensionMapper: (K) -> String
) : ResourceProvider {
  private companion object {
    const val DOWNLOADS_DIRECTORY = ".downloads"
  }

  private val downloadDirectory = destinationDirectory.resolve(DOWNLOADS_DIRECTORY)

  val downloadStatistics = DownloadStatistics()

  init {
    destinationDirectory.createDir()
    downloadDirectory.forceDeleteIfExists()
  }

  @Throws(InterruptedException::class)
  override fun provide(key: K): ProvideResult {
    val downloadEvent = downloadStatistics.downloadStarted()
    val tempDirectory = createTempDirectoryForDownload(key)
    try {
      return with(downloader.download(key, tempDirectory)) {
        when (this) {
          is DownloadResult.Downloaded -> {
            downloadEvent.downloadEnded(downloadedFileOrDirectory.fileSize)
            saveDownloadedFileToFinalDestination(key, downloadedFileOrDirectory, extension, isDirectory)
          }
          is DownloadResult.NotFound -> ProvideResult.NotFound(reason)
          is DownloadResult.FailedToDownload -> ProvideResult.Failed(reason, error)
        }
      }
    } finally {
      tempDirectory.deleteLogged()
    }
  }

  @Synchronized
  private fun saveDownloadedFileToFinalDestination(
    key: K,
    tempDownloadedFile: Path,
    extension: String,
    isDirectory: Boolean
  ): ProvideResult {
    val destination = getDestinationFile(key, isDirectory, extension)
    try {
      moveFileOrDirectory(tempDownloadedFile, destination)
    } catch (e: Exception) {
      e.rethrowIfInterrupted()
      return ProvideResult.Failed("Unable to download $key", e)
    }
    return ProvideResult.Provided(destination)
  }

  private fun getDestinationFile(key: K, isDirectory: Boolean, extension: String): Path {
    check(extension == extension.replaceInvalidFileNameCharacters()) { "Extension must not contain invalid characters: $extension" }

    val nameWithoutExtension = fileNameWithoutExtensionMapper(key).replaceInvalidFileNameCharacters()
    val extensionSuffix = if (isDirectory || extension.isEmpty()) "" else ".$extension"

    var destination = destinationDirectory.resolve(nameWithoutExtension + extensionSuffix)
    var nextSuffix = 1
    while (Files.exists(destination)) {
      val newName = if (extensionSuffix.isEmpty()) {
        "$nameWithoutExtension ($nextSuffix)"
      } else {
        "$nameWithoutExtension ($nextSuffix)$extensionSuffix"
      }
      destination = destinationDirectory.resolve(newName)
      nextSuffix++
    }
    return destination
  }

  private fun createTempDirectoryForDownload(key: K) =
    try {
      Files.createTempDirectory(
        downloadDirectory.createDir(),
        "download-" + fileNameWithoutExtensionMapper(key).replaceInvalidFileNameCharacters() + "-"
      )
    } catch (e: IOException) {
      throw RuntimeException(e)
    }

  private fun moveFileOrDirectory(fileOrDirectory: Path, destination: Path) {
    if (destination.exists()) {
      destination.deleteLogged()
    }
    if (fileOrDirectory.isDirectory) {
      FileUtils.moveDirectory(fileOrDirectory.toFile(), destination.toFile())
    } else {
      FileUtils.moveFile(fileOrDirectory.toFile(), destination.toFile())
    }
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy