1 package com
.intellij
.openapi
.components
.impl
;
3 import com
.intellij
.diagnostic
.PluginException
;
4 import com
.intellij
.ide
.plugins
.IdeaPluginDescriptor
;
5 import com
.intellij
.openapi
.application
.ApplicationManager
;
6 import com
.intellij
.openapi
.application
.ex
.ApplicationManagerEx
;
7 import com
.intellij
.openapi
.application
.impl
.ApplicationInfoImpl
;
8 import com
.intellij
.openapi
.components
.BaseComponent
;
9 import com
.intellij
.openapi
.components
.ComponentConfig
;
10 import com
.intellij
.openapi
.components
.ComponentManager
;
11 import com
.intellij
.openapi
.components
.StateStorage
;
12 import com
.intellij
.openapi
.components
.ex
.ComponentManagerEx
;
13 import com
.intellij
.openapi
.components
.impl
.stores
.IComponentStore
;
14 import com
.intellij
.openapi
.diagnostic
.Logger
;
15 import com
.intellij
.openapi
.progress
.ProcessCanceledException
;
16 import com
.intellij
.openapi
.progress
.ProgressIndicator
;
17 import com
.intellij
.openapi
.progress
.ProgressManager
;
18 import com
.intellij
.openapi
.util
.Disposer
;
19 import com
.intellij
.openapi
.util
.UserDataHolderBase
;
20 import com
.intellij
.util
.ArrayUtil
;
21 import com
.intellij
.util
.IncorrectOperationException
;
22 import com
.intellij
.util
.ReflectionCache
;
23 import com
.intellij
.util
.containers
.ConcurrentHashMap
;
24 import com
.intellij
.util
.containers
.HashMap
;
25 import com
.intellij
.util
.messages
.MessageBus
;
26 import com
.intellij
.util
.messages
.MessageBusFactory
;
27 import com
.intellij
.util
.pico
.IdeaPicoContainer
;
28 import org
.jetbrains
.annotations
.NonNls
;
29 import org
.jetbrains
.annotations
.NotNull
;
30 import org
.jetbrains
.annotations
.Nullable
;
31 import org
.picocontainer
.*;
32 import org
.picocontainer
.defaults
.CachingComponentAdapter
;
33 import org
.picocontainer
.defaults
.ConstructorInjectionComponentAdapter
;
35 import java
.io
.IOException
;
36 import java
.lang
.reflect
.Array
;
37 import java
.util
.ArrayList
;
38 import java
.util
.List
;
44 public abstract class ComponentManagerImpl
extends UserDataHolderBase
implements ComponentManagerEx
, Disposable
{
45 private static final Logger LOG
= Logger
.getInstance("#com.intellij.components.ComponentManager");
47 private final Map
<Class
, Object
> myInitializedComponents
= new ConcurrentHashMap
<Class
, Object
>(4096);
49 private boolean myComponentsCreated
= false;
51 private MutablePicoContainer myPicoContainer
;
52 private volatile boolean myDisposed
= false;
53 private volatile boolean myDisposeCompleted
= false;
55 private MessageBus myMessageBus
;
57 private final ComponentManagerConfigurator myConfigurator
= new ComponentManagerConfigurator(this);
58 private final ComponentManager myParentComponentManager
;
59 private IComponentStore myComponentStore
;
60 private Boolean myHeadless
;
61 private ComponentsRegistry myComponentsRegistry
= new ComponentsRegistry();
62 private boolean myHaveProgressManager
= false;
64 protected ComponentManagerImpl(ComponentManager parentComponentManager
) {
65 myParentComponentManager
= parentComponentManager
;
66 boostrapPicoContainer();
69 //todo[mike] there are several init* methods. Make it just 1
76 public synchronized IComponentStore
getStateStore() {
77 if (myComponentStore
== null) {
78 assert myPicoContainer
!= null;
79 myComponentStore
= (IComponentStore
)myPicoContainer
.getComponentInstance(IComponentStore
.class);
81 return myComponentStore
;
84 public IComponentStore
getComponentStore() {
85 return getStateStore();
89 public MessageBus
getMessageBus() {
90 assert !myDisposeCompleted
&& !myDisposed
: "Already disposed";
91 assert myMessageBus
!= null : "Not initialized yet";
95 public boolean isComponentsCreated() {
96 return myComponentsCreated
;
99 private void createComponents() {
101 myComponentsRegistry
.loadClasses();
103 final Class
[] componentInterfaces
= myComponentsRegistry
.getComponentInterfaces();
104 for (Class componentInterface
: componentInterfaces
) {
106 createComponent(componentInterface
);
108 catch (StateStorage
.StateStorageException e
) {
111 catch (ProcessCanceledException e
) {
120 myComponentsCreated
= true;
124 private synchronized Object
createComponent(Class componentInterface
) {
125 final Object component
= getPicoContainer().getComponentInstance(componentInterface
.getName());
126 assert component
!= null : "Can't instantiate component for: " + componentInterface
;
130 protected void disposeComponents() {
131 final List
<Object
> components
= myComponentsRegistry
.getRegisteredImplementations();
134 for (int i
= components
.size() - 1; i
>= 0; i
--) {
135 Object component
= components
.get(i
);
136 if (component
instanceof BaseComponent
) {
138 ((BaseComponent
)component
).disposeComponent();
140 catch (Throwable e
) {
146 myComponentsCreated
= false;
149 @SuppressWarnings({"unchecked"})
151 protected <T
> T
getComponentFromContainer(Class
<T
> interfaceClass
) {
152 final T initializedComponent
= (T
)myInitializedComponents
.get(interfaceClass
);
153 if (initializedComponent
!= null) return initializedComponent
;
155 //if (!myComponentsCreated) {
156 // LOG.error("Component requests are not allowed before they are created");
159 synchronized (this) {
160 if (!myComponentsRegistry
.containsInterface(interfaceClass
)) {
164 Object lock
= myComponentsRegistry
.getComponentLock(interfaceClass
);
166 synchronized (lock
) {
167 T dcl
= (T
)myInitializedComponents
.get(interfaceClass
);
168 if (dcl
!= null) return dcl
;
170 T component
= (T
)getPicoContainer().getComponentInstance(interfaceClass
.getName());
171 if (component
== null) {
172 component
= (T
)createComponent(interfaceClass
);
175 if (component
== null) {
176 throw new IncorrectOperationException("createComponent() returns null for: " + interfaceClass
);
179 myInitializedComponents
.put(interfaceClass
, component
);
181 if (component
instanceof com
.intellij
.openapi
.Disposable
) {
182 Disposer
.register(this, (com
.intellij
.openapi
.Disposable
)component
);
190 public <T
> T
getComponent(Class
<T
> interfaceClass
) {
191 assert !myDisposeCompleted
: "Already disposed";
192 return getComponent(interfaceClass
, null);
195 public <T
> T
getComponent(Class
<T
> interfaceClass
, T defaultImplementation
) {
196 final T fromContainer
= getComponentFromContainer(interfaceClass
);
197 if (fromContainer
!= null) return fromContainer
;
198 if (defaultImplementation
!= null) return defaultImplementation
;
202 private void initComponent(Object component
) {
203 if (myHaveProgressManager
) {
204 final ProgressManager progressManager
= ProgressManager
.getInstance();
206 final ProgressIndicator indicator
= progressManager
!= null ? progressManager
.getProgressIndicator() : null;
207 if (indicator
!= null) {
208 String name
= component
instanceof BaseComponent ?
((BaseComponent
)component
).getComponentName() : component
.getClass().getName();
209 indicator
.checkCanceled();
210 indicator
.setText2(name
);
211 indicator
.setIndeterminate(false);
212 indicator
.setFraction(myComponentsRegistry
.getPercentageOfComponentsLoaded());
215 if (component
instanceof ProgressManager
) {
216 myHaveProgressManager
= true;
220 getStateStore().initComponent(component
);
221 if (component
instanceof BaseComponent
) {
222 ((BaseComponent
)component
).initComponent();
225 catch (StateStorage
.StateStorageException e
) {
228 catch (ProcessCanceledException e
) {
231 catch (Throwable ex
) {
232 handleInitComponentError(ex
, false, component
.getClass().getName());
236 protected void handleInitComponentError(final Throwable ex
, final boolean fatal
, final String componentClassName
) {
240 public void registerComponent(Class interfaceClass
, Class implementationClass
) {
241 registerComponent(interfaceClass
, implementationClass
, null);
245 @SuppressWarnings({"unchecked"})
246 public void registerComponent(Class interfaceClass
, Class implementationClass
, Map options
) {
247 LOG
.warn("Deprecated method usage: registerComponent", new Throwable());
249 final ComponentConfig config
= new ComponentConfig();
250 config
.implementationClass
= implementationClass
.getName();
251 config
.interfaceClass
= interfaceClass
.getName();
252 config
.options
= options
;
253 registerComponent(config
);
256 @SuppressWarnings({"NonPrivateFieldAccessedInSynchronizedContext"})
257 public synchronized void registerComponent(final ComponentConfig config
, final IdeaPluginDescriptor pluginDescriptor
) {
259 String headlessImplClass
= config
.headlessImplementationClass
;
260 if (headlessImplClass
!= null) {
261 if (headlessImplClass
.trim().length() == 0) return;
262 config
.implementationClass
= headlessImplClass
;
266 config
.implementationClass
= config
.implementationClass
.trim();
268 if (config
.interfaceClass
== null) config
.interfaceClass
= config
.implementationClass
;
269 config
.interfaceClass
= config
.interfaceClass
.trim();
271 config
.pluginDescriptor
= pluginDescriptor
;
272 myComponentsRegistry
.registerComponent(config
);
276 public synchronized Class
[] getComponentInterfaces() {
277 LOG
.warn("Deprecated method usage: getComponentInterfaces", new Throwable());
278 return myComponentsRegistry
.getComponentInterfaces();
281 public synchronized boolean hasComponent(@NotNull Class interfaceClass
) {
282 return myComponentsRegistry
.containsInterface(interfaceClass
);
285 protected synchronized Object
[] getComponents() {
286 Class
[] componentClasses
= myComponentsRegistry
.getComponentInterfaces();
287 ArrayList
<Object
> components
= new ArrayList
<Object
>(componentClasses
.length
);
288 for (Class
<?
> interfaceClass
: componentClasses
) {
289 Object component
= getComponent(interfaceClass
);
290 if (component
!= null) components
.add(component
);
292 return ArrayUtil
.toObjectArray(components
);
295 @SuppressWarnings({"unchecked"})
297 public synchronized <T
> T
[] getComponents(Class
<T
> baseClass
) {
298 return myComponentsRegistry
.getComponentsByType(baseClass
);
302 public MutablePicoContainer
getPicoContainer() {
303 assert !myDisposeCompleted
: "Already disposed";
304 return myPicoContainer
;
307 protected MutablePicoContainer
createPicoContainer() {
308 MutablePicoContainer result
;
310 if (myParentComponentManager
!= null) {
311 result
= new IdeaPicoContainer(myParentComponentManager
.getPicoContainer());
314 result
= new IdeaPicoContainer();
320 public synchronized BaseComponent
getComponent(String name
) {
321 return myComponentsRegistry
.getComponentByName(name
);
324 protected boolean isComponentSuitable(Map
<String
, String
> options
) {
325 return !isTrue(options
, "internal") || ApplicationManagerEx
.getApplicationEx().isInternal();
328 private static boolean isTrue(Map
<String
, String
> options
, @NonNls final String option
) {
329 return options
!= null && options
.containsKey(option
) && Boolean
.valueOf(options
.get(option
)).booleanValue();
332 public synchronized void dispose() {
333 ApplicationManager
.getApplication().assertIsDispatchThread();
334 myDisposeCompleted
= true;
336 if (myMessageBus
!= null) {
337 myMessageBus
.dispose();
341 myInitializedComponents
.clear();
342 myComponentsRegistry
= null;
343 myComponentStore
= null;
344 myPicoContainer
= null;
347 public boolean isDisposed() {
352 public void initComponents() {
357 protected void loadComponentsConfiguration(ComponentConfig
[] components
, @Nullable final IdeaPluginDescriptor descriptor
, final boolean defaultProject
) {
358 myConfigurator
.loadComponentsConfiguration(components
, descriptor
, defaultProject
);
361 protected void boostrapPicoContainer() {
362 myPicoContainer
= createPicoContainer();
364 myMessageBus
= MessageBusFactory
.newMessageBus(this, myParentComponentManager
!= null ? myParentComponentManager
.getMessageBus() : null);
365 final MutablePicoContainer picoContainer
= getPicoContainer();
366 picoContainer
.registerComponentInstance(MessageBus
.class, myMessageBus
);
368 picoContainer.registerComponentInstance(ExtensionInitializer.class, new ExtensionInitializer() {
369 public void initExtension(final Object extension) {
370 getComponentStore().initComponent(extension);
377 protected ComponentManager
getParentComponentManager() {
378 return myParentComponentManager
;
381 private boolean isHeadless() {
382 if (myHeadless
== null) {
383 myHeadless
= ApplicationManager
.getApplication().isHeadlessEnvironment();
386 return myHeadless
.booleanValue();
390 public void registerComponent(final ComponentConfig config
) {
391 registerComponent(config
, null);
395 public ComponentConfig
[] getComponentConfigurations() {
396 return myComponentsRegistry
.getComponentConfigurations();
400 public Object
getComponent(final ComponentConfig componentConfig
) {
401 return getPicoContainer().getComponentInstance(componentConfig
.interfaceClass
);
404 public ComponentConfig
getConfig(Class componentImplementation
) {
405 return myComponentsRegistry
.getConfig(componentImplementation
);
408 private class ComponentsRegistry
{
409 private final Map
<Class
, Object
> myInterfaceToLockMap
= new HashMap
<Class
, Object
>();
410 private final Map
<Class
, Class
> myInterfaceToClassMap
= new HashMap
<Class
, Class
>();
411 private final ArrayList
<Class
> myComponentInterfaces
= new ArrayList
<Class
>(); // keeps order of component's registration
412 private final Map
<String
, BaseComponent
> myNameToComponent
= new HashMap
<String
, BaseComponent
>();
413 private final List
<ComponentConfig
> myComponentConfigs
= new ArrayList
<ComponentConfig
>();
414 private final List
<Object
> myImplementations
= new ArrayList
<Object
>();
415 private final Map
<Class
, ComponentConfig
> myComponentClassToConfig
= new java
.util
.HashMap
<Class
, ComponentConfig
>();
416 private boolean myClassesLoaded
= false;
418 private void loadClasses() {
419 assert !myClassesLoaded
;
421 for (ComponentConfig config
: myComponentConfigs
) {
425 myClassesLoaded
= true;
428 private void loadClasses(final ComponentConfig config
) {
429 ClassLoader loader
= config
.getClassLoader();
432 final Class
<?
> interfaceClass
= Class
.forName(config
.interfaceClass
, true, loader
);
433 final Class
<?
> implementationClass
= Class
.forName(config
.implementationClass
, true, loader
);
435 if (myInterfaceToClassMap
.get(interfaceClass
) != null) {
436 throw new Error("ComponentSetup for component " + interfaceClass
.getName() + " already registered");
439 getPicoContainer().registerComponent(new ComponentConfigComponentAdapter(config
));
440 myInterfaceToClassMap
.put(interfaceClass
, implementationClass
);
441 myComponentClassToConfig
.put(implementationClass
, config
);
442 myComponentInterfaces
.add(interfaceClass
);
444 catch (Exception e
) {
445 @NonNls final String message
= "Error while registering component: " + config
;
447 if (config
.pluginDescriptor
!= null) {
448 LOG
.error(message
, new PluginException(e
, config
.pluginDescriptor
.getPluginId()));
451 LOG
.error(message
, e
);
455 if (config
.pluginDescriptor
!= null) {
456 LOG
.error(new PluginException(e
, config
.pluginDescriptor
.getPluginId()));
464 private Object
getComponentLock(final Class componentClass
) {
465 Object lock
= myInterfaceToLockMap
.get(componentClass
);
467 myInterfaceToLockMap
.put(componentClass
, lock
= new Object());
472 private Class
[] getComponentInterfaces() {
473 assert myClassesLoaded
;
474 return myComponentInterfaces
.toArray(new Class
[myComponentInterfaces
.size()]);
477 private boolean containsInterface(final Class interfaceClass
) {
478 if (!myClassesLoaded
) loadClasses();
479 return myInterfaceToClassMap
.containsKey(interfaceClass
);
482 public double getPercentageOfComponentsLoaded() {
483 return ((double)myImplementations
.size()) / myComponentConfigs
.size();
486 private void registerComponentInstance(final Object component
) {
487 myImplementations
.add(component
);
489 if (component
instanceof BaseComponent
) {
490 BaseComponent baseComponent
= (BaseComponent
)component
;
491 final String componentName
= baseComponent
.getComponentName();
493 if (myNameToComponent
.containsKey(componentName
)) {
494 BaseComponent loadedComponent
= myNameToComponent
.get(componentName
);
495 // component may have been already loaded by PicoContainer, so fire error only if components are really different
496 if (!component
.equals(loadedComponent
)) {
497 LOG
.error("Component name collision: " + componentName
+ " " + loadedComponent
.getClass() + " and " + component
.getClass());
501 myNameToComponent
.put(componentName
, baseComponent
);
506 public List
<Object
> getRegisteredImplementations() {
507 return myImplementations
;
510 private void registerComponent(ComponentConfig config
) {
511 myComponentConfigs
.add(config
);
513 if (myClassesLoaded
) {
518 private BaseComponent
getComponentByName(final String name
) {
519 return myNameToComponent
.get(name
);
522 @SuppressWarnings({"unchecked"})
523 public <T
> T
[] getComponentsByType(final Class
<T
> baseClass
) {
524 ArrayList
<T
> array
= new ArrayList
<T
>();
526 //noinspection ForLoopReplaceableByForEach
527 for (int i
= 0; i
< myComponentInterfaces
.size(); i
++) {
528 Class interfaceClass
= myComponentInterfaces
.get(i
);
529 final Class implClass
= myInterfaceToClassMap
.get(interfaceClass
);
530 if (ReflectionCache
.isAssignable(baseClass
, implClass
)) {
531 array
.add((T
)getComponent(interfaceClass
));
535 return array
.toArray((T
[])Array
.newInstance(baseClass
, array
.size()));
538 public ComponentConfig
[] getComponentConfigurations() {
539 return myComponentConfigs
.toArray(new ComponentConfig
[myComponentConfigs
.size()]);
542 public ComponentConfig
getConfig(final Class componentImplementation
) {
543 return myComponentClassToConfig
.get(componentImplementation
);
547 private class ComponentConfigComponentAdapter
implements ComponentAdapter
{
548 private final ComponentConfig myConfig
;
549 private ComponentAdapter myDelegate
;
550 private boolean myInitialized
= false;
551 private boolean myInitializing
= false;
553 public ComponentConfigComponentAdapter(final ComponentConfig config
) {
557 public Object
getComponentKey() {
558 return myConfig
.interfaceClass
;
561 public Class
getComponentImplementation() {
562 return getDelegate().getComponentImplementation();
565 public Object
getComponentInstance(final PicoContainer container
) throws PicoInitializationException
, PicoIntrospectionException
{
566 return getDelegate().getComponentInstance(container
);
569 public void verify(final PicoContainer container
) throws PicoIntrospectionException
{
570 getDelegate().verify(container
);
573 public void accept(final PicoVisitor visitor
) {
574 visitor
.visitComponentAdapter(this);
575 getDelegate().accept(visitor
);
578 private ComponentAdapter
getDelegate() {
579 if (myDelegate
== null) {
580 final Object componentKey
= getComponentKey();
582 ClassLoader loader
= myConfig
.getClassLoader();
584 Class
<?
> implementationClass
= null;
587 implementationClass
= Class
.forName(myConfig
.implementationClass
, true, loader
);
589 catch (Exception e
) {
590 @NonNls final String message
= "Error while registering component: " + myConfig
;
592 if (myConfig
.pluginDescriptor
!= null) {
593 LOG
.error(message
, new PluginException(e
, myConfig
.pluginDescriptor
.getPluginId()));
596 LOG
.error(message
, e
);
600 if (myConfig
.pluginDescriptor
!= null) {
601 LOG
.error(new PluginException(e
, myConfig
.pluginDescriptor
.getPluginId()));
608 assert implementationClass
!= null;
610 myDelegate
= new CachingComponentAdapter(new ConstructorInjectionComponentAdapter(componentKey
, implementationClass
, null, true)) {
611 public Object
getComponentInstance(PicoContainer picoContainer
) throws PicoInitializationException
, PicoIntrospectionException
{
612 Object componentInstance
= null;
614 long startTime
= myInitialized ?
0 : System
.nanoTime();
615 componentInstance
= super.getComponentInstance(picoContainer
);
617 if (!myInitialized
) {
618 if (myInitializing
) LOG
.error(new Throwable("Cyclic component initialization: " + componentKey
));
619 myInitializing
= true;
620 myComponentsRegistry
.registerComponentInstance(componentInstance
);
621 initComponent(componentInstance
);
622 long endTime
= System
.nanoTime();
623 long ms
= (endTime
- startTime
) / 1000000;
625 if (ApplicationInfoImpl
.getShadowInstance().isEAP()) {
626 LOG
.info(componentInstance
.getClass().getName() + " initialized in " + ms
+ " ms");
628 else if (LOG
.isDebugEnabled()) {
629 LOG
.debug(componentInstance
.getClass().getName() + " initialized in " + ms
+ " ms");
632 myInitializing
= false;
633 myInitialized
= true;
636 catch (ProcessCanceledException e
) {
639 catch (StateStorage
.StateStorageException e
) {
642 catch (Throwable t
) {
643 handleInitComponentError(t
, componentInstance
== null, componentKey
.toString());
645 return componentInstance
;
654 protected void doSave() throws IOException
{
655 IComponentStore
.SaveSession session
= null;
657 session
= getStateStore().startSave();
661 if (session
!= null) {
662 session
.finishSave();
667 public final int hashCode() {
668 return super.hashCode();
671 public final boolean equals(Object obj
) {
672 return super.equals(obj
);