6039df711059e0dedc5b3c40cc26d08824c60483
[idea/community.git] / platform / service-container / src / com / intellij / serviceContainer / PlatformComponentManagerImpl.kt
1 // Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package com.intellij.serviceContainer
3
4 import com.intellij.diagnostic.*
5 import com.intellij.ide.plugins.*
6 import com.intellij.ide.plugins.cl.PluginClassLoader
7 import com.intellij.openapi.Disposable
8 import com.intellij.openapi.application.Application
9 import com.intellij.openapi.application.ApplicationManager
10 import com.intellij.openapi.components.*
11 import com.intellij.openapi.components.impl.ComponentManagerImpl
12 import com.intellij.openapi.components.impl.stores.IComponentStore
13 import com.intellij.openapi.diagnostic.ControlFlowException
14 import com.intellij.openapi.diagnostic.logger
15 import com.intellij.openapi.extensions.*
16 import com.intellij.openapi.extensions.impl.ExtensionsAreaImpl
17 import com.intellij.openapi.progress.ProcessCanceledException
18 import com.intellij.openapi.progress.ProgressIndicator
19 import com.intellij.openapi.progress.ProgressIndicatorProvider
20 import com.intellij.openapi.progress.ProgressManager
21 import com.intellij.openapi.util.Disposer
22 import com.intellij.util.IncorrectOperationException
23 import com.intellij.util.SmartList
24 import com.intellij.util.containers.ContainerUtil
25 import com.intellij.util.io.storage.HeavyProcessLatch
26 import com.intellij.util.messages.*
27 import com.intellij.util.messages.impl.MessageBusImpl
28 import com.intellij.util.pico.DefaultPicoContainer
29 import org.jetbrains.annotations.ApiStatus.Internal
30 import org.jetbrains.annotations.TestOnly
31 import java.lang.reflect.Constructor
32 import java.lang.reflect.InvocationTargetException
33 import java.lang.reflect.Modifier
34 import java.util.*
35 import java.util.concurrent.ConcurrentMap
36
37 abstract class PlatformComponentManagerImpl @JvmOverloads constructor(internal val parent: ComponentManager?, setExtensionsRootArea: Boolean = parent == null) : ComponentManagerImpl(parent), LazyListenerCreator {
38   companion object {
39     private val LOG = logger<PlatformComponentManagerImpl>()
40
41     private val constructorParameterResolver = PlatformConstructorParameterResolver(isExtensionSupported = false)
42     private val heavyConstructorParameterResolver = PlatformConstructorParameterResolver(isExtensionSupported = true)
43
44     private class PlatformConstructorParameterResolver(private val isExtensionSupported: Boolean) : ConstructorParameterResolver() {
45       override fun isResolvable(componentManager: PlatformComponentManagerImpl, requestorKey: Any, expectedType: Class<*>): Boolean {
46         if (isLightService(expectedType) || super.isResolvable(componentManager, requestorKey, expectedType)) {
47           return true
48         }
49         return isExtensionSupported && componentManager.extensionArea.findExtensionByClass(expectedType) != null
50       }
51
52       override fun resolveInstance(componentManager: PlatformComponentManagerImpl, requestorKey: Any, expectedType: Class<*>): Any? {
53         if (isLightService(expectedType)) {
54           return componentManager.getLightService(componentManager.lightServices!!, expectedType, true)
55         }
56
57         val result = super.resolveInstance(componentManager, requestorKey, expectedType)
58         if (result == null && isExtensionSupported) {
59           val extension = componentManager.extensionArea.findExtensionByClass(expectedType)
60           if (extension != null) {
61             LOG.warn("Do not use constructor injection to get extension instance (requestorKey=$requestorKey, extensionClass=${expectedType.name})")
62           }
63           return extension
64         }
65         return result
66       }
67     }
68
69     @JvmStatic
70     protected val fakeCorePluginDescriptor = object : PluginDescriptor {
71       override fun getPluginClassLoader() = null
72
73       override fun getPluginId() = PluginId.getId(PluginManagerCore.CORE_PLUGIN_ID)
74     }
75   }
76
77   @Suppress("LeakingThis")
78   private val extensionArea = ExtensionsAreaImpl(this)
79
80   private var handlingInitComponentError = false
81
82   var componentCreated = false
83     private set
84
85   private val lightServices: ConcurrentMap<Class<*>, Any>? = when {
86     parent == null || parent.picoContainer.parent == null -> ContainerUtil.newConcurrentMap()
87     else -> null
88   }
89
90   protected open val componentStore: IComponentStore
91     get() = getService(IComponentStore::class.java)
92
93   init {
94     if (setExtensionsRootArea) {
95       Extensions.setRootArea(extensionArea)
96     }
97   }
98
99   final override fun getExtensionArea(): ExtensionsAreaImpl = extensionArea
100
101   @Internal
102   open fun registerComponents(plugins: List<IdeaPluginDescriptor>) {
103     @Suppress("UNCHECKED_CAST")
104     plugins as List<IdeaPluginDescriptorImpl>
105     val activityNamePrefix = activityNamePrefix()
106     val parallelActivity = if (activityNamePrefix == null) null else ParallelActivity.PREPARE_APP_INIT
107     parallelActivity.run("${activityNamePrefix}service and ep registration") {
108       // register services before registering extensions because plugins can access services in their
109       // extensions which can be invoked right away if the plugin is loaded dynamically
110       for (plugin in plugins) {
111         val containerDescriptor = getContainerDescriptor(plugin)
112         registerServices(containerDescriptor.services, plugin)
113
114         containerDescriptor.extensionPoints?.let {
115           extensionArea.registerExtensionPoints(plugin, it, this)
116         }
117       }
118     }
119
120     parallelActivity.run("${activityNamePrefix}extension registration") {
121       val notifyListeners = LoadingPhase.isStartupComplete()
122       for (descriptor in plugins) {
123         descriptor.registerExtensions(extensionArea, this, notifyListeners)
124       }
125     }
126
127     val app = getApplication()
128     val headless = app == null || app.isHeadlessEnvironment
129
130     var map: ConcurrentMap<String, MutableList<ListenerDescriptor>>? = null
131     val isHeadlessMode = app?.isHeadlessEnvironment == true
132     val isUnitTestMode = app?.isUnitTestMode == true
133
134     var componentConfigCount = 0
135     for (plugin in plugins) {
136       val containerDescriptor = getContainerDescriptor(plugin)
137
138       for (config in containerDescriptor.components) {
139         if (!config.prepareClasses(headless) || !isComponentSuitable(config)) {
140           continue
141         }
142
143         try {
144           registerComponent(config, plugin)
145           componentConfigCount++
146         }
147         catch (e: Throwable) {
148           handleInitComponentError(e, null, plugin.pluginId)
149         }
150       }
151
152       val listeners = containerDescriptor.listeners
153       if (listeners.isNotEmpty()) {
154         if (map == null) {
155           map = ContainerUtil.newConcurrentMap()
156         }
157
158         for (listener in listeners) {
159           if ((isUnitTestMode && !listener.activeInTestMode) || (isHeadlessMode && !listener.activeInHeadlessMode)) {
160             continue
161           }
162
163           map.getOrPut(listener.topicClassName) { SmartList() }.add(listener)
164         }
165       }
166     }
167
168     if (myComponentConfigCount <= 0) {
169       myComponentConfigCount = componentConfigCount
170     }
171
172     // app - phase must be set before getMessageBus()
173     if (picoContainer.parent == null && !LoadingPhase.PROJECT_OPENED.isComplete /* loading plugin on the fly */) {
174       LoadingPhase.setCurrentPhase(LoadingPhase.COMPONENT_REGISTERED)
175     }
176
177     // todo support lazy listeners for dynamically loaded plugins
178     // ensure that messageBus is created, regardless of lazy listeners map state
179     val messageBus = messageBus as MessageBusImpl
180     if (map != null) {
181       messageBus.setLazyListeners(map)
182     }
183   }
184
185   protected fun createComponents(indicator: ProgressIndicator?) {
186     LOG.assertTrue(!componentCreated)
187
188     if (indicator != null) {
189       indicator.isIndeterminate = false
190     }
191
192     val activityNamePrefix = activityNamePrefix()
193     val activity = when (activityNamePrefix) {
194       null -> null
195       else -> StartUpMeasurer.start("$activityNamePrefix${StartUpMeasurer.Phases.CREATE_COMPONENTS_SUFFIX}")
196     }
197
198     for (componentAdapter in myPicoContainer.componentAdapters) {
199       if (componentAdapter is MyComponentAdapter) {
200         componentAdapter.getInstance<Any>(this, indicator = indicator)
201       }
202     }
203
204     activity?.end()
205
206     componentCreated = true
207   }
208
209   @TestOnly
210   fun registerComponentImplementation(componentKey: Class<*>, componentImplementation: Class<*>, shouldBeRegistered: Boolean) {
211     val picoContainer = picoContainer
212     val adapter = picoContainer.unregisterComponent(componentKey) as MyComponentAdapter?
213     if (shouldBeRegistered) {
214       LOG.assertTrue(adapter != null)
215     }
216     picoContainer.registerComponent(MyComponentAdapter(componentKey, componentImplementation.name, DefaultPluginDescriptor("test registerComponentImplementation"), this, componentImplementation))
217   }
218
219   @TestOnly
220   fun <T : Any> registerComponentInstance(componentKey: Class<T>, componentImplementation: T, parentDisposable: Disposable?): T? {
221     val adapter = myPicoContainer.getComponentAdapter(componentKey) as MyComponentAdapter
222     return adapter.replaceInstance(componentImplementation, parentDisposable)
223   }
224
225   private fun registerComponent(config: ComponentConfig, pluginDescriptor: PluginDescriptor) {
226     val interfaceClass = Class.forName(config.interfaceClass, true, pluginDescriptor.pluginClassLoader)
227
228     if (config.options != null && java.lang.Boolean.parseBoolean(config.options!!.get("overrides"))) {
229       myPicoContainer.unregisterComponent(interfaceClass) ?: throw PluginException("$config does not override anything", pluginDescriptor.pluginId)
230     }
231
232     val implementationClass = when {
233       config.interfaceClass == config.implementationClass -> interfaceClass.name
234       else -> config.implementationClass
235     }
236
237     // implementationClass == null means we want to unregister this component
238     if (!implementationClass.isNullOrEmpty()) {
239       val ws = config.options != null && java.lang.Boolean.parseBoolean(config.options!!.get("workspace"))
240       myPicoContainer.registerComponent(MyComponentAdapter(interfaceClass, implementationClass, pluginDescriptor, this, null, ws))
241     }
242   }
243
244   internal fun getApplication(): Application? = if (this is Application) this else ApplicationManager.getApplication()
245
246   private fun registerServices(services: List<ServiceDescriptor>, pluginDescriptor: IdeaPluginDescriptor) {
247     val picoContainer = myPicoContainer
248     for (descriptor in services) {
249       // Allow to re-define service implementations in plugins.
250       // Empty serviceImplementation means we want to unregister service.
251       if (descriptor.overrides && picoContainer.unregisterComponent(descriptor.getInterface()) == null) {
252         throw PluginException("Service: ${descriptor.getInterface()} doesn't override anything", pluginDescriptor.pluginId)
253       }
254
255       // empty serviceImplementation means we want to unregister service
256       if (descriptor.implementation != null) {
257         picoContainer.registerComponent(ServiceComponentAdapter(descriptor, pluginDescriptor, this))
258       }
259     }
260   }
261
262   @Internal
263   fun handleInitComponentError(t: Throwable, componentClassName: String?, pluginId: PluginId) {
264     if (handlingInitComponentError) {
265       return
266     }
267
268     handlingInitComponentError = true
269     try {
270       PluginManager.handleComponentError(t, componentClassName, pluginId)
271     }
272     finally {
273       handlingInitComponentError = false
274     }
275   }
276
277   @Internal
278   fun initializeComponent(component: Any, serviceDescriptor: ServiceDescriptor?) {
279     if (serviceDescriptor == null || !(component is PathMacroManager || component is IComponentStore || component is MessageBusFactory)) {
280       LoadingPhase.CONFIGURATION_STORE_INITIALIZED.assertAtLeast()
281       componentStore.initComponent(component, serviceDescriptor)
282     }
283   }
284
285   protected abstract fun getContainerDescriptor(pluginDescriptor: IdeaPluginDescriptorImpl): ContainerDescriptor
286
287   final override fun <T : Any> getComponent(interfaceClass: Class<T>): T? {
288     val picoContainer = picoContainer
289     val adapter = picoContainer.getComponentAdapter(interfaceClass) ?: return null
290     @Suppress("UNCHECKED_CAST")
291     return when (adapter) {
292       is BaseComponentAdapter -> {
293         if (parent != null && adapter.componentManager !== this) {
294           LOG.error("getComponent must be called on appropriate container (current: $this, expected: ${adapter.componentManager})")
295         }
296         adapter.getInstance(adapter.componentManager)
297       }
298       else -> adapter.getComponentInstance(picoContainer) as T
299     }
300   }
301
302   final override fun <T : Any> getService(serviceClass: Class<T>, createIfNeeded: Boolean): T? {
303     val lightServices = lightServices
304     if (lightServices != null && isLightService(serviceClass)) {
305       return getLightService(lightServices, serviceClass, createIfNeeded)
306     }
307
308     val key = serviceClass.name
309     val adapter = picoContainer.getServiceAdapter(key) as? ServiceComponentAdapter
310     if (adapter != null) {
311       return adapter.getInstance(this, createIfNeeded)
312     }
313
314     ProgressManager.checkCanceled()
315
316     if (parent != null) {
317       val result = parent.getService(serviceClass, createIfNeeded)
318       if (result != null) {
319         LOG.error("$key is registered as application service, but requested as project one")
320         return result
321       }
322     }
323
324     val result = getComponent(serviceClass) ?: return null
325     PluginException.logPluginError(LOG, "$key requested as a service, but it is a component - convert it to a service or " +
326                                         "change call to ${if (parent == null) "ApplicationManager.getApplication().getComponent()" else "project.getComponent()"}",
327                                    null, serviceClass)
328     return result
329   }
330
331   private fun <T : Any> getLightService(lightServices: ConcurrentMap<Class<*>, Any>, serviceClass: Class<T>, createIfNeeded: Boolean): T? {
332     @Suppress("UNCHECKED_CAST")
333     val result = lightServices.get(serviceClass) as T?
334     if (result != null || !createIfNeeded) {
335       return result
336     }
337     else {
338       synchronized(serviceClass) {
339         return getOrCreateLightService(serviceClass, lightServices)
340       }
341     }
342   }
343
344   @Internal
345   fun getServiceImplementationClassNames(prefix: String): List<String> {
346     val result = ArrayList<String>()
347     ServiceManagerImpl.processAllDescriptors(this) { serviceDescriptor ->
348       val implementation = serviceDescriptor.implementation ?: return@processAllDescriptors
349       if (implementation.startsWith(prefix)) {
350         result.add(implementation)
351       }
352     }
353     return result
354   }
355
356   private fun <T : Any> getOrCreateLightService(serviceClass: Class<T>, cache: ConcurrentMap<Class<*>, Any>): T {
357     LoadingPhase.COMPONENT_REGISTERED.assertAtLeast()
358
359     @Suppress("UNCHECKED_CAST")
360     var result = cache.get(serviceClass) as T?
361     if (result != null) {
362       return result
363     }
364
365     HeavyProcessLatch.INSTANCE.processStarted("Creating service '${serviceClass.name}'").use {
366       if (ProgressIndicatorProvider.getGlobalProgressIndicator() == null) {
367         result = createLightService(serviceClass)
368       }
369       else {
370         ProgressManager.getInstance().executeNonCancelableSection {
371           result = createLightService(serviceClass)
372         }
373       }
374     }
375
376     val prevValue = cache.put(serviceClass, result)
377     LOG.assertTrue(prevValue == null)
378     return result!!
379   }
380
381   final override fun createMessageBus(): MessageBus {
382     val messageBus: MessageBus = MessageBusFactory.newMessageBus(this, parent?.messageBus)
383     if (messageBus is MessageBusImpl && StartUpMeasurer.isMeasuringPluginStartupCosts()) {
384       messageBus.setMessageDeliveryListener { topic, messageName, handler, duration ->
385         if (!StartUpMeasurer.isMeasuringPluginStartupCosts()) {
386           messageBus.setMessageDeliveryListener(null)
387           return@setMessageDeliveryListener
388         }
389
390         logMessageBusDelivery(topic, messageName, handler, duration)
391       }
392     }
393
394     registerServiceInstance(MessageBus::class.java, messageBus, fakeCorePluginDescriptor)
395     return messageBus
396   }
397
398   protected open fun logMessageBusDelivery(topic: Topic<*>, messageName: String?, handler: Any, durationInNano: Long) {
399     val loader = handler.javaClass.classLoader
400     val pluginId = if (loader is PluginClassLoader) loader.pluginIdString else PluginManagerCore.CORE_PLUGIN_ID
401     StartUpMeasurer.addPluginCost(pluginId, "MessageBus", durationInNano)
402   }
403
404   /**
405    * Use only if approved by core team.
406    */
407   @Internal
408   fun registerComponent(key: Class<*>, implementation: Class<*>, pluginDescriptor: PluginDescriptor, override: Boolean) {
409     val picoContainer = picoContainer
410     if (override && picoContainer.unregisterComponent(key) == null) {
411       throw PluginException("Component $key doesn't override anything", pluginDescriptor.pluginId)
412     }
413     picoContainer.registerComponent(MyComponentAdapter(key, implementation.name, pluginDescriptor, this, implementation))
414   }
415
416   /**
417    * Use only if approved by core team.
418    */
419   @Internal
420   fun registerService(serviceInterface: Class<*>, implementation: Class<*>, pluginDescriptor: PluginDescriptor, override: Boolean) {
421     val serviceKey = serviceInterface.name
422     if (override && myPicoContainer.unregisterComponent(serviceKey) == null) {
423       throw PluginException("Service $serviceKey doesn't override anything", pluginDescriptor.pluginId)
424     }
425
426     val descriptor = ServiceDescriptor()
427     descriptor.serviceInterface = serviceInterface.name
428     descriptor.serviceImplementation = implementation.name
429     myPicoContainer.registerComponent(ServiceComponentAdapter(descriptor, pluginDescriptor, this, implementation))
430   }
431
432   /**
433    * Use only if approved by core team.
434    */
435   @Internal
436   fun <T : Any> registerServiceInstance(serviceInterface: Class<T>, instance: T, pluginDescriptor: PluginDescriptor) {
437     val serviceKey = serviceInterface.name
438     myPicoContainer.unregisterComponent(serviceKey)
439
440     val descriptor = ServiceDescriptor()
441     descriptor.serviceInterface = serviceKey
442     descriptor.serviceImplementation = instance.javaClass.name
443     picoContainer.registerComponent(ServiceComponentAdapter(descriptor, pluginDescriptor, this, instance.javaClass, instance))
444   }
445
446   @TestOnly
447   @Internal
448   fun <T : Any> replaceServiceInstance(serviceInterface: Class<T>, instance: T, parentDisposable: Disposable) {
449     val adapter = myPicoContainer.getServiceAdapter(serviceInterface.name) as ServiceComponentAdapter
450     adapter.replaceInstance(instance, parentDisposable)
451   }
452
453   private fun <T : Any> createLightService(serviceClass: Class<T>): T {
454     val startTime = StartUpMeasurer.getCurrentTime()
455
456     val result = instantiateClass(serviceClass, null)
457     if (result is Disposable) {
458       Disposer.register(this, result)
459     }
460
461     initializeComponent(result, null)
462     ParallelActivity.SERVICE.record(startTime, serviceClass, getActivityLevel())
463     return result
464   }
465
466   final override fun <T : Any> instantiateClass(aClass: Class<T>, pluginId: PluginId?): T {
467     try {
468       ProgressManager.checkCanceled()
469     }
470     catch (e: ProcessCanceledException) {
471       // otherwise ExceptionInInitializerError happens and the class is screwed forever
472       @Suppress("SpellCheckingInspection")
473       if (!e.stackTrace.any { it.methodName == "<clinit>" }) {
474         throw e
475       }
476     }
477
478     try {
479       if (parent == null) {
480         val constructor = aClass.getDeclaredConstructor()
481         constructor.isAccessible = true
482         return constructor.newInstance()
483       }
484       else {
485         val constructors = aClass.declaredConstructors
486
487         var constructor: Constructor<*>? = if (constructors.size > 1) {
488           // see ConfigurableEP - prefer constructor that accepts our instance
489           constructors.firstOrNull { it.parameterCount == 1 && it.parameterTypes[0].isAssignableFrom(javaClass) }
490         }
491         else {
492           null
493         }
494
495         if (constructor == null) {
496           constructors.sortBy { it.parameterCount }
497           constructor = constructors.first()!!
498         }
499
500         constructor.isAccessible = true
501         @Suppress("UNCHECKED_CAST")
502         return when (constructor.parameterCount) {
503           1 -> constructor.newInstance(this)
504           else -> constructor.newInstance()
505         } as T
506       }
507     }
508     catch (e: Throwable) {
509       if (e is InvocationTargetException) {
510         val targetException = e.targetException
511         if (targetException is ControlFlowException) {
512           throw targetException
513         }
514       }
515       else if (e is ControlFlowException) {
516         throw e
517       }
518
519       val message = "Cannot create class ${aClass.name}"
520       if (pluginId == null) {
521         throw PluginException.createByClass(message, e, aClass)
522       }
523       else {
524         throw PluginException(message, e, pluginId)
525       }
526     }
527   }
528
529   final override fun <T : Any> instantiateClassWithConstructorInjection(aClass: Class<T>, key: Any, pluginId: PluginId?): T {
530     // constructorParameterResolver is very expensive, because pico container behaviour is to find greediest satisfiable constructor,
531     // so, if class has constructors (Project) and (Project, Foo, Bar), then Foo and Bar unrelated classes will be searched for.
532     // To avoid this expensive nearly linear search of extension, first resolve without our logic, and in case of error try expensive.
533     try {
534       return instantiateUsingPicoContainer(aClass, key, this, constructorParameterResolver)
535     }
536     catch (e: ProcessCanceledException) {
537       throw e
538     }
539     catch (e: ExtensionNotApplicableException) {
540       throw e
541     }
542     catch (e: IncorrectOperationException) {
543       throw e
544     }
545     catch (e: Exception) {
546       if (lightServices == null) {
547         throw e
548       }
549       else {
550         assertExtensionInjection(pluginId, e)
551         return instantiateUsingPicoContainer(aClass, key, this, heavyConstructorParameterResolver)
552       }
553     }
554   }
555
556   protected open fun assertExtensionInjection(pluginId: PluginId?, e: Exception) {
557     val app = getApplication()
558     @Suppress("SpellCheckingInspection")
559     if (app != null && app.isUnitTestMode && pluginId?.idString != "org.jetbrains.kotlin" && pluginId?.idString != "Lombook Plugin") {
560       throw UnsupportedOperationException("In tests deprecated constructor injection for extension is disabled", e)
561     }
562   }
563
564   final override fun <T : Any> instantiateExtensionWithPicoContainerOnlyIfNeeded(className: String?, pluginDescriptor: PluginDescriptor?): T {
565     val pluginId = pluginDescriptor?.pluginId ?: PluginId.getId("unknown")
566     if (className == null) {
567       throw PluginException("implementation class is not specified", pluginId)
568     }
569
570     val aClass = try {
571       @Suppress("UNCHECKED_CAST")
572       Class.forName(className, true, pluginDescriptor?.pluginClassLoader ?: javaClass.classLoader) as Class<T>
573     }
574     catch (e: Throwable) {
575       throw PluginException(e, pluginId)
576     }
577
578     try {
579       return instantiateClass(aClass, pluginId)
580     }
581     catch (e: ProcessCanceledException) {
582       throw e
583     }
584     catch (e: ExtensionNotApplicableException) {
585       throw e
586     }
587     catch (e: Throwable) {
588       when {
589         e.cause is NoSuchMethodException || e.cause is IllegalArgumentException -> {
590           val exception = PluginException("Bean extension class constructor must not have parameters: $className", pluginId)
591           if ((pluginDescriptor?.isBundled == true) || getApplication()?.isUnitTestMode == true) {
592             LOG.error(exception)
593           }
594           else {
595             LOG.warn(exception)
596           }
597         }
598         e is PluginException -> throw e
599         else -> throw if (pluginDescriptor == null) PluginException.createByClass(e, aClass) else PluginException(e, pluginDescriptor.pluginId)
600       }
601     }
602
603     return instantiateClassWithConstructorInjection(aClass, aClass, pluginId)
604   }
605
606   final override fun createListener(descriptor: ListenerDescriptor): Any {
607     val pluginDescriptor = descriptor.pluginDescriptor
608     val aClass = try {
609       Class.forName(descriptor.listenerClassName, true, pluginDescriptor.pluginClassLoader)
610     }
611     catch (e: Throwable) {
612       throw PluginException("Cannot create listener ${descriptor.listenerClassName}", e, pluginDescriptor.pluginId)
613     }
614     return instantiateClass(aClass, pluginDescriptor.pluginId)
615   }
616
617   final override fun logError(error: Throwable, pluginId: PluginId) {
618     if (error is ProcessCanceledException || error is ExtensionNotApplicableException) {
619       throw error
620     }
621
622     LOG.error(createPluginExceptionIfNeeded(error, pluginId))
623   }
624
625   final override fun createError(error: Throwable, pluginId: PluginId): RuntimeException {
626     return when (error) {
627       is ProcessCanceledException, is ExtensionNotApplicableException -> error as RuntimeException
628       else -> createPluginExceptionIfNeeded(error, pluginId)
629     }
630   }
631
632   final override fun createError(message: String, pluginId: PluginId) = PluginException(message, pluginId)
633
634   @Internal
635   fun getActivityLevel(): StartUpMeasurer.Level = DefaultPicoContainer.getActivityLevel(myPicoContainer)
636
637   @Internal
638   fun unloadServices(containerDescriptor: ContainerDescriptor): List<Any> {
639     val unloadedInstances = ArrayList<Any>()
640     for (service in containerDescriptor.services) {
641       val adapter = myPicoContainer.unregisterComponent(service.getInterface()) as? ServiceComponentAdapter ?: continue
642       val instance = adapter.getInstance<Any>(this, createIfNeeded = false) ?: continue
643       if (instance is Disposable) {
644         Disposer.dispose(instance)
645       }
646       unloadedInstances.add(instance)
647     }
648     return unloadedInstances
649   }
650
651   @Internal
652   fun precreateService(serviceClass: String) {
653     (myPicoContainer.getServiceAdapter(serviceClass) as ServiceComponentAdapter?)?.getInstance<Any>(this)
654   }
655
656   @Internal
657   open fun activityNamePrefix(): String? = null
658 }
659
660 private fun createPluginExceptionIfNeeded(error: Throwable, pluginId: PluginId): RuntimeException {
661   return when (error) {
662     is PluginException, is ExtensionInstantiationException -> error as RuntimeException
663     else -> PluginException(error, pluginId)
664   }
665 }
666
667 private fun <T> isLightService(serviceClass: Class<T>): Boolean {
668   return Modifier.isFinal(serviceClass.modifiers) && serviceClass.isAnnotationPresent(Service::class.java)
669 }