fix: project initialization error is not handled properly

This commit is contained in:
Akash Yadav
2023-08-06 20:46:30 +05:30
parent e725d1cacf
commit 8f74b0faee
4 changed files with 78 additions and 41 deletions
@@ -80,9 +80,12 @@ abstract class ProjectHandlerActivity : BaseEditorActivity(), IProjectHandler {
protected val mBuildEventListener = EditorBuildEventListener()
companion object {
const val STATE_KEY_FROM_SAVED_INSTANACE = "ide.editor.isFromSavedInstance"
const val STATE_KEY_SHOULD_INITIALIZE = "ide.editor.isInitializing"
@JvmStatic private val buildServiceConnection = GradleBuildServiceConnnection()
@JvmStatic
private val buildServiceConnection = GradleBuildServiceConnnection()
}
abstract fun doCloseAll(runAfter: () -> Unit)
@@ -258,9 +261,11 @@ abstract class ProjectHandlerActivity : BaseEditorActivity(), IProjectHandler {
this.initializingFuture!!.whenCompleteAsync { result, error ->
releaseServerListener()
if (result == null || error != null) {
if (result == null || !result.isSuccessful || error != null) {
log.error("An error occurred initializing the project with Tooling API", error)
setStatus(getString(string.msg_project_initialization_failed))
ThreadUtils.runOnUiThread {
postProjectInit(false)
}
return@whenCompleteAsync
}
@@ -306,7 +311,7 @@ abstract class ProjectHandlerActivity : BaseEditorActivity(), IProjectHandler {
cachedInitResult = result
setupProject()
notifyProjectUpdate()
ThreadUtils.runOnUiThread { postProjectInit() }
ThreadUtils.runOnUiThread { postProjectInit(true) }
}
protected open fun preProjectInit() {
@@ -314,7 +319,15 @@ abstract class ProjectHandlerActivity : BaseEditorActivity(), IProjectHandler {
viewModel.isInitializing = true
}
protected open fun postProjectInit() {
protected open fun postProjectInit(isSuccessful: Boolean) {
if (!isSuccessful) {
setStatus(getString(string.msg_project_initialization_failed))
flashError(string.msg_project_initialization_failed)
viewModel.isInitializing = false
projectInitialized = false
return
}
initialSetup()
setStatus(getString(string.msg_project_initialized))
viewModel.isInitializing = false
@@ -336,7 +349,8 @@ abstract class ProjectHandlerActivity : BaseEditorActivity(), IProjectHandler {
val moduleDirs =
try {
rootProject!!.subProjects.stream().map(GradleProject::projectDir).collect(Collectors.toList())
rootProject!!.subProjects.stream().map(GradleProject::projectDir)
.collect(Collectors.toList())
} catch (e: Throwable) {
flashError(getString(string.msg_no_modules))
emptyList()
@@ -392,10 +406,10 @@ abstract class ProjectHandlerActivity : BaseEditorActivity(), IProjectHandler {
if (extensions.isNotEmpty()) {
if (extensions.contains("|")) {
for (str in
extensions
.split(Pattern.quote("|").toRegex())
.dropLastWhile { it.isEmpty() }
.toTypedArray()) {
extensions
.split(Pattern.quote("|").toRegex())
.dropLastWhile { it.isEmpty() }
.toTypedArray()) {
if (str.trim().isEmpty()) {
continue
}
@@ -0,0 +1,26 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.tooling.impl
/**
* Exception thrown when the project fails to sync/initialize.
*
* @author Akash Yadav
*/
class ProjectInitializationExeption(reason: String = "", cause: Throwable? = null) :
RuntimeException("Failed to initialize project: $reason", cause)
@@ -43,7 +43,6 @@ import com.itsaky.androidide.tooling.impl.sync.ModelBuilderException
import com.itsaky.androidide.tooling.impl.sync.RootModelBuilder
import com.itsaky.androidide.tooling.impl.util.StopWatch
import com.itsaky.androidide.utils.ILogger
import org.eclipse.lsp4j.jsonrpc.CompletableFutures
import org.gradle.tooling.BuildCancelledException
import org.gradle.tooling.BuildException
import org.gradle.tooling.CancellationTokenSource
@@ -71,7 +70,7 @@ internal class ToolingApiServerImpl(private val project: ProjectImpl) :
private val log = ILogger.newInstance(javaClass.simpleName)
override fun initialize(params: InitializeProjectMessage): CompletableFuture<InitializeResult> {
return CompletableFutures.computeAsync {
return CompletableFuture.supplyAsync {
try {
if (initialized && connector != null) {
connector?.disconnect()
@@ -91,11 +90,8 @@ internal class ToolingApiServerImpl(private val project: ProjectImpl) :
stopWatch.lap("Connector created")
if (this.connector == null) {
throw CompletionException(
RuntimeException(
"Unable to create gradle connector for project directory: ${params.directory}"
)
)
throw IllegalStateException(
"Unable to create gradle connector for project directory: ${params.directory}")
}
notifyBeforeBuild(BuildInfo(emptyList()))
@@ -103,11 +99,17 @@ internal class ToolingApiServerImpl(private val project: ProjectImpl) :
val connection = this.connector!!.connect()
stopWatch.lapFromLast("Project connection established")
val project = RootModelBuilder.build(connection to "debug") as? ProjectImpl?
project ?: throw ModelBuilderException("Failed to build project model")
stopWatch.lapFromLast("Project read successful")
val project = try {
val impl = RootModelBuilder.build(connection to "debug") as? ProjectImpl?
?: throw ModelBuilderException("Failed to build project model")
impl
} catch (err: Throwable) {
throw err
} finally {
connection.close()
}
connection.close()
stopWatch.lapFromLast("Project read successful")
stopWatch.log()
this.project.rootProject = project.rootProject
@@ -115,13 +117,12 @@ internal class ToolingApiServerImpl(private val project: ProjectImpl) :
initialized = true
notifyBuildSuccess(emptyList())
return@computeAsync InitializeResult(emptyMap())
return@supplyAsync InitializeResult(true)
} catch (err: Throwable) {
log.error(err)
log.error("Failed to initialize project", err)
notifyBuildFailure(emptyList())
return@supplyAsync InitializeResult(false)
}
return@computeAsync InitializeResult(emptyMap())
}
}
@@ -130,14 +131,14 @@ internal class ToolingApiServerImpl(private val project: ProjectImpl) :
}
override fun getRootProject(): CompletableFuture<IProject> {
return CompletableFutures.computeAsync {
return CompletableFuture.supplyAsync {
assertProjectInitialized()
return@computeAsync this.project
return@supplyAsync this.project
}
}
override fun executeTasks(message: TaskExecutionMessage): CompletableFuture<TaskExecutionResult> {
return CompletableFutures.computeAsync {
return CompletableFuture.supplyAsync {
assertProjectInitialized()
log.debug("Received request to run tasks.", message)
@@ -177,10 +178,10 @@ internal class ToolingApiServerImpl(private val project: ProjectImpl) :
builder.run()
this.buildCancellationToken = null
notifyBuildSuccess(message.tasks)
return@computeAsync TaskExecutionResult(true, null)
return@supplyAsync TaskExecutionResult(true, null)
} catch (error: Throwable) {
notifyBuildFailure(message.tasks)
return@computeAsync TaskExecutionResult(false, getTaskFailureType(error))
return@supplyAsync TaskExecutionResult(false, getTaskFailureType(error))
}
}
}
@@ -222,9 +223,9 @@ internal class ToolingApiServerImpl(private val project: ProjectImpl) :
}
override fun cancelCurrentBuild(): CompletableFuture<BuildCancellationRequestResult> {
return CompletableFutures.computeAsync {
return CompletableFuture.supplyAsync {
if (this.buildCancellationToken == null) {
return@computeAsync BuildCancellationRequestResult(
return@supplyAsync BuildCancellationRequestResult(
false,
BuildCancellationRequestResult.Reason.NO_RUNNING_BUILD
)
@@ -235,10 +236,10 @@ internal class ToolingApiServerImpl(private val project: ProjectImpl) :
} catch (e: Exception) {
val failureReason = CANCELLATION_ERROR
failureReason.message = "${failureReason.message}: ${e.message}"
return@computeAsync BuildCancellationRequestResult(false, failureReason)
return@supplyAsync BuildCancellationRequestResult(false, failureReason)
}
return@computeAsync BuildCancellationRequestResult(true, null)
return@supplyAsync BuildCancellationRequestResult(true, null)
}
}
@@ -270,7 +271,7 @@ internal class ToolingApiServerImpl(private val project: ProjectImpl) :
}
private fun assertProjectInitialized() {
if (!isServerInitialized().get() || project == null) {
if (!isServerInitialized().get()) {
throw CompletionException(IllegalStateException("Project is not initialized!"))
}
}
@@ -17,14 +17,10 @@
package com.itsaky.androidide.tooling.api.messages.result
import com.itsaky.androidide.builder.model.DefaultProjectSyncIssues
/**
* Result received after an initialize project request.
*
* @param project The initialized project model.
* @param syncIssues The issues reported by the Android Gradle Plugin during the project sync.
*
* @param isSuccessful Whether the project initialization was successful.
* @author Akash Yadav
*/
data class InitializeResult(val syncIssues: Map<String, DefaultProjectSyncIssues>)
class InitializeResult(val isSuccessful: Boolean)