Skip to content

Commit 9109943

Browse files
authored
fix(amazonq): fix LinkageError when starting AmazonQLspService (attempt 2) (#5627)
In certain IDE environments, for example when user has the latest Scala plugin, Amazon Q attempts to load lsp4j through transitive optional dependencies instead of through the IDE. Ideally we do not bundle the library, but this is not possible until JetBrains can guarantee stronger isolation between plugins As part of this we optimistically exclude Gson in hopes that it does not have the same issue. SLF4J is additionally packaged due to the same linkage error, though it seems to be pulling in an older version from somewhere else. We also need to bump the Kotlin version, since there is a bug where Kotlin compiler runs out of memory for some reason if we exclude Gson from the distribution bundle.
1 parent 9dad6c9 commit 9109943

File tree

22 files changed

+42
-31
lines changed

22 files changed

+42
-31
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type" : "bugfix",
3+
"description" : "Fix LinkageError while attempting to do Amazon Q inline suggestions in certain environments"
4+
}

buildSrc/src/main/kotlin/software/aws/toolkits/gradle/changelog/ChangeLog.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ enum class ChangeType(val sectionTitle: String) {
3939

4040
class Serializer : StdSerializer<ChangeType>(ChangeType::class.java) {
4141
override fun serialize(value: ChangeType, gen: JsonGenerator?, provider: SerializerProvider?) {
42-
gen?.writeString(value.name.toLowerCase())
42+
gen?.writeString(value.name.lowercase())
4343
}
4444
}
4545
}

buildSrc/src/main/kotlin/software/aws/toolkits/gradle/changelog/tasks/NewChange.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ open class NewChange : ChangeLogTask() {
6565
)
6666
}
6767

68-
private fun newFile(changeType: ChangeType) = nextReleaseDirectory.file("${changeType.name.toLowerCase()}-${UUID.randomUUID()}.json").get().asFile.apply {
68+
private fun newFile(changeType: ChangeType) = nextReleaseDirectory.file("${changeType.name.lowercase()}-${UUID.randomUUID()}.json").get().asFile.apply {
6969
parentFile?.mkdirs()
7070
createNewFile()
7171
}

buildSrc/src/main/kotlin/toolkit-intellij-subplugin.gradle.kts

+5-3
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,11 @@ configurations {
4848

4949
// Exclude dependencies that ship with iDE
5050
exclude(group = "org.slf4j")
51-
// we want kotlinx-coroutines-debug and kotlinx-coroutines-test
52-
exclude(group = "org.jetbrains.kotlinx", "kotlinx-coroutines-core-jvm")
53-
exclude(group = "org.jetbrains.kotlinx", "kotlinx-coroutines-core")
51+
if (!name.startsWith("kotlinCompiler") && !name.startsWith("generateModels") && !name.startsWith("rdGen")) {
52+
// we want kotlinx-coroutines-debug and kotlinx-coroutines-test
53+
exclude(group = "org.jetbrains.kotlinx", "kotlinx-coroutines-core-jvm")
54+
exclude(group = "org.jetbrains.kotlinx", "kotlinx-coroutines-core")
55+
}
5456

5557
resolutionStrategy.eachDependency {
5658
if (requested.group == "org.jetbrains.kotlinx" && requested.name.startsWith("kotlinx-coroutines")) {

buildSrc/src/main/kotlin/toolkit-publishing-conventions.gradle.kts

+2-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ configurations {
5454

5555
// Make sure we exclude stuff we either A) ships with IDE, B) we don't use to cut down on size
5656
runtimeClasspath {
57-
exclude(group = "org.slf4j")
57+
exclude(group = "com.google.code.gson")
58+
exclude(group = "org.slf4j", module = "slf4j-jdk14")
5859
exclude(group = "org.jetbrains.kotlin")
5960
exclude(group = "org.jetbrains.kotlinx")
6061
}

gradle/libs.versions.toml

+3-1
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,10 @@ junit4 = "4.13.2"
1919
junit5 = "5.11.0"
2020
# https://plugins.jetbrains.com/docs/intellij/kotlin.html#adding-kotlin-support
2121
# https://kotlinlang.org/docs/releases.html#release-details
22-
kotlin = "2.0.0"
22+
kotlin = "2.1.20"
2323
# set in <root>/settings.gradle.kts
2424
kotlinCoroutines = "1.8.0"
25+
lsp4j = "0.24.0"
2526
mockito = "5.12.0"
2627
mockitoKotlin = "5.4.0"
2728
mockk = "1.13.17"
@@ -105,6 +106,7 @@ kotlin-coroutinesTest = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-tes
105106
kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" }
106107
kotlin-stdLibJdk8 = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlin" }
107108
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" }
109+
lsp4j = { module = "org.eclipse.lsp4j:org.eclipse.lsp4j", version.ref = "lsp4j" }
108110
mockito-core = { module = "org.mockito:mockito-core", version.ref = "mockito" }
109111
mockito-junit-jupiter = { module = "org.mockito:mockito-junit-jupiter", version.ref = "mockito" }
110112
mockito-kotlin = { module = "org.mockito.kotlin:mockito-kotlin", version.ref = "mockitoKotlin" }

plugins/amazonq/build.gradle.kts

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ dependencies {
3636
implementation(project(":plugin-amazonq:codewhisperer"))
3737
implementation(project(":plugin-amazonq:mynah-ui"))
3838
implementation(project(":plugin-amazonq:shared"))
39+
implementation(libs.lsp4j)
3940

4041
testImplementation(project(":plugin-core"))
4142
}

plugins/amazonq/codewhisperer/jetbrains-community/tstFixtures/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererTestUtil.kt

+4-4
Original file line numberDiff line numberDiff line change
@@ -172,10 +172,10 @@ object CodeWhispererTestUtil {
172172
const val leftContext_success_Iac = "# Create an S3 Bucket named CodeWhisperer in CloudFormation"
173173
const val leftContext_failure_Iac = "Create an S3 Bucket named CodeWhisperer"
174174

175-
internal fun pythonResponseWithToken(token: String): GenerateCompletionsResponse =
175+
fun pythonResponseWithToken(token: String): GenerateCompletionsResponse =
176176
pythonResponse.toBuilder().nextToken(token).build()
177177

178-
internal fun generateMockCompletionDetail(content: String): Completion {
178+
fun generateMockCompletionDetail(content: String): Completion {
179179
val referenceInfo = getReferenceInfo()
180180
return Completion.builder().content(content)
181181
.references(
@@ -184,9 +184,9 @@ object CodeWhispererTestUtil {
184184
.build()
185185
}
186186

187-
internal fun getReferenceInfo() = testReferenceInfoPair[Random.nextInt(testReferenceInfoPair.size)]
187+
fun getReferenceInfo() = testReferenceInfoPair[Random.nextInt(testReferenceInfoPair.size)]
188188

189-
internal fun generateMockCompletionDetail(
189+
fun generateMockCompletionDetail(
190190
content: String,
191191
licenseName: String,
192192
repository: String,

plugins/amazonq/codewhisperer/jetbrains-community/tstFixtures/software/aws/toolkits/jetbrains/services/codewhisperer/importadder/CodeWhispererImportAdderTestBase.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import kotlin.test.fail
1818
open class CodeWhispererImportAdderTestBase(
1919
val importAdder: CodeWhispererImportAdder,
2020
@Rule @JvmField val projectRule: CodeInsightTestFixtureRule,
21-
internal val fileExtension: String,
21+
val fileExtension: String,
2222
) {
2323

2424
fun <T> testCreateNewImportPsiElementReturnValueForStatements(

plugins/core/build.gradle.kts

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ dependencies {
1515
implementation(project(":plugin-core:resources"))
1616
implementation(project(":plugin-core:sdk-codegen"))
1717
implementation(project(":plugin-core:webview"))
18+
implementation(libs.slf4j.api)
1819
}
1920

2021
tasks.check {

plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/utils/TextUtils.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ fun formatText(project: Project, language: Language, content: String): String {
2929
* Designed to convert underscore separated words (e.g. UPDATE_COMPLETE) into title cased human readable text
3030
* (e.g. Update Complete)
3131
*/
32-
fun String.toHumanReadable() = StringUtil.toTitleCase(toLowerCase().replace('_', ' '))
32+
fun String.toHumanReadable() = StringUtil.toTitleCase(lowercase().replace('_', ' '))
3333

3434
fun generateUnifiedPatch(patch: String, filePath: String): TextFilePatch {
3535
val unifiedPatch = "--- $filePath\n+++ $filePath\n$patch"

plugins/core/jetbrains-community/tst/software/aws/toolkits/jetbrains/core/AwsResourceCacheTest.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ class AwsResourceCacheTest {
243243
whenever(mockResource.fetch(any())).thenReturn("hello")
244244
val viewResource = Resource.view(mockResource) { toList() }
245245

246-
val filteredAndMapped = viewResource.filter { it != 'l' }.map { it.toUpperCase() }
246+
val filteredAndMapped = viewResource.filter { it != 'l' }.map { it.uppercaseChar() }
247247
assertThat(sut.getResource(filteredAndMapped, connectionSettings)).hasValue(listOf('H', 'E', 'O'))
248248

249249
val find = viewResource.find { it == 'l' }

plugins/toolkit/jetbrains-core/src/software/aws/toolkits/jetbrains/services/PathMapper.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ class PathMapper(private val mappings: List<PathMapping>) {
5555

5656
fun normalizeLocal(localPath: String): String {
5757
val updatedPath = if (SystemInfo.isWindows) {
58-
localPath.toLowerCase()
58+
localPath.lowercase()
5959
} else {
6060
localPath
6161
}

plugins/toolkit/jetbrains-core/src/software/aws/toolkits/jetbrains/services/cloudformation/CloudFormationTemplate.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ interface CloudFormationTemplate {
4040
}
4141

4242
private fun isYaml(templateFile: VirtualFile): Boolean = templateFile.fileType == YAMLFileType.YML ||
43-
templateFile.extension?.toLowerCase() in YAML_EXTENSIONS
43+
templateFile.extension?.lowercase() in YAML_EXTENSIONS
4444

4545
private val YAML_EXTENSIONS = setOf("yaml", "yml")
4646
}

plugins/toolkit/jetbrains-core/src/software/aws/toolkits/jetbrains/services/dynamic/DynamicResourceStateChangedNotificationHandler.kt

+5-5
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,12 @@ class DynamicResourceStateChangedNotificationHandler(private val project: Projec
2525
message(
2626
"dynamic_resources.operation_status_notification_title",
2727
state.resourceIdentifier ?: state.resourceType,
28-
state.operation.name.toLowerCase()
28+
state.operation.name.lowercase()
2929
),
3030
message(
3131
"dynamic_resources.operation_status_success",
3232
state.resourceIdentifier ?: state.resourceType,
33-
state.operation.name.toLowerCase()
33+
state.operation.name.lowercase()
3434
),
3535
project
3636
)
@@ -48,7 +48,7 @@ class DynamicResourceStateChangedNotificationHandler(private val project: Projec
4848
message(
4949
"dynamic_resources.operation_status_failed_no_message",
5050
state.resourceIdentifier ?: state.resourceType,
51-
state.operation.name.toLowerCase()
51+
state.operation.name.lowercase()
5252
)
5353
)
5454
} else {
@@ -57,7 +57,7 @@ class DynamicResourceStateChangedNotificationHandler(private val project: Projec
5757
message(
5858
"dynamic_resources.operation_status_failed",
5959
state.resourceIdentifier ?: state.resourceType,
60-
state.operation.name.toLowerCase(),
60+
state.operation.name.lowercase(),
6161
state.message
6262
)
6363
)
@@ -79,7 +79,7 @@ class DynamicResourceStateChangedNotificationHandler(private val project: Projec
7979
message(
8080
"dynamic_resources.operation_status_notification_title",
8181
state.resourceIdentifier ?: state.resourceType,
82-
state.operation.name.toLowerCase()
82+
state.operation.name.lowercase()
8383
),
8484
errorMessage,
8585
project

plugins/toolkit/jetbrains-core/src/software/aws/toolkits/jetbrains/services/dynamic/DynamicResourcesUpdateManager.kt

+3-3
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ internal class DynamicResourceUpdateManager(private val project: Project) {
5050
message(
5151
"dynamic_resources.operation_status_notification_title",
5252
dynamicResourceIdentifier.resourceIdentifier,
53-
message("general.delete").toLowerCase()
53+
message("general.delete").lowercase()
5454
),
5555
project
5656
)
@@ -80,7 +80,7 @@ internal class DynamicResourceUpdateManager(private val project: Project) {
8080
message(
8181
"dynamic_resources.operation_status_notification_title",
8282
dynamicResourceIdentifier.resourceIdentifier,
83-
message("dynamic_resources.editor.submitResourceUpdateRequest_text").toLowerCase()
83+
message("dynamic_resources.editor.submitResourceUpdateRequest_text").lowercase()
8484
),
8585
project
8686
)
@@ -152,7 +152,7 @@ internal class DynamicResourceUpdateManager(private val project: Project) {
152152
message(
153153
"dynamic_resources.operation_status_notification_title",
154154
mutation.resourceIdentifier ?: mutation.resourceType,
155-
mutation.operation.name.toLowerCase()
155+
mutation.operation.name.lowercase()
156156
),
157157
project
158158
)

plugins/toolkit/jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/wizard/SchemaCodeGenUtils.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ class SchemaCodeGenUtils {
6464
if (builder.isNotEmpty()) {
6565
builder.append(IdentifierFormatter.PACKAGE_SEPARATOR)
6666
}
67-
builder.append(IdentifierFormatter.toValidIdentifier(segment.toLowerCase()))
67+
builder.append(IdentifierFormatter.toValidIdentifier(segment.lowercase()))
6868
return this
6969
}
7070

plugins/toolkit/jetbrains-core/src/software/aws/toolkits/jetbrains/services/s3/editor/S3TreeTable.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ class S3TreeTable(
160160
TreeSpeedSearch.installOn(
161161
tree,
162162
false,
163-
Function<TreePath, String> { obj ->
163+
Function<TreePath, String?> { obj ->
164164
val node = obj.lastPathComponent as DefaultMutableTreeNode
165165
val userObject = node.userObject as? S3TreeNode ?: return@Function null
166166
return@Function if (userObject !is S3TreeContinuationNode<*>) {

plugins/toolkit/jetbrains-core/src/software/aws/toolkits/jetbrains/ui/ResourceSelector.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ class ResourceSelector<T> private constructor(
160160
setEditable(false)
161161

162162
if (sortOnLoad) {
163-
model.replaceAll(value.sortedBy { it.toString().toLowerCase() })
163+
model.replaceAll(value.sortedBy { it.toString().lowercase() })
164164
} else {
165165
model.replaceAll(value.toList())
166166
}

plugins/toolkit/jetbrains-gateway/src/software/aws/toolkits/jetbrains/gateway/Workspace.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ data class Workspace(
3737
// TODO: probably need to model the @sha:[...] case better
3838
val (productCode, buildNumber) = ide.runtime().substringAfter("$JB_ECR_DOMAIN/").split(':', limit = 2)
3939

40-
productCode.substringBefore("@sha").toUpperCase() to buildNumber
40+
productCode.substringBefore("@sha").uppercase() to buildNumber
4141
}
4242

4343
val platformProduct = build?.let { IntelliJPlatformProduct.fromProductCode(it.first) }

plugins/toolkit/jetbrains-rider/build.gradle.kts

+1-1
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,7 @@ tasks.withType<DetektCreateBaselineTask>().configureEach {
388388
}
389389

390390
configurations.all {
391-
if (name.contains("detekt")) {
391+
if (name.contains("detekt") || name.contains("kotlinCompiler") || name.contains("rdGen")) {
392392
return@all
393393
}
394394

plugins/toolkit/jetbrains-ultimate/it/software/aws/toolkits/jetbrains/services/lambda/go/GoLocalRunConfigurationIntegrationTest.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ class GoLocalRunConfigurationIntegrationTest(private val runtime: LambdaRuntime)
240240
val executeLambda = executeRunConfigurationAndWait(runConfiguration, DefaultDebugExecutor.EXECUTOR_ID)
241241

242242
assertThat(executeLambda.exitCode).isEqualTo(0)
243-
assertThat(executeLambda.stdout).contains(input.toUpperCase())
243+
assertThat(executeLambda.stdout).contains(input.uppercase())
244244

245245
assertThat(debuggerIsHit.get()).isTrue
246246
}
@@ -281,7 +281,7 @@ class GoLocalRunConfigurationIntegrationTest(private val runtime: LambdaRuntime)
281281
runtime = runtime,
282282
mockCredentialsId = mockId,
283283
input = input,
284-
expectedOutput = input.toUpperCase()
284+
expectedOutput = input.uppercase()
285285
)
286286

287287
@Test

0 commit comments

Comments
 (0)