Skip to content

Commit e877783

Browse files
authored
Merge pull request #35 from lstreckeisen/improve-concurrency-handling
Improve concurrency handling
2 parents fcdc9ba + 78c6acd commit e877783

File tree

4 files changed

+102
-81
lines changed

4 files changed

+102
-81
lines changed

docs/existing_use_cases_1.cml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
UseCase UC1_CML_Editing {
2-
actor = "ContextMapper User"
2+
actor = "Context Mapper User"
33
interactions =
44
"recognise" a "SyntaxError",
55
"recognise" a "CmlKeyword",
@@ -16,21 +16,21 @@ UseCase UC1_CML_Editing {
1616
}
1717

1818
UseCase UC2_CML_QuickFix {
19-
actor = "ContextMapper User"
19+
actor = "Context Mapper User"
2020
interactions =
2121
"create" a "MissingBoundedContext",
2222
"split" a "UserStory"
2323
benefit = "I am able to fix small mistakes in my CML definition"
2424
}
2525

2626
UseCase UC3_CML_Generate_ContextMap {
27-
actor = "ContextMapper User"
27+
actor = "Context Mapper User"
2828
interactions = "generate" a "VisualContextMap"
2929
benefit = "I am able to create a context map diagram from my CML context map"
3030
}
3131

3232
UseCase UC4_CML_Generate_PlantUML {
33-
actor = "ContextMapper User"
33+
actor = "Context Mapper User"
3434
interactions = "generate" a "PlantUmlDiagram"
3535
benefit = "I am able to create visual representations of my CML definitions"
3636
}

docs/existing_use_cases_2.cml

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,23 @@
11
UseCase UC5_CML_Generate_SketchMiner {
2-
actor = "ContextMapper User"
2+
actor = "Context Mapper User"
33
interactions = "generate" a "SketchMinerDiagram"
44
benefit = "I am able to create a BPMN diagram from my process flow definitions"
55
}
66

77
UseCase UC6_CML_Generate_MDSLContract {
8-
actor = "ContextMapper User"
8+
actor = "Context Mapper User"
99
interactions = "generate" a "MDSLContract"
1010
benefit = "I am able to create an MDSL contract from my CML definitions"
1111
}
1212

13-
UseCase UC7_CML_ServiceCutter {
14-
actor = "ContextMapper User"
15-
interactions =
16-
"create" a "ServiceCutterConfiguration",
17-
"generate" a "ServiceCutterDiagram"
18-
benefit = "I am able to create ServiceCutter diagrams so I can visually see my service cuts"
19-
}
20-
21-
UseCase UC8_CML_FreeMarker {
22-
actor = "ContextMapper User"
13+
UseCase UC7_CML_FreeMarker {
14+
actor = "Context Mapper User"
2315
interactions = "apply" a "FreeMakerTemplate"
2416
benefit = "I am able to create my own files, e.g. Markdown, from my CML definitions"
2517
}
2618

27-
UseCase UC9_CML_Refactorings {
28-
actor = "ContextMapper User"
19+
UseCase UC8_CML_Refactorings {
20+
actor = "Context Mapper User"
2921
interactions =
3022
"split" an "Aggregate",
3123
"split" a "BoundedContext",
@@ -39,14 +31,14 @@ UseCase UC9_CML_Refactorings {
3931
benefit = "I am able to easily restructure my CML definitions"
4032
}
4133

42-
UseCase UC10_CML_Discovery {
43-
actor = "ContextMapper User"
44-
interactions = "discover" a "ContextMapperDefintion" for "my existing project"
34+
UseCase UC9_CML_Discovery {
35+
actor = "Context Mapper User"
36+
interactions = "discover" a "Context MapperDefintion" for "my existing project"
4537
benefit = "I am able to get CML defintions for my existing project"
4638
}
4739

48-
UseCase U11_CML_Validators {
49-
actor = "ContextMapper User"
40+
UseCase U10_CML_Validators {
41+
actor = "Context Mapper User"
5042
interactions = "validate" a "CMLFile"
5143
benefit = "I am able to validate my CML files for their semantic correctness"
5244
}

src/main/kotlin/org/contextmapper/intellij/actions/generators/ContextMapperGenerator.kt

Lines changed: 56 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,7 @@ package org.contextmapper.intellij.actions.generators
33
import com.intellij.openapi.project.Project
44
import com.redhat.devtools.lsp4ij.LanguageServerItem
55
import com.redhat.devtools.lsp4ij.LanguageServerManager
6-
import com.redhat.devtools.lsp4ij.commands.CommandExecutor
76
import com.redhat.devtools.lsp4ij.commands.LSPCommandContext
8-
import kotlinx.coroutines.CoroutineScope
9-
import kotlinx.coroutines.Dispatchers
10-
import kotlinx.coroutines.launch
117
import org.contextmapper.intellij.actions.LspCommandExecutor
128
import org.contextmapper.intellij.utils.CONTEXT_MAPPER_SERVER_ID
139
import org.eclipse.lsp4j.Command
@@ -26,70 +22,78 @@ class ContextMapperGenerator(
2622

2723
val context = LSPCommandContext(command, project)
2824

29-
val languageServerResult = getLanguageServer()
30-
if (languageServerResult.isFailure) {
31-
future.complete(Result.failure(languageServerResult.exceptionOrNull()!!))
32-
return future
33-
}
34-
context.preferredLanguageServerId = CONTEXT_MAPPER_SERVER_ID
35-
context.preferredLanguageServer = languageServerResult.getOrNull()
25+
languageServerManager.getLanguageServer(CONTEXT_MAPPER_SERVER_ID)
26+
.thenAcceptAsync { languageServer ->
27+
executeCommand(future, context, languageServer)
28+
}
29+
.exceptionally { throwable ->
30+
future.complete(
31+
Result.failure(
32+
ContextMapperGeneratorException(
33+
"Could not find language server instance.",
34+
throwable,
35+
),
36+
),
37+
)
38+
null
39+
}
3640

37-
val response = commandExecutor(context)
38-
CoroutineScope(Dispatchers.IO).launch {
39-
processResponse(response, future)
40-
}
4141
return future
4242
}
4343

44-
private fun processResponse(
45-
response: CommandExecutor.LSPCommandResponse,
46-
future: CompletableFuture<Result<GeneratorResult>>
44+
private fun executeCommand(
45+
future: CompletableFuture<Result<GeneratorResult>>,
46+
context: LSPCommandContext,
47+
languageServerItem: LanguageServerItem?
4748
) {
48-
val result = response.response
49-
50-
if (result != null) {
51-
try {
52-
val returnedValue = result.join()
49+
if (languageServerItem == null) {
50+
future.complete(
51+
Result.failure(
52+
ContextMapperGeneratorException(
53+
"Could not find language server instance.",
54+
),
55+
),
56+
)
57+
return
58+
}
59+
context.preferredLanguageServerId = CONTEXT_MAPPER_SERVER_ID
60+
context.preferredLanguageServer = languageServerItem
5361

54-
val generatedFilesResult = extractGeneratedFiles(returnedValue)
55-
if (generatedFilesResult.isFailure) {
56-
future.complete(Result.failure(generatedFilesResult.exceptionOrNull()!!))
57-
}
62+
val response = commandExecutor(context).response
63+
if (response == null) {
64+
future.complete(Result.failure(ContextMapperGeneratorException("Generator failed without error")))
65+
return
66+
}
5867

59-
val generatedFiles = generatedFilesResult.getOrNull()!!
60-
if (generatedFiles.isEmpty()) {
61-
future.complete(Result.success(GeneratorResult(listOf())))
62-
} else {
63-
future.complete(Result.success(GeneratorResult(generatedFiles)))
64-
}
65-
} catch (ex: Exception) {
68+
response.thenAcceptAsync { generatorResult -> processResponse(future, generatorResult) }
69+
.exceptionally { throwable ->
6670
future.complete(
6771
Result.failure(
6872
ContextMapperGeneratorException(
69-
"Generator failed with exception: ${ex.message}",
70-
ex,
73+
"Generator failed with exception: ${throwable.message}",
74+
throwable,
7175
),
7276
),
7377
)
78+
null
7479
}
75-
} else {
76-
future.complete(Result.failure(ContextMapperGeneratorException("Generator failed without error")))
77-
}
7880
}
7981

80-
private fun getLanguageServer(): Result<LanguageServerItem?> {
81-
return try {
82-
val languageServer =
83-
languageServerManager.getLanguageServer(CONTEXT_MAPPER_SERVER_ID)
84-
.join()
85-
Result.success(languageServer)
86-
} catch (ex: Exception) {
87-
Result.failure(
88-
ContextMapperGeneratorException(
89-
"Could not find language server instance.",
90-
ex,
91-
),
92-
)
82+
private fun processResponse(
83+
future: CompletableFuture<Result<GeneratorResult>>,
84+
generatorResult: Any
85+
) {
86+
val generatedFilesResult = extractGeneratedFiles(generatorResult)
87+
if (generatedFilesResult.isFailure) {
88+
future.complete(Result.failure(generatedFilesResult.exceptionOrNull()!!))
89+
return
90+
}
91+
92+
val generatedFiles = generatedFilesResult.getOrNull()!!
93+
if (generatedFiles.isEmpty()) {
94+
future.complete(Result.success(GeneratorResult(listOf())))
95+
} else {
96+
future.complete(Result.success(GeneratorResult(generatedFiles)))
9397
}
9498
}
9599

src/test/kotlin/org/contextmapper/intellij/actions/generators/ContextMapperGeneratorTest.kt

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.contextmapper.intellij.actions.generators
22

33
import com.intellij.openapi.project.Project
4+
import com.redhat.devtools.lsp4ij.LanguageServerItem
45
import com.redhat.devtools.lsp4ij.LanguageServerManager
56
import com.redhat.devtools.lsp4ij.commands.CommandExecutor
67
import com.redhat.devtools.lsp4ij.settings.UserDefinedLanguageServerSettings
@@ -17,6 +18,8 @@ import org.junit.jupiter.api.Assertions.assertNotNull
1718
import org.junit.jupiter.api.Assertions.assertTrue
1819
import org.junit.jupiter.api.BeforeEach
1920
import org.junit.jupiter.api.Test
21+
import java.util.concurrent.CompletableFuture
22+
import java.util.function.Consumer
2023

2124
class ContextMapperGeneratorTest() {
2225
private val command = Command("TestCommand", "cmd.id")
@@ -35,7 +38,11 @@ class ContextMapperGeneratorTest() {
3538
mockk(relaxed = true) {
3639
every { getLanguageServer(any()) } returns
3740
mockk {
38-
every { join() } returns mockk(relaxed = true)
41+
val actionSlot = slot<Consumer<LanguageServerItem?>>()
42+
every { thenAcceptAsync(capture(actionSlot)) } answers {
43+
actionSlot.captured.accept(mockk(relaxed = true))
44+
CompletableFuture.completedFuture(null)
45+
}
3946
}
4047
}
4148
project =
@@ -55,7 +62,11 @@ class ContextMapperGeneratorTest() {
5562
mockk<CommandExecutor.LSPCommandResponse> {
5663
every { response } returns
5764
mockk {
58-
every { join() } returns listOf("diagram.puml")
65+
val actionSlot = slot<Consumer<Any>>()
66+
every { thenAcceptAsync(capture(actionSlot)) } answers {
67+
actionSlot.captured.accept(listOf("diagram.puml"))
68+
CompletableFuture.completedFuture(null)
69+
}
5970
}
6071
}
6172

@@ -74,7 +85,11 @@ class ContextMapperGeneratorTest() {
7485
mockk<CommandExecutor.LSPCommandResponse> {
7586
every { response } returns
7687
mockk {
77-
every { join() } returns listOf<String>()
88+
val actionSlot = slot<Consumer<Any>>()
89+
every { thenAcceptAsync(capture(actionSlot)) } answers {
90+
actionSlot.captured.accept(listOf<String>())
91+
CompletableFuture.completedFuture(null)
92+
}
7893
}
7994
}
8095

@@ -93,7 +108,10 @@ class ContextMapperGeneratorTest() {
93108
mockk<CommandExecutor.LSPCommandResponse> {
94109
every { response } returns
95110
mockk {
96-
every { join() } throws Exception("Test Exception")
111+
val actionSlot = slot<Consumer<Any>>()
112+
every { thenAcceptAsync(capture(actionSlot)) } answers {
113+
CompletableFuture.failedFuture(Exception("Test Exception"))
114+
}
97115
}
98116
}
99117

@@ -128,7 +146,11 @@ class ContextMapperGeneratorTest() {
128146
mockk<CommandExecutor.LSPCommandResponse> {
129147
every { response } returns
130148
mockk {
131-
every { join() } returns mockk<ResponseErrorException>()
149+
val actionSlot = slot<Consumer<Any>>()
150+
every { thenAcceptAsync(capture(actionSlot)) } answers {
151+
actionSlot.captured.accept(mockk<ResponseErrorException>())
152+
CompletableFuture.completedFuture(null)
153+
}
132154
}
133155
}
134156

@@ -145,7 +167,10 @@ class ContextMapperGeneratorTest() {
145167
fun testGeneratorWithMissingLanguageServer() {
146168
every { languageServerManager.getLanguageServer(eq(CONTEXT_MAPPER_SERVER_ID)) } returns
147169
mockk {
148-
every { join() } throws RuntimeException()
170+
val actionSlot = slot<Consumer<LanguageServerItem?>>()
171+
every { thenAcceptAsync(capture(actionSlot)) } answers {
172+
CompletableFuture.failedFuture(RuntimeException())
173+
}
149174
}
150175

151176
val result =

0 commit comments

Comments
 (0)