Skip to content

Commit 90b56e3

Browse files
authored
refactor(mps-sync-plugin-lib): ApplicationScope -> ProjectScope refactoring (#112)
* refactor(mps-sync-plugin): remove unused listeners (MODELIX-952) * refactor(mps-sync-plugin): make ModelSyncService project scoped instead of application scoped * docs(mps-sync-plugin-lib): experimental api linting * refactor(mps-sync-plugin): create a new GUI for each opened project * refactor(mps-sync-plugin-lib): make some objects project-scoped (WIP) BindingsRegistry, BranchRegistry, InjectableNotifierWrapper, BindingsComboBoxRefresher * refactor(mps-sync-plugin-lib): make SyncQueue project-scoped (WIP) * refactor(mps-sync-plugin-lib): refactor application-scoped objects to project-scoped services (WIP) * refactor(mps-sync-plugin-lib): rename InjectableNotifierWrapper for a more descriptive name * refactor(mps-sync-plugin-lib): move clases to more fitting packages * refactor(mps-sync-plugin-lib): implement ServiceLocator to unify service injection (WIP) * refactor(mps-sync-plugin): refactor plugin.xml Remove those Services and Actions that are registered via annotations or the intelliJ API. For simplicity reasons, start the BindingsComBoBoxRefresher directly in the ModelSyncGui. * fix(mps-sync-plugin): avoid registering the Modelix Actions twice if we have more MPS windows open * fix(mps-sync-plugin-lib): listen to ProjectManagerListener instead of ApplicationLifecycleListener To get notified about when a Project is about to be disposed, before the actual disposal is happening. * fix(mps-sync-plugin-lib): implement MPSLanguageRepository hashCode and equals by delegate Otherwise it will cause troubles when more than once MPS instaces are running and register their ProjectRepositories. * refactor(mps-sync-plugin): remove unncessary hashCode and equals from object * fix(mps-sync-plugin): replace requireNotNull by checkNotNull * fix(mps-sync-plugin): make ModelixActionGroup singleton * refactor(mps-sync-plugin-lib): make MPS Language Repository Provider application-level And use it once, instead of registering the MPSLanguageRepository for each project. At the end of the day, all ProjectRepositories use the same MPSModuleRepository internally. Therefore, it's enough to register this repository only once.
1 parent bbd3fbe commit 90b56e3

File tree

61 files changed

+1028
-854
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+1028
-854
lines changed

mps-sync-plugin-lib/src/main/kotlin/org/modelix/mps/sync/IRebindModulesSyncService.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ import java.io.IOException
1111
* A sub-interface of [ISyncService] that can be implemented by all users of the modelix sync lib, so that they can
1212
* extend the default implementation in [SyncServiceImpl], e.g. adding extra logging or informing the users by errors.
1313
*/
14-
@UnstableModelixFeature(reason = "The new modelix MPS plugin is under construction", intendedFinalization = "2024.1")
14+
@UnstableModelixFeature(
15+
reason = "The new modelix MPS plugin is under construction",
16+
intendedFinalization = "This feature is finalized when the new sync plugin is ready for release.",
17+
)
1518
interface IRebindModulesSyncService {
1619

1720
/**

mps-sync-plugin-lib/src/main/kotlin/org/modelix/mps/sync/ISyncService.kt

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package org.modelix.mps.sync
22

3-
import com.intellij.openapi.project.Project
43
import jetbrains.mps.extapi.model.SModelBase
54
import jetbrains.mps.project.AbstractModule
65
import org.modelix.kotlin.utils.UnstableModelixFeature
@@ -13,7 +12,7 @@ import java.net.URL
1312
import java.util.concurrent.CompletableFuture
1413

1514
@UnstableModelixFeature(reason = "The new modelix MPS plugin is under construction", intendedFinalization = "This feature is finalized when the new sync plugin is ready for release.")
16-
interface ISyncService : AutoCloseable, IRebindModulesSyncService {
15+
interface ISyncService : IRebindModulesSyncService {
1716

1817
override fun connectModelServer(serverURL: String, jwt: String?) = connectModelServer(URL(serverURL), jwt)
1918

@@ -37,8 +36,6 @@ interface ISyncService : AutoCloseable, IRebindModulesSyncService {
3736
fun bindModuleFromMps(module: AbstractModule, branch: IBranch): Iterable<IBinding>
3837

3938
fun bindModelFromMps(model: SModelBase, branch: IBranch): IBinding
40-
41-
fun setActiveProject(project: Project)
4239
}
4340

4441
@UnstableModelixFeature(reason = "The new modelix MPS plugin is under construction", intendedFinalization = "This feature is finalized when the new sync plugin is ready for release.")

mps-sync-plugin-lib/src/main/kotlin/org/modelix/mps/sync/SyncServiceImpl.kt

Lines changed: 47 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
11
package org.modelix.mps.sync
22

3-
import com.intellij.openapi.project.Project
43
import jetbrains.mps.extapi.model.SModelBase
54
import jetbrains.mps.project.AbstractModule
6-
import jetbrains.mps.project.MPSProject
75
import kotlinx.coroutines.CoroutineScope
86
import kotlinx.coroutines.Dispatchers
97
import kotlinx.coroutines.launch
108
import kotlinx.coroutines.runBlocking
119
import mu.KotlinLogging
10+
import org.jetbrains.mps.openapi.module.SRepository
1211
import org.modelix.kotlin.utils.UnstableModelixFeature
1312
import org.modelix.model.api.IBranch
14-
import org.modelix.model.api.ILanguageRepository
1513
import org.modelix.model.client2.ModelClientV2
1614
import org.modelix.model.client2.ReplicatedModel
1715
import org.modelix.model.lazy.BranchReference
@@ -21,17 +19,14 @@ import org.modelix.mps.sync.bindings.BindingsRegistry
2119
import org.modelix.mps.sync.bindings.EmptyBinding
2220
import org.modelix.mps.sync.bindings.ModelBinding
2321
import org.modelix.mps.sync.bindings.ModuleBinding
24-
import org.modelix.mps.sync.modelix.BranchRegistry
25-
import org.modelix.mps.sync.modelix.ITreeTraversal
26-
import org.modelix.mps.sync.modelix.ReplicatedModelInitContext
27-
import org.modelix.mps.sync.mps.ActiveMpsProjectInjector
28-
import org.modelix.mps.sync.mps.notifications.INotifier
29-
import org.modelix.mps.sync.mps.notifications.InjectableNotifierWrapper
22+
import org.modelix.mps.sync.modelix.branch.BranchRegistry
23+
import org.modelix.mps.sync.modelix.branch.ReplicatedModelInitContext
24+
import org.modelix.mps.sync.modelix.tree.ITreeTraversal
25+
import org.modelix.mps.sync.mps.notifications.WrappedNotifier
26+
import org.modelix.mps.sync.mps.services.InjectableService
27+
import org.modelix.mps.sync.mps.services.ServiceLocator
3028
import org.modelix.mps.sync.mps.util.ModuleIdWithName
3129
import org.modelix.mps.sync.mps.util.isDescriptorModel
32-
import org.modelix.mps.sync.tasks.FuturesWaitQueue
33-
import org.modelix.mps.sync.tasks.SyncQueue
34-
import org.modelix.mps.sync.transformation.cache.MpsToModelixMap
3530
import org.modelix.mps.sync.transformation.cache.MpsToModelixMapInitializerVisitor
3631
import org.modelix.mps.sync.transformation.modelixToMps.initial.ITreeToSTreeTransformer
3732
import org.modelix.mps.sync.transformation.mpsToModelix.initial.ModelSynchronizer
@@ -43,21 +38,32 @@ import java.net.URL
4338
reason = "The new modelix MPS plugin is under construction",
4439
intendedFinalization = "This feature is finalized when the new sync plugin is ready for release.",
4540
)
46-
class SyncServiceImpl(userNotifier: INotifier) : ISyncService {
41+
class SyncServiceImpl : ISyncService, InjectableService {
4742

4843
private val logger = KotlinLogging.logger {}
49-
private val mpsProjectInjector = ActiveMpsProjectInjector
50-
private val notifierInjector = InjectableNotifierWrapper
5144

5245
private val networkDispatcher = Dispatchers.IO // rather IO-intensive tasks
5346
private val cpuDispatcher = Dispatchers.Default // rather CPU-intensive tasks
5447

55-
init {
56-
notifierInjector.notifier = userNotifier
48+
private val notifier: WrappedNotifier
49+
get() = serviceLocator.wrappedNotifier
5750

58-
logger.debug { "ModelixSyncPlugin: Registering built-in languages" }
59-
// just a dummy call, the initializer of ILanguageRegistry takes care of the rest...
60-
ILanguageRepository.default.javaClass
51+
private val bindingsRegistry: BindingsRegistry
52+
get() = serviceLocator.bindingsRegistry
53+
54+
private val branchRegistry: BranchRegistry
55+
get() = serviceLocator.branchRegistry
56+
57+
private val mpsRepository: SRepository
58+
get() = serviceLocator.mpsProject.repository
59+
60+
private val languageRepository: MPSLanguageRepository
61+
get() = serviceLocator.languageRepository
62+
63+
private lateinit var serviceLocator: ServiceLocator
64+
65+
override fun initService(serviceLocator: ServiceLocator) {
66+
this.serviceLocator = serviceLocator
6167
}
6268

6369
@Throws(IOException::class)
@@ -78,19 +84,19 @@ class SyncServiceImpl(userNotifier: INotifier) : ISyncService {
7884
logger.info { "Disconnected from ${client.baseUrl}" }
7985

8086
logger.info { "Deactivating bindings and disposing cloned branch." }
81-
BindingsRegistry.deactivateBindings(waitForCompletion = true)
82-
BranchRegistry.close()
87+
bindingsRegistry.deactivateBindings(waitForCompletion = true)
88+
branchRegistry.dispose()
8389
logger.info { "Bindings are deactivated and branch is disposed." }
8490
}
8591

8692
override fun disconnectFromBranch(branch: IBranch, branchName: String) {
8793
logger.info { "Deactivating bindings and disposing cloned branch $branchName." }
88-
BindingsRegistry.deactivateBindings(waitForCompletion = true)
89-
BranchRegistry.unsetBranch(branch)
94+
bindingsRegistry.deactivateBindings(waitForCompletion = true)
95+
branchRegistry.unsetBranch(branch)
9096
logger.info { "Bindings are deactivated and branch ($branchName) is disposed." }
9197
}
9298

93-
override fun getActiveBranch(): IBranch? = BranchRegistry.branch
99+
override fun getActiveBranch(): IBranch? = branchRegistry.getBranch()
94100

95101
/**
96102
* WARNING: this is a long-running blocking call.
@@ -112,13 +118,10 @@ class SyncServiceImpl(userNotifier: INotifier) : ISyncService {
112118
initialVersion: CLVersion? = null,
113119
): ReplicatedModel {
114120
logger.info { "Connecting to branch $branchReference with initial version $initialVersion (null = latest version)." }
115-
val targetProject = mpsProjectInjector.activeMpsProject!!
116-
val languageRepository = registerLanguages(targetProject)
117-
val model = BranchRegistry.setReplicatedModel(
121+
val model = branchRegistry.setReplicatedModel(
118122
client,
119123
branchReference,
120124
languageRepository,
121-
targetProject,
122125
ReplicatedModelInitContext(CoroutineScope(networkDispatcher), initialVersion),
123126
)
124127
logger.info { "Connected to branch $branchReference with initial version $initialVersion" }
@@ -137,15 +140,11 @@ class SyncServiceImpl(userNotifier: INotifier) : ISyncService {
137140
): Iterable<IBinding> {
138141
val moduleName = module.name
139142
logger.info { "Binding Module '$moduleName' from the server ($branchReference)." }
140-
141-
val targetProject = mpsProjectInjector.activeMpsProject!!
142-
val languageRepository = registerLanguages(targetProject)
143-
144143
// fetch replicated model and branch content
145144
val branch = connectToBranch(client, branchReference)
146145

147146
// transform the modules and models
148-
val bindings = ITreeToSTreeTransformer(branch, languageRepository).transform(module.id)
147+
val bindings = ITreeToSTreeTransformer(branch, languageRepository, serviceLocator).transform(module.id)
149148

150149
notifyUserAboutBindings(bindings, moduleName)
151150

@@ -164,7 +163,7 @@ class SyncServiceImpl(userNotifier: INotifier) : ISyncService {
164163
if (!modules.iterator().hasNext()) {
165164
val message =
166165
"The list is restorable Modules is empty, therefore no Module- or Model Binding is restored for them."
167-
notifierInjector.notifyAndLogWarning(message, logger)
166+
notifier.notifyAndLogWarning(message, logger)
168167
return null
169168
}
170169

@@ -174,27 +173,25 @@ class SyncServiceImpl(userNotifier: INotifier) : ISyncService {
174173
// recreate the mapping between the local MPS elements and the modelix Nodes
175174
val branch = replicatedModel.getBranch()
176175
runBlocking(cpuDispatcher) {
177-
val repository = ActiveMpsProjectInjector.activeMpsProject?.repository
178-
requireNotNull(repository) { "SRepository must exist, otherwise we cannot restore the Modules." }
179-
val mappingRecreator = MpsToModelixMapInitializerVisitor(MpsToModelixMap, repository, branch)
176+
val mappingRecreator = MpsToModelixMapInitializerVisitor(serviceLocator.nodeMap, mpsRepository, branch)
180177
val treeTraversal = ITreeTraversal(branch)
181178
treeTraversal.visit(mappingRecreator)
182179
}
183180

184181
// register the bindings
185182
val bindings = mutableListOf<IBinding>()
186183
modules.forEach { module ->
187-
val moduleBinding = ModuleBinding(module, branch)
188-
BindingsRegistry.addModuleBinding(moduleBinding)
184+
val moduleBinding = ModuleBinding(module, branch, serviceLocator)
185+
bindingsRegistry.addModuleBinding(moduleBinding)
189186

190187
module.models.forEach { model ->
191188
require(model is SModelBase) { "Model ($model) is not an SModelBase." }
192189
val binding = if (model.isDescriptorModel()) {
193190
// We do not track changes in descriptor models. See ModelTransformer.isDescriptorModel()
194191
EmptyBinding()
195192
} else {
196-
val modelBinding = ModelBinding(model, branch)
197-
BindingsRegistry.addModelBinding(modelBinding)
193+
val modelBinding = ModelBinding(model, branch, serviceLocator)
194+
bindingsRegistry.addModelBinding(modelBinding)
198195
modelBinding
199196
}
200197
bindings.add(binding)
@@ -218,7 +215,9 @@ class SyncServiceImpl(userNotifier: INotifier) : ISyncService {
218215

219216
// warning: blocking call
220217
@Suppress("UNCHECKED_CAST")
221-
val bindings = ModuleSynchronizer(branch).addModule(module, true).getResult().get() as Iterable<IBinding>
218+
val bindings = ModuleSynchronizer(branch, serviceLocator)
219+
.addModule(module, true)
220+
.getResult().get() as Iterable<IBinding>
222221

223222
notifyUserAboutBindings(bindings, module.moduleName)
224223

@@ -229,11 +228,11 @@ class SyncServiceImpl(userNotifier: INotifier) : ISyncService {
229228
val hasAnyBinding = bindings.iterator().hasNext()
230229
if (hasAnyBinding) {
231230
val message = "Module- and Model Bindings for Module '$moduleName' are created."
232-
notifierInjector.notifyAndLogInfo(message, logger)
231+
notifier.notifyAndLogInfo(message, logger)
233232
} else {
234233
val message =
235234
"No Module- or Model Binding is created for Module '$moduleName'. This might be due to an error."
236-
notifierInjector.notifyAndLogWarning(message, logger)
235+
notifier.notifyAndLogWarning(message, logger)
237236
}
238237
}
239238

@@ -243,43 +242,20 @@ class SyncServiceImpl(userNotifier: INotifier) : ISyncService {
243242
override fun bindModelFromMps(model: SModelBase, branch: IBranch): IBinding {
244243
logger.info { "Binding Model '${model.name}' to the server." }
245244

246-
val synchronizer = ModelSynchronizer(branch)
245+
val synchronizer = ModelSynchronizer(branch, serviceLocator = serviceLocator)
247246
// synchronize model. Warning: blocking call
248247
val binding = synchronizer.addModel(model).getResult().get() as IBinding
249248
// wait until the model imports are synced. Warning: blocking call
250249
synchronizer.resolveModelImportsInTask().getResult().get()
251250

252251
if (binding !is EmptyBinding) {
253252
val message = "Model Binding for '${model.name}' is created."
254-
notifierInjector.notifyAndLogInfo(message, logger)
253+
notifier.notifyAndLogInfo(message, logger)
255254
} else {
256255
val message = "No Model Binding is created for '${model.name}'. This might be due to an error."
257-
notifierInjector.notifyAndLogWarning(message, logger)
256+
notifier.notifyAndLogWarning(message, logger)
258257
}
259258

260259
return binding
261260
}
262-
263-
override fun setActiveProject(project: Project) {
264-
mpsProjectInjector.setActiveProject(project)
265-
}
266-
267-
override fun close() {
268-
logger.debug { "Closing SyncServiceImpl." }
269-
// dispose task and wait queues
270-
SyncQueue.close()
271-
FuturesWaitQueue.close()
272-
// dispose replicated model
273-
BranchRegistry.close()
274-
// dispose all bindings
275-
BindingsRegistry.deactivateBindings()
276-
logger.debug { "SyncServiceImpl is closed." }
277-
}
278-
279-
private fun registerLanguages(project: MPSProject): MPSLanguageRepository {
280-
val repository = project.repository
281-
val mpsLanguageRepo = MPSLanguageRepository(repository)
282-
ILanguageRepository.register(mpsLanguageRepo)
283-
return mpsLanguageRepo
284-
}
285261
}

mps-sync-plugin-lib/src/main/kotlin/org/modelix/mps/sync/bindings/BindingsRegistry.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import org.jetbrains.mps.openapi.model.SModelId
2222
import org.jetbrains.mps.openapi.module.SModule
2323
import org.modelix.kotlin.utils.UnstableModelixFeature
2424
import org.modelix.mps.sync.IBinding
25+
import org.modelix.mps.sync.mps.services.InjectableService
2526
import org.modelix.mps.sync.util.synchronizedLinkedHashSet
2627
import org.modelix.mps.sync.util.synchronizedMap
2728
import java.util.concurrent.CompletableFuture
@@ -30,7 +31,7 @@ import java.util.concurrent.LinkedBlockingQueue
3031
import java.util.function.BiConsumer
3132

3233
@UnstableModelixFeature(reason = "The new modelix MPS plugin is under construction", intendedFinalization = "This feature is finalized when the new sync plugin is ready for release.")
33-
object BindingsRegistry {
34+
class BindingsRegistry : InjectableService {
3435

3536
private val modelBindingsByModule = synchronizedMap<SModule, MutableSet<ModelBinding>>()
3637
private val moduleBindings = synchronizedLinkedHashSet<ModuleBinding>()
@@ -119,6 +120,10 @@ object BindingsRegistry {
119120

120121
private fun bindingRemoved(binding: IBinding) =
121122
changedBindings.put(BindingState(binding, BindingLifecycleState.REMOVE))
123+
124+
override fun dispose() {
125+
deactivateBindings()
126+
}
122127
}
123128

124129
@UnstableModelixFeature(reason = "The new modelix MPS plugin is under construction", intendedFinalization = "This feature is finalized when the new sync plugin is ready for release.")

mps-sync-plugin-lib/src/main/kotlin/org/modelix/mps/sync/bindings/ModelBinding.kt

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,27 +22,30 @@ import mu.KotlinLogging
2222
import org.modelix.kotlin.utils.UnstableModelixFeature
2323
import org.modelix.model.api.IBranch
2424
import org.modelix.mps.sync.IBinding
25-
import org.modelix.mps.sync.mps.notifications.InjectableNotifierWrapper
25+
import org.modelix.mps.sync.mps.services.ServiceLocator
2626
import org.modelix.mps.sync.tasks.SyncDirection
2727
import org.modelix.mps.sync.tasks.SyncLock
28-
import org.modelix.mps.sync.tasks.SyncQueue
29-
import org.modelix.mps.sync.transformation.cache.MpsToModelixMap
3028
import org.modelix.mps.sync.transformation.mpsToModelix.incremental.ModelChangeListener
3129
import org.modelix.mps.sync.transformation.mpsToModelix.incremental.NodeChangeListener
3230
import org.modelix.mps.sync.util.completeWithDefault
3331
import java.util.concurrent.CompletableFuture
3432

35-
@UnstableModelixFeature(reason = "The new modelix MPS plugin is under construction", intendedFinalization = "This feature is finalized when the new sync plugin is ready for release.")
36-
class ModelBinding(val model: SModelBase, branch: IBranch) : IBinding {
33+
@UnstableModelixFeature(
34+
reason = "The new modelix MPS plugin is under construction",
35+
intendedFinalization = "This feature is finalized when the new sync plugin is ready for release.",
36+
)
37+
class ModelBinding(val model: SModelBase, branch: IBranch, serviceLocator: ServiceLocator) : IBinding {
3738

3839
private val logger = KotlinLogging.logger {}
39-
private val nodeMap = MpsToModelixMap
40-
private val syncQueue = SyncQueue
41-
private val bindingsRegistry = BindingsRegistry
42-
private val notifierInjector = InjectableNotifierWrapper
4340

44-
private val modelChangeListener = ModelChangeListener(branch, this)
45-
private val nodeChangeListener = NodeChangeListener(branch)
41+
private val nodeMap = serviceLocator.nodeMap
42+
private val syncQueue = serviceLocator.syncQueue
43+
44+
private val bindingsRegistry = serviceLocator.bindingsRegistry
45+
private val notifier = serviceLocator.wrappedNotifier
46+
47+
private val modelChangeListener = ModelChangeListener(this, branch, serviceLocator)
48+
private val nodeChangeListener = NodeChangeListener(branch, serviceLocator)
4649

4750
@Volatile
4851
private var isDisposed = false
@@ -68,7 +71,7 @@ class ModelBinding(val model: SModelBase, branch: IBranch) : IBinding {
6871
bindingsRegistry.bindingActivated(this)
6972

7073
val message = "${name()} is activated."
71-
notifierInjector.notifyAndLogInfo(message, logger)
74+
notifier.notifyAndLogInfo(message, logger)
7275

7376
callback?.run()
7477
}
@@ -142,7 +145,7 @@ class ModelBinding(val model: SModelBase, branch: IBranch) : IBinding {
142145
""
143146
}
144147
}."
145-
notifierInjector.notifyAndLogInfo(message, logger)
148+
notifier.notifyAndLogInfo(message, logger)
146149

147150
callback?.run()
148151
}.getResult()

0 commit comments

Comments
 (0)