From 63dd0fc4ae3b1168b2ad2068a2fcd964a73dc0e7 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Fri, 15 Jan 2016 15:51:30 +0100 Subject: [PATCH] streamlined plugin update logic --- .../kotlin/idea/KotlinPluginUpdater.kt | 156 ++++++++++-------- .../actions/ConfigurePluginUpdatesAction.kt | 4 +- 2 files changed, 86 insertions(+), 74 deletions(-) diff --git a/idea/src/org/jetbrains/kotlin/idea/KotlinPluginUpdater.kt b/idea/src/org/jetbrains/kotlin/idea/KotlinPluginUpdater.kt index 34598511d1c..fdf500779cb 100644 --- a/idea/src/org/jetbrains/kotlin/idea/KotlinPluginUpdater.kt +++ b/idea/src/org/jetbrains/kotlin/idea/KotlinPluginUpdater.kt @@ -42,17 +42,35 @@ import com.intellij.util.Alarm import com.intellij.util.io.HttpRequests import com.intellij.util.text.VersionComparatorUtil import java.io.File +import java.io.PrintWriter +import java.io.StringWriter import java.net.URLEncoder import java.util.concurrent.TimeUnit sealed class PluginUpdateStatus { object LatestVersionInstalled : PluginUpdateStatus() - class Update(val newVersion: String, - val descriptorToInstall: IdeaPluginDescriptor, + class Update(val pluginDescriptor: IdeaPluginDescriptor, val hostToInstallFrom: String?) : PluginUpdateStatus() - class CheckFailed(val message: String) : PluginUpdateStatus() + class CheckFailed(val message: String, val detail: String? = null) : PluginUpdateStatus() + + fun mergeWith(other: PluginUpdateStatus): PluginUpdateStatus { + if (other is Update && (this is LatestVersionInstalled || + (this is Update && VersionComparatorUtil.compare(other.pluginDescriptor.version, + pluginDescriptor.version) > 0))) { + return other + } + return this + } + + companion object { + fun fromException(message: String, e: Exception): PluginUpdateStatus { + val writer = StringWriter() + e.printStackTrace(PrintWriter(writer)) + return CheckFailed(message, writer.toString()) + } + } } class KotlinPluginUpdater(val propertiesComponent: PropertiesComponent) : Disposable { @@ -69,8 +87,9 @@ class KotlinPluginUpdater(val propertiesComponent: PropertiesComponent) : Dispos val lastUpdateTime = java.lang.Long.parseLong(propertiesComponent.getValue(PROPERTY_NAME, "0")) if (lastUpdateTime == 0L || System.currentTimeMillis() - lastUpdateTime > TimeUnit.DAYS.toMillis(1)) { queueUpdateCheck { updateStatus -> - if (updateStatus is PluginUpdateStatus.Update) { - notifyPluginUpdateAvailable(updateStatus) + when (updateStatus) { + is PluginUpdateStatus.Update -> notifyPluginUpdateAvailable(updateStatus) + is PluginUpdateStatus.CheckFailed -> LOG.info("Plugin update check failed: ${updateStatus.message}, details: ${updateStatus.detail}") } true } @@ -86,61 +105,37 @@ class KotlinPluginUpdater(val propertiesComponent: PropertiesComponent) : Dispos } } - private fun updateCheck(callback: (PluginUpdateStatus) -> Boolean) { - try { - var (mainRepoUpdateSuccess, latestVersionInRepository) = getPluginVersionFromMainRepository() - var descriptorToInstall: IdeaPluginDescriptor? = initPluginDescriptor(latestVersionInRepository) - var hostToInstallFrom: String? = null - - for (host in RepositoryHelper.getPluginHosts().filterNotNull()) { - val plugins = try { - RepositoryHelper.loadPlugins(host, null) - } - catch(e: Exception) { - LOG.info("Checking custom plugin repository $host failed", e) - continue - } - - val kotlinPlugin = plugins.find { it.pluginId == KotlinPluginUtil.KOTLIN_PLUGIN_ID } - if (kotlinPlugin != null && VersionComparatorUtil.compare(kotlinPlugin.version, latestVersionInRepository) > 0) { - latestVersionInRepository = kotlinPlugin.version - descriptorToInstall = kotlinPlugin - hostToInstallFrom = host - } - } - - checkQueued = false - - if (mainRepoUpdateSuccess || latestVersionInRepository != null) { - recordSuccessfulUpdateCheck() - if (latestVersionInRepository != null && VersionComparatorUtil.compare(latestVersionInRepository, KotlinPluginUtil.getPluginVersion()) > 0) { - ApplicationManager.getApplication().invokeLater({ - callback(PluginUpdateStatus.Update(latestVersionInRepository!!, descriptorToInstall!!, hostToInstallFrom)) - }, ModalityState.any()) - } - else { - ApplicationManager.getApplication().invokeLater({ - callback(PluginUpdateStatus.LatestVersionInstalled) - }, ModalityState.any()) - } - } - else { - ApplicationManager.getApplication().invokeLater { - if (callback(PluginUpdateStatus.CheckFailed("Plugin not found in repository"))) queueUpdateCheck(callback) - } - } - } - catch(e: Exception) { - LOG.info("Kotlin plugin update check failed", e) - checkQueued = false - ApplicationManager.getApplication().invokeLater { - if (callback(PluginUpdateStatus.CheckFailed(e.message ?: "Unknown error"))) queueUpdateCheck(callback) - } + fun runUpdateCheck(callback: (PluginUpdateStatus) -> Boolean) { + ApplicationManager.getApplication().executeOnPooledThread { + updateCheck(callback) } } - private fun initPluginDescriptor(newVersion: String?): IdeaPluginDescriptor? { - if (newVersion == null) return null + private fun updateCheck(callback: (PluginUpdateStatus) -> Boolean) { + var updateStatus: PluginUpdateStatus + try { + updateStatus = checkUpdatesInMainRepository() + + for (host in RepositoryHelper.getPluginHosts().filterNotNull()) { + val customUpdateStatus = checkUpdatesInCustomRepository(host) + updateStatus = updateStatus.mergeWith(customUpdateStatus) + } + } + catch(e: Exception) { + updateStatus = PluginUpdateStatus.fromException("Kotlin plugin update check failed", e) + } + + checkQueued = false + + if (updateStatus !is PluginUpdateStatus.CheckFailed) { + recordSuccessfulUpdateCheck() + } + ApplicationManager.getApplication().invokeLater({ + callback(updateStatus) + }, ModalityState.any()) + } + + private fun initPluginDescriptor(newVersion: String): IdeaPluginDescriptor { val originalPlugin = PluginManager.getPlugin(KotlinPluginUtil.KOTLIN_PLUGIN_ID)!! return PluginNode(KotlinPluginUtil.KOTLIN_PLUGIN_ID).apply { version = newVersion @@ -149,31 +144,48 @@ class KotlinPluginUpdater(val propertiesComponent: PropertiesComponent) : Dispos } } - data class RepositoryCheckResult(val success: Boolean, val newVersion: String?) - - fun getPluginVersionFromMainRepository(): RepositoryCheckResult { + fun checkUpdatesInMainRepository(): PluginUpdateStatus { val buildNumber = ApplicationInfo.getInstance().build.asString() - val pluginVersion = KotlinPluginUtil.getPluginVersion() + val currentVersion = KotlinPluginUtil.getPluginVersion() val os = URLEncoder.encode(SystemInfo.OS_NAME + " " + SystemInfo.OS_VERSION, CharsetToolkit.UTF8) val uid = UpdateChecker.getInstallationUID(propertiesComponent) - val url = "https://plugins.jetbrains.com/plugins/list?pluginId=6954&build=$buildNumber&pluginVersion=$pluginVersion&os=$os&uuid=$uid" + val url = "https://plugins.jetbrains.com/plugins/list?pluginId=6954&build=$buildNumber&pluginVersion=$currentVersion&os=$os&uuid=$uid" val responseDoc = HttpRequests.request(url).connect { JDOMUtil.load(it.inputStream) } if (responseDoc.name != "plugin-repository") { - LOG.info("Unexpected plugin repository response: ${JDOMUtil.writeElement(responseDoc, "\n")}") - return RepositoryCheckResult(false, null) + return PluginUpdateStatus.CheckFailed("Unexpected plugin repository response", JDOMUtil.writeElement(responseDoc, "\n")) } if (responseDoc.children.isEmpty()) { // No plugin version compatible with current IDEA build; don't retry updates - return RepositoryCheckResult(true, null) + return PluginUpdateStatus.LatestVersionInstalled } - val version = responseDoc.getChild("category")?.getChild("idea-plugin")?.getChild("version")?.text - if (version == null) { - LOG.info("Couldn't find plugin version in repository response: ${JDOMUtil.writeElement(responseDoc, "\n")}") - return RepositoryCheckResult(false, null) + val newVersion = responseDoc.getChild("category")?.getChild("idea-plugin")?.getChild("version")?.text + if (newVersion == null) { + return PluginUpdateStatus.CheckFailed("Couldn't find plugin version in repository response", JDOMUtil.writeElement(responseDoc, "\n")) } - return RepositoryCheckResult(true, version) + val pluginDescriptor = initPluginDescriptor(newVersion) + return updateIfNotLatest(pluginDescriptor, null) + } + + private fun checkUpdatesInCustomRepository(host: String): PluginUpdateStatus { + val plugins = try { + RepositoryHelper.loadPlugins(host, null) + } + catch(e: Exception) { + return PluginUpdateStatus.fromException("Checking custom plugin repository $host failed", e) + } + + val kotlinPlugin = plugins.find { it.pluginId == KotlinPluginUtil.KOTLIN_PLUGIN_ID } ?: return PluginUpdateStatus.LatestVersionInstalled + return updateIfNotLatest(kotlinPlugin, host) + } + + private fun updateIfNotLatest(kotlinPlugin: IdeaPluginDescriptor, host: String?): PluginUpdateStatus { + if (VersionComparatorUtil.compare(kotlinPlugin.version, KotlinPluginUtil.getPluginVersion()) <= 0) { + return PluginUpdateStatus.LatestVersionInstalled + } + + return PluginUpdateStatus.Update(kotlinPlugin, host) } private fun recordSuccessfulUpdateCheck() { @@ -184,7 +196,7 @@ class KotlinPluginUpdater(val propertiesComponent: PropertiesComponent) : Dispos private fun notifyPluginUpdateAvailable(update: PluginUpdateStatus.Update) { val notification = notificationGroup.createNotification( "Kotlin", - "A new version ${update.newVersion} of the Kotlin plugin is available. Install", + "A new version ${update.pluginDescriptor.version} of the Kotlin plugin is available. Install", NotificationType.INFORMATION) { notification, event -> notification.expire() installPluginUpdate(update) { @@ -197,7 +209,7 @@ class KotlinPluginUpdater(val propertiesComponent: PropertiesComponent) : Dispos fun installPluginUpdate(update: PluginUpdateStatus.Update, cancelCallback: () -> Unit = {}) { - val descriptor = update.descriptorToInstall + val descriptor = update.pluginDescriptor val pluginDownloader = PluginDownloader.createDownloader(descriptor, update.hostToInstallFrom, null) ProgressManager.getInstance().run(object : Task.Backgroundable(null, "Downloading plugins", true) { override fun run(indicator: ProgressIndicator) { diff --git a/idea/src/org/jetbrains/kotlin/idea/actions/ConfigurePluginUpdatesAction.kt b/idea/src/org/jetbrains/kotlin/idea/actions/ConfigurePluginUpdatesAction.kt index 5728b29f266..c7d4d879ff5 100644 --- a/idea/src/org/jetbrains/kotlin/idea/actions/ConfigurePluginUpdatesAction.kt +++ b/idea/src/org/jetbrains/kotlin/idea/actions/ConfigurePluginUpdatesAction.kt @@ -46,7 +46,7 @@ class ConfigurePluginUpdatesDialog(project: Project) : DialogWrapper(project, fa saveSettings() form.updateCheckProgressIcon.resume() resetUpdateStatus() - KotlinPluginUpdater.getInstance().queueUpdateCheck() { pluginUpdateStatus -> + KotlinPluginUpdater.getInstance().runUpdateCheck{ pluginUpdateStatus -> form.updateCheckProgressIcon.suspend() when (pluginUpdateStatus) { PluginUpdateStatus.LatestVersionInstalled -> @@ -55,7 +55,7 @@ class ConfigurePluginUpdatesDialog(project: Project) : DialogWrapper(project, fa is PluginUpdateStatus.Update -> { update = pluginUpdateStatus form.installButton.isVisible = true - form.updateStatusLabel.text = "A new version ${pluginUpdateStatus.newVersion} is available" + form.updateStatusLabel.text = "A new version ${pluginUpdateStatus.pluginDescriptor.version} is available" } is PluginUpdateStatus.CheckFailed ->