/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.components.impl;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.AccessToken;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.*;
import com.intellij.openapi.components.ex.ComponentManagerEx;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.*;
import com.intellij.openapi.extensions.impl.ExtensionComponentAdapter;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
import com.intellij.util.PairProcessor;
import com.intellij.util.io.storage.HeavyProcessLatch;
import com.intellij.util.pico.AssignableToComponentAdapter;
import com.intellij.util.pico.CachingConstructorInjectionComponentAdapter;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.picocontainer.*;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
public class ServiceManagerImpl implements BaseComponent {
private static final Logger LOGGER = Logger.getInstance(ServiceManagerImpl.class);
private static final ExtensionPointName<ServiceDescriptor> APP_SERVICES = new ExtensionPointName<>("com.intellij.applicationService");
private static final ExtensionPointName<ServiceDescriptor> PROJECT_SERVICES = new ExtensionPointName<>("com.intellij.projectService");
private ExtensionPointName<ServiceDescriptor> myExtensionPointName;
private ExtensionPointListener<ServiceDescriptor> myExtensionPointListener;
public ServiceManagerImpl() {
installEP(APP_SERVICES, ApplicationManager.getApplication());
}
public ServiceManagerImpl(Project project) {
installEP(PROJECT_SERVICES, project);
}
protected ServiceManagerImpl(boolean ignoreInit) {
}
protected void installEP(final ExtensionPointName<ServiceDescriptor> pointName, final ComponentManager componentManager) {
myExtensionPointName = pointName;
final ExtensionPoint<ServiceDescriptor> extensionPoint = Extensions.getArea(null).getExtensionPoint(pointName);
final MutablePicoContainer picoContainer = (MutablePicoContainer)componentManager.getPicoContainer();
myExtensionPointListener = new ExtensionPointListener<ServiceDescriptor>() {
@Override
public void extensionAdded(@NotNull final ServiceDescriptor descriptor, final PluginDescriptor pluginDescriptor) {
picoContainer.registerComponent(new MyComponentAdapter(descriptor, pluginDescriptor, (ComponentManagerEx)componentManager));
}
@Override
public void extensionRemoved(@NotNull final ServiceDescriptor extension, final PluginDescriptor pluginDescriptor) {
picoContainer.unregisterComponent(extension.getInterface());
}
};
extensionPoint.addExtensionPointListener(myExtensionPointListener);
}
public List<ServiceDescriptor> getAllDescriptors() {
ServiceDescriptor[] extensions = Extensions.getExtensions(myExtensionPointName);
return Arrays.asList(extensions);
}
public static void processAllImplementationClasses(@NotNull ComponentManagerImpl componentManager,
@NotNull PairProcessor<Class<?>, PluginDescriptor> processor) {
Collection adapters = componentManager.getPicoContainer().getComponentAdapters();
if (adapters.isEmpty()) {
return;
}
for (Object o : adapters) {
Class aClass;
if (o instanceof MyComponentAdapter) {
MyComponentAdapter adapter = (MyComponentAdapter)o;
PluginDescriptor pluginDescriptor = adapter.myPluginDescriptor;
try {
ComponentAdapter delegate = adapter.myDelegate;
// avoid delegation creation & class initialization
if (delegate == null) {
ClassLoader classLoader = pluginDescriptor == null ? ServiceManagerImpl.class.getClassLoader() : pluginDescriptor.getPluginClassLoader();
aClass = Class.forName(adapter.myDescriptor.getImplementation(), false, classLoader);
}
else {
aClass = delegate.getComponentImplementation();
}
}
catch (Throwable e) {
LOGGER.error(e);
continue;
}
if (!processor.process(aClass, pluginDescriptor)) {
break;
}
}
else if (o instanceof ComponentAdapter && !(o instanceof ExtensionComponentAdapter)) {
try {
aClass = ((ComponentAdapter)o).getComponentImplementation();
}
catch (Throwable e) {
LOGGER.error(e);
continue;
}
ComponentConfig config = componentManager.getConfig(aClass);
if (config != null) {
processor.process(aClass, config.pluginDescriptor);
}
}
}
}
@Override
@NonNls
@NotNull
public String getComponentName() {
return getClass().getName();
}
@Override
public void initComponent() {
}
@Override
public void disposeComponent() {
final ExtensionPoint<ServiceDescriptor> extensionPoint = Extensions.getArea(null).getExtensionPoint(myExtensionPointName);
extensionPoint.removeExtensionPointListener(myExtensionPointListener);
}
private static class MyComponentAdapter implements AssignableToComponentAdapter {
private ComponentAdapter myDelegate;
private final ServiceDescriptor myDescriptor;
private final PluginDescriptor myPluginDescriptor;
private final ComponentManagerEx myComponentManager;
private volatile Object myInitializedComponentInstance = null;
public MyComponentAdapter(final ServiceDescriptor descriptor, final PluginDescriptor pluginDescriptor, ComponentManagerEx componentManager) {
myDescriptor = descriptor;
myPluginDescriptor = pluginDescriptor;
myComponentManager = componentManager;
myDelegate = null;
}
@Override
public String getComponentKey() {
return myDescriptor.getInterface();
}
@Override
public Class getComponentImplementation() {
return loadClass(myDescriptor.getInterface());
}
private Class loadClass(final String className) {
try {
final ClassLoader classLoader = myPluginDescriptor != null ? myPluginDescriptor.getPluginClassLoader() : getClass().getClassLoader();
return Class.forName(className, true, classLoader);
}
catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
@Override
public Object getComponentInstance(@NotNull PicoContainer container) throws PicoInitializationException, PicoIntrospectionException {
Object instance = myInitializedComponentInstance;
if (instance != null) {
return instance;
}
synchronized (this) {
instance = myInitializedComponentInstance;
if (instance != null) {
// DCL is fine, field is volatile
return instance;
}
ComponentAdapter delegate = getDelegate();
if (LOGGER.isDebugEnabled() &&
ApplicationManager.getApplication().isWriteAccessAllowed() &&
!ApplicationManager.getApplication().isUnitTestMode() &&
PersistentStateComponent.class.isAssignableFrom(delegate.getComponentImplementation())) {
LOGGER.warn(
new Throwable("Getting service from write-action leads to possible deadlock. Service implementation " + myDescriptor.getImplementation()));
}
// prevent storages from flushing and blocking FS
AccessToken token = HeavyProcessLatch.INSTANCE.processStarted("Creating component '" + myDescriptor.getImplementation() + "'");
try {
instance = delegate.getComponentInstance(container);
if (instance instanceof Disposable) {
Disposer.register(myComponentManager, (Disposable)instance);
}
myComponentManager.initializeComponent(instance, true);
myInitializedComponentInstance = instance;
return instance;
}
finally {
token.finish();
}
}
}
@NotNull
private synchronized ComponentAdapter getDelegate() {
if (myDelegate == null) {
Class<?> implClass;
try {
ClassLoader classLoader = myPluginDescriptor != null ? myPluginDescriptor.getPluginClassLoader() : getClass().getClassLoader();
implClass = Class.forName(myDescriptor.getImplementation(), true, classLoader);
}
catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
myDelegate = new CachingConstructorInjectionComponentAdapter(getComponentKey(), implClass, null, true);
}
return myDelegate;
}
@Override
public void verify(final PicoContainer container) throws PicoIntrospectionException {
getDelegate().verify(container);
}
@Override
public void accept(final PicoVisitor visitor) {
visitor.visitComponentAdapter(this);
}
@Override
public String getAssignableToClassName() {
return myDescriptor.getInterface();
}
@Override
public String toString() {
return "ServiceComponentAdapter[" +
myDescriptor.getInterface() +
"]: implementation=" +
myDescriptor.getImplementation() +
", plugin=" +
myPluginDescriptor;
}
}
}