From d09c7efa6a3cabe6798394a44401892ad9f1bf55 Mon Sep 17 00:00:00 2001 From: Pavlo Skliar Date: Mon, 23 Jun 2025 15:23:00 +0200 Subject: [PATCH] IJPL-175943 - Migrate databases to EEL GitOrigin-RevId: 5beea7d67c2777ecdedb62563935314c2a48bcff --- .../messages/DataGridBundle.properties | 3 +- platform/execution/api-dump-unreviewed.txt | 1 - platform/execution/api-dump.txt | 2 + .../execution/rmi/RemoteProcessSupport.java | 4 +- .../resources/META-INF/IdeCore.xml | 2 + .../eel/EelDownloadableFileServiceImpl.kt | 20 ++ .../impl/eel/EelFileDownloaderImpl.kt | 183 ++++++++++++++++++ .../eel/EelDownloadableFileService.kt | 20 ++ .../util/download/eel/EelFileDownloader.kt | 24 +++ .../eel/EelMavenServerRemoteProcessSupport.kt | 2 +- 10 files changed, 256 insertions(+), 5 deletions(-) create mode 100644 platform/ide-core-impl/src/com/intellij/util/download/impl/eel/EelDownloadableFileServiceImpl.kt create mode 100644 platform/ide-core-impl/src/com/intellij/util/download/impl/eel/EelFileDownloaderImpl.kt create mode 100644 platform/ide-core/src/com/intellij/util/download/eel/EelDownloadableFileService.kt create mode 100644 platform/ide-core/src/com/intellij/util/download/eel/EelFileDownloader.kt diff --git a/grid/core-impl/resources/messages/DataGridBundle.properties b/grid/core-impl/resources/messages/DataGridBundle.properties index 7fb488b63dc3..bb82f9262509 100644 --- a/grid/core-impl/resources/messages/DataGridBundle.properties +++ b/grid/core-impl/resources/messages/DataGridBundle.properties @@ -418,4 +418,5 @@ separator.TableViewColoring.mode=Table Color Mapping separator.TableViewColoring.options=Options button.copy=Copy separator.order.by=ORDER BY -separator.add.to.order.by=Add to ORDER BY \ No newline at end of file +separator.add.to.order.by=Add to ORDER BY +driver.expert.properties.use.eel=Use EEL \ No newline at end of file diff --git a/platform/execution/api-dump-unreviewed.txt b/platform/execution/api-dump-unreviewed.txt index b5e3d6e08ed1..8dac7f024702 100644 --- a/platform/execution/api-dump-unreviewed.txt +++ b/platform/execution/api-dump-unreviewed.txt @@ -821,7 +821,6 @@ a:com.intellij.execution.rmi.RemoteProcessSupport - p:logText(java.lang.Object,com.intellij.execution.process.ProcessEvent,com.intellij.openapi.util.Key):V - p:onCreated(java.lang.Object,java.lang.Object,java.lang.Object):V - p:onProcessTerminated(com.intellij.execution.process.ProcessEvent):V -- p:publishPort(I):I - release(java.lang.Object,java.lang.Object):java.util.concurrent.Future - p:sendDataAfterStart(com.intellij.execution.process.ProcessHandler):V - stopAll():V diff --git a/platform/execution/api-dump.txt b/platform/execution/api-dump.txt index 290bbda5e931..7f8e6d90959d 100644 --- a/platform/execution/api-dump.txt +++ b/platform/execution/api-dump.txt @@ -182,6 +182,8 @@ com.intellij.execution.filters.Filter - com.intellij.openapi.project.PossiblyDumbAware - sf:EMPTY_ARRAY:com.intellij.execution.filters.Filter[] - a:applyFilter(java.lang.String,I):com.intellij.execution.filters.Filter$Result +a:com.intellij.execution.rmi.RemoteProcessSupport +- p:publishPort(I,java.lang.Object):I com.intellij.execution.runners.ProcessProxy - a:attach(com.intellij.execution.process.ProcessHandler):V - canSendBreak():Z diff --git a/platform/execution/src/com/intellij/execution/rmi/RemoteProcessSupport.java b/platform/execution/src/com/intellij/execution/rmi/RemoteProcessSupport.java index 4c8ba7a7a2d6..b74f145c890c 100644 --- a/platform/execution/src/com/intellij/execution/rmi/RemoteProcessSupport.java +++ b/platform/execution/src/com/intellij/execution/rmi/RemoteProcessSupport.java @@ -221,7 +221,7 @@ public abstract class RemoteProcessSupport { } } - protected int publishPort(int port) { + protected int publishPort(int port, @NotNull Target target) { return port; } @@ -428,7 +428,7 @@ public abstract class RemoteProcessSupport { String id = data.get(2); String host = getRemoteHost(); LOG.info("Started remote process on: " + host + ":" + port + "with service port " + servicesPort + " and id = " + id); - result = new RunningInfo(info.handler, host, publishPort(port), id, publishPort(servicesPort)); + result = new RunningInfo(info.handler, host, publishPort(port, key.first), id, publishPort(servicesPort, key.first)); myProcMap.put(key, result); myProcMap.notifyAll(); } diff --git a/platform/ide-core-impl/resources/META-INF/IdeCore.xml b/platform/ide-core-impl/resources/META-INF/IdeCore.xml index 9adffd4faf2b..366724791670 100644 --- a/platform/ide-core-impl/resources/META-INF/IdeCore.xml +++ b/platform/ide-core-impl/resources/META-INF/IdeCore.xml @@ -15,6 +15,8 @@ + , +) : EelFileDownloader { + + @Throws(IOException::class) + override fun download(targetDir: Path): MutableList> { + val downloadedFiles = Collections.synchronizedList(mutableListOf>()) + val existingFiles = Collections.synchronizedList(mutableListOf>()) + + try { + val progressManager = ConcurrentTasksProgressManager(createIndicator(), myFileDescriptions.size) + val maxParallelDownloads = Runtime.getRuntime().availableProcessors() + LOG.debug("Downloading ${myFileDescriptions.size} files using ${maxParallelDownloads} threads") + val start = System.currentTimeMillis() + val executor = AppExecutorUtil.createBoundedApplicationPoolExecutor("EelFileDownloaderImpl Pool", maxParallelDownloads) + val results: MutableList> = mutableListOf() + val totalSize = AtomicLong() + myFileDescriptions.forEach { + results.add(executor.submit(createDownloadTask(progressManager, targetDir, it, existingFiles, downloadedFiles, totalSize))) + } + + results.forEach { it.getCatching() } + val duration = System.currentTimeMillis() - start + LOG.debug("Downloaded ${StringUtil.formatFileSize(totalSize.get())} in ${StringUtil.formatDuration(duration)}(${duration}ms)") + + return (moveToDir(downloadedFiles, targetDir) + existingFiles).toMutableList() + } + catch (e: ProcessCanceledException) { + deleteFiles(downloadedFiles) + throw e + } + catch (e: IOException) { + deleteFiles(downloadedFiles) + throw e + } + } + + private fun createDownloadTask(progressManager: ConcurrentTasksProgressManager, + targetDir: Path, description: DownloadableFileDescription, + existingFiles: MutableCollection>, + downloadedFiles: MutableCollection>, + totalSize: AtomicLong): Callable { + return Callable { + val indicator = progressManager.createSubTaskIndicator(1) + indicator.checkCanceled() + + val existing = targetDir.resolve(description.getDefaultFileName()) + val url = description.getDownloadUrl() + if (url.startsWith(LIB_SCHEMA)) { + val path = Path.of(url.removePrefix(LIB_SCHEMA)) + val file = PathManager.getLibDir().resolve(path) + existingFiles.add(file to description) + } + else if (url.startsWith(LocalFileSystem.PROTOCOL_PREFIX)) { + val path = Path.of(url.removePrefix(LocalFileSystem.PROTOCOL_PREFIX)) + if (Files.exists(path)) { + existingFiles.add(path to description) + } + } + else { + LOG.debug("Downloading `$url` into `$existing`") + val downloaded: Path + try { + downloaded = downloadFile(description, existing, indicator) + } + catch (e: IOException) { + throw IOException(IdeCoreBundle.message("error.file.download.failed", description.getDownloadUrl(), + e.message), e) + } + if (downloaded == existing) { + existingFiles.add(existing to description) + } + else { + totalSize.addAndGet(Files.size(downloaded)) + downloadedFiles.add(downloaded to description) + } + } + indicator.finished() + null + } + } + + private fun createIndicator(): ProgressIndicator { + val indicator = ProgressManager.getInstance().getProgressIndicator()?.also { it.setIndeterminate(false) } ?: EmptyProgressIndicator() + indicator.setText(IdeCoreBundle.message("progress.downloading.0.files.text", myFileDescriptions.size)) + + return indicator + } + + companion object { + private val LOG = Logger.getInstance(EelFileDownloaderImpl::class.java) + private const val LIB_SCHEMA = "lib://" + + private fun Future.getCatching() { + try { + this.get() + } + catch (_: InterruptedException) { + throw ProcessCanceledException() + } + catch (e: ExecutionException) { + if (e.cause is IOException) { + throw (e.cause as IOException) + } + if (e.cause is ProcessCanceledException) { + throw (e.cause as ProcessCanceledException) + } + LOG.error(e) + } + } + + @Throws(IOException::class) + fun moveToDir( + downloadedFiles: List>, + targetDir: Path + ): List> { + Files.createDirectories(targetDir) + + return downloadedFiles.map { (fromPath, description) -> + val fileName = description.generateFileName { name -> + !Files.exists(targetDir.resolve(name)) + } + + val toPath = targetDir.resolve(fileName) + Files.move(fromPath, toPath, StandardCopyOption.REPLACE_EXISTING) + + toPath to description + } + } + + private fun deleteFiles(pairs: List>) = pairs.forEach { it.first.delete() } + + @Throws(IOException::class) + private fun downloadFile( + description: DownloadableFileDescription, + existingFile: Path, + indicator: ProgressIndicator, + ): Path { + val presentableUrl = description.getPresentableDownloadUrl() + indicator.setText(IdeCoreBundle.message("progress.connecting.to.download.file.text", presentableUrl)) + + return HttpRequests.request(description.getDownloadUrl()).connect(object : HttpRequests.RequestProcessor { + @Throws(IOException::class) + override fun process(request: HttpRequests.Request): Path { + val size = request.getConnection().getContentLength() + if (Files.exists(existingFile) && size.toLong() == Files.size(existingFile)) { + return existingFile + } + + indicator.setText(IdeCoreBundle.message("progress.download.file.text", description.getPresentableFileName(), presentableUrl)) + return request.saveToFile(Files.createTempFile("download.", ".tmp"), indicator) + } + }) + } + } +} \ No newline at end of file diff --git a/platform/ide-core/src/com/intellij/util/download/eel/EelDownloadableFileService.kt b/platform/ide-core/src/com/intellij/util/download/eel/EelDownloadableFileService.kt new file mode 100644 index 000000000000..743223fb9951 --- /dev/null +++ b/platform/ide-core/src/com/intellij/util/download/eel/EelDownloadableFileService.kt @@ -0,0 +1,20 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.util.download.eel + +import com.intellij.openapi.application.ApplicationManager +import com.intellij.util.download.DownloadableFileDescription +import com.intellij.util.download.DownloadableFileSetDescription +import org.jetbrains.annotations.ApiStatus + +@ApiStatus.Internal +abstract class EelDownloadableFileService { + + abstract fun createDownloader(description: DownloadableFileSetDescription): EelFileDownloader + + abstract fun createFileDescription(downloadUrl: String, fileName: String): DownloadableFileDescription + + companion object { + val instance: EelDownloadableFileService + get() = ApplicationManager.getApplication().getService(EelDownloadableFileService::class.java) + } +} \ No newline at end of file diff --git a/platform/ide-core/src/com/intellij/util/download/eel/EelFileDownloader.kt b/platform/ide-core/src/com/intellij/util/download/eel/EelFileDownloader.kt new file mode 100644 index 000000000000..9f9a8b05aa60 --- /dev/null +++ b/platform/ide-core/src/com/intellij/util/download/eel/EelFileDownloader.kt @@ -0,0 +1,24 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.util.download.eel + +import com.intellij.util.download.DownloadableFileDescription +import org.jetbrains.annotations.ApiStatus +import java.io.IOException +import java.nio.file.Path + +/** + * Use [EelDownloadableFileService] to create instances of this interface + */ +@ApiStatus.Internal +interface EelFileDownloader { + + /** + * Download files synchronously. Call this method under progress only (see [com.intellij.openapi.progress.Task]). + * + * @param targetDir target directory for downloaded files + * @return list of downloaded files with their descriptions + * @throws IOException On errors. + */ + @Throws(IOException::class) + fun download(targetDir: Path): MutableList> +} \ No newline at end of file diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/server/eel/EelMavenServerRemoteProcessSupport.kt b/plugins/maven/src/main/java/org/jetbrains/idea/maven/server/eel/EelMavenServerRemoteProcessSupport.kt index 3196d1bf2c58..06175feb5d8b 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/server/eel/EelMavenServerRemoteProcessSupport.kt +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/server/eel/EelMavenServerRemoteProcessSupport.kt @@ -73,7 +73,7 @@ class EelMavenServerRemoteProcessSupport( } @OptIn(DelicateCoroutinesApi::class) - override fun publishPort(port: Int): Int { + override fun publishPort(port: Int, target: Any): Int { myProject.service().coroutineScope.launch { forwardLocalPort(eel.tunnels, port, EelTunnelsApi.HostAddress.Builder(port.toUShort()).hostname(remoteHost).build()) }