IJPL-175943 - Migrate databases to EEL

GitOrigin-RevId: 5beea7d67c2777ecdedb62563935314c2a48bcff
This commit is contained in:
Pavlo Skliar
2025-08-21 08:44:52 +00:00
committed by intellij-monorepo-bot
parent 3d1cc196a5
commit d09c7efa6a
10 changed files with 256 additions and 5 deletions
@@ -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
separator.add.to.order.by=Add to ORDER BY
driver.expert.properties.use.eel=Use EEL
@@ -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
+2
View File
@@ -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
@@ -221,7 +221,7 @@ public abstract class RemoteProcessSupport<Target, EntryPoint, Parameters> {
}
}
protected int publishPort(int port) {
protected int publishPort(int port, @NotNull Target target) {
return port;
}
@@ -428,7 +428,7 @@ public abstract class RemoteProcessSupport<Target, EntryPoint, Parameters> {
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();
}
@@ -15,6 +15,8 @@
<activityTracker implementation="com.intellij.util.MergingUpdateQueueActivityTracker"/>
<applicationService serviceInterface="com.intellij.util.ui.update.MergingUpdateQueueTracker"
serviceImplementation="com.intellij.util.MergingUpdateQueueTrackerImpl"/>
<applicationService serviceInterface="com.intellij.util.download.eel.EelDownloadableFileService"
serviceImplementation="com.intellij.util.download.impl.eel.EelDownloadableFileServiceImpl"/>
<applicationService serviceInterface="com.intellij.util.download.DownloadableFileService"
serviceImplementation="com.intellij.util.download.impl.DownloadableFileServiceImpl"/>
<applicationService serviceInterface="com.intellij.notification.NotificationGroupManager"
@@ -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.impl.eel
import com.intellij.openapi.util.io.FileUtilRt
import com.intellij.util.download.DownloadableFileDescription
import com.intellij.util.download.DownloadableFileSetDescription
import com.intellij.util.download.eel.EelDownloadableFileService
import com.intellij.util.download.eel.EelFileDownloader
import com.intellij.util.download.impl.DownloadableFileDescriptionImpl
internal class EelDownloadableFileServiceImpl : EelDownloadableFileService() {
override fun createDownloader(description: DownloadableFileSetDescription): EelFileDownloader {
return EelFileDownloaderImpl(description.getFiles())
}
override fun createFileDescription(downloadUrl: String, fileName: String): DownloadableFileDescription {
return DownloadableFileDescriptionImpl(downloadUrl, FileUtilRt.getNameWithoutExtension(fileName), FileUtilRt.getExtension(fileName))
}
}
@@ -0,0 +1,183 @@
// 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.impl.eel
import com.intellij.ide.IdeCoreBundle
import com.intellij.openapi.application.PathManager
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.progress.*
import com.intellij.openapi.util.text.StringUtil
import com.intellij.openapi.vfs.*
import com.intellij.util.concurrency.AppExecutorUtil
import com.intellij.util.download.DownloadableFileDescription
import com.intellij.util.download.eel.EelFileDownloader
import com.intellij.util.io.HttpRequests
import com.intellij.util.io.delete
import com.intellij.util.progress.ConcurrentTasksProgressManager
import org.jetbrains.annotations.ApiStatus
import java.io.IOException
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.StandardCopyOption
import java.util.*
import java.util.concurrent.Callable
import java.util.concurrent.ExecutionException
import java.util.concurrent.Future
import java.util.concurrent.atomic.AtomicLong
@ApiStatus.Internal
internal class EelFileDownloaderImpl(
private val myFileDescriptions: MutableList<out DownloadableFileDescription>,
) : EelFileDownloader {
@Throws(IOException::class)
override fun download(targetDir: Path): MutableList<Pair<Path, DownloadableFileDescription>> {
val downloadedFiles = Collections.synchronizedList(mutableListOf<Pair<Path, DownloadableFileDescription>>())
val existingFiles = Collections.synchronizedList(mutableListOf<Pair<Path, DownloadableFileDescription>>())
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<Future<Void>> = 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<Pair<Path, DownloadableFileDescription>>,
downloadedFiles: MutableCollection<Pair<Path, DownloadableFileDescription>>,
totalSize: AtomicLong): Callable<Void> {
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<Void>.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<Pair<Path, DownloadableFileDescription>>,
targetDir: Path
): List<Pair<Path, DownloadableFileDescription>> {
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<Pair<Path, DownloadableFileDescription>>) = 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<Path> {
@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)
}
})
}
}
}
@@ -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>(EelDownloadableFileService::class.java)
}
}
@@ -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<Pair<Path, DownloadableFileDescription>>
}
@@ -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<CoroutineService>().coroutineScope.launch {
forwardLocalPort(eel.tunnels, port, EelTunnelsApi.HostAddress.Builder(port.toUShort()).hostname(remoteHost).build())
}