forward
[fedora-idea.git] / platform / platform-impl / src / com / intellij / openapi / components / impl / ComponentManagerImpl.java
blobd266f32cd5fa4306d5b41d09613a17776e051653
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;
39 import java.util.Map;
41 /**
42 * @author mike
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
70 public void init() {
71 initComponents();
75 @NotNull
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";
92 return myMessageBus;
95 public boolean isComponentsCreated() {
96 return myComponentsCreated;
99 private void createComponents() {
100 try {
101 myComponentsRegistry.loadClasses();
103 final Class[] componentInterfaces = myComponentsRegistry.getComponentInterfaces();
104 for (Class componentInterface : componentInterfaces) {
105 try {
106 createComponent(componentInterface);
108 catch (StateStorage.StateStorageException e) {
109 throw e;
111 catch (ProcessCanceledException e) {
112 throw e;
114 catch(Exception e) {
115 LOG.error(e);
119 finally {
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;
127 return component;
130 protected void disposeComponents() {
131 final List<Object> components = myComponentsRegistry.getRegisteredImplementations();
132 myDisposed = true;
134 for (int i = components.size() - 1; i >= 0; i--) {
135 Object component = components.get(i);
136 if (component instanceof BaseComponent) {
137 try {
138 ((BaseComponent)component).disposeComponent();
140 catch (Throwable e) {
141 LOG.error(e);
146 myComponentsCreated = false;
149 @SuppressWarnings({"unchecked"})
150 @Nullable
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)) {
161 return null;
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);
185 return 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;
199 return null;
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;
219 try {
220 getStateStore().initComponent(component);
221 if (component instanceof BaseComponent) {
222 ((BaseComponent)component).initComponent();
225 catch (StateStorage.StateStorageException e) {
226 throw e;
228 catch (ProcessCanceledException e) {
229 throw 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) {
237 LOG.error(ex);
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) {
258 if (isHeadless()) {
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);
275 @NotNull
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"})
296 @NotNull
297 public synchronized <T> T[] getComponents(Class<T> baseClass) {
298 return myComponentsRegistry.getComponentsByType(baseClass);
301 @NotNull
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());
313 else {
314 result = new IdeaPicoContainer();
317 return result;
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();
338 myMessageBus = null;
341 myInitializedComponents.clear();
342 myComponentsRegistry = null;
343 myComponentStore = null;
344 myPicoContainer = null;
347 public boolean isDisposed() {
348 return myDisposed;
352 public void initComponents() {
353 createComponents();
354 getComponents();
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);
394 @NotNull
395 public ComponentConfig[] getComponentConfigurations() {
396 return myComponentsRegistry.getComponentConfigurations();
399 @Nullable
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) {
422 loadClasses(config);
425 myClassesLoaded = true;
428 private void loadClasses(final ComponentConfig config) {
429 ClassLoader loader = config.getClassLoader();
431 try {
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()));
450 else {
451 LOG.error(message, e);
454 catch (Error e) {
455 if (config.pluginDescriptor != null) {
456 LOG.error(new PluginException(e, config.pluginDescriptor.getPluginId()));
458 else {
459 throw e;
464 private Object getComponentLock(final Class componentClass) {
465 Object lock = myInterfaceToLockMap.get(componentClass);
466 if (lock == null) {
467 myInterfaceToLockMap.put(componentClass, lock = new Object());
469 return lock;
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());
500 else {
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) {
514 loadClasses(config);
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) {
554 myConfig = 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;
586 try {
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()));
595 else {
596 LOG.error(message, e);
599 catch (Error e) {
600 if (myConfig.pluginDescriptor != null) {
601 LOG.error(new PluginException(e, myConfig.pluginDescriptor.getPluginId()));
603 else {
604 throw e;
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;
613 try {
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;
624 if (ms > 10) {
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) {
637 throw e;
639 catch (StateStorage.StateStorageException e) {
640 throw e;
642 catch (Throwable t) {
643 handleInitComponentError(t, componentInstance == null, componentKey.toString());
645 return componentInstance;
650 return myDelegate;
654 protected void doSave() throws IOException {
655 IComponentStore.SaveSession session = null;
656 try {
657 session = getStateStore().startSave();
658 session.save();
660 finally {
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);