package org.zstack.core.componentloader; import org.zstack.core.Platform; import org.zstack.core.componentloader.PluginDSL.ExtensionDefinition; import org.zstack.core.componentloader.PluginDSL.PluginDefinition; import org.zstack.header.exception.CloudRuntimeException; import org.zstack.utils.DebugUtils; import org.zstack.utils.function.Function; import org.zstack.utils.logging.CLogger; import org.zstack.utils.logging.CLoggerImpl; import java.util.*; import java.util.concurrent.ConcurrentHashMap; public class PluginRegistryImpl implements PluginRegistryIN { private static final CLogger logger = CLoggerImpl.getLogger(PluginRegistryImpl.class); private Map<String, List<PluginExtension>> extensions = new HashMap<>(); private Map<String, List<PluginExtension>> extensionsByInterfaceName = new HashMap<>(); private Map<Class, List> extensionsByInterfaceClass = new HashMap<>(); private Map<Class, Map<Object, Object>> extensionAsMap = new HashMap<>(); private Map<Class, Map<Object, List>> extensionListAsMap = new HashMap<>(); private void sortPlugins() { for (List<PluginExtension> exts : extensionsByInterfaceName.values()) { Collections.sort(exts, new Comparator<PluginExtension>() { @Override public int compare(PluginExtension o1, PluginExtension o2) { // greater order means the position is more proceeding in plugin list return o2.getOrder() - o1.getOrder(); } }); } } private void buildPluginTree() { ComponentLoader loader = Platform.getComponentLoader(); for (Map.Entry<String, List<PluginExtension>> entry : extensions.entrySet()) { for (PluginExtension ext : entry.getValue()) { /* * If instance-id is specified in extension declaration, find * that bean used as extension implementation. Otherwise, use * parent plugin as implementation */ try { Class<?> interfaceClass = Class.forName(ext.getReferenceInterface()); Object instance; if (!"".equals(ext.getInstanceId())) { instance = loader.getComponentByBeanName(ext.getInstanceId()); } else { instance = loader.getComponentByBeanName(ext.getBeanName()); } ext.setInstance(instance); if (!interfaceClass.isInstance(ext.getInstance())) { throw new IllegalArgumentException(String.format("%s is not an instance of the interface %s", ext.getInstance().getClass().getCanonicalName(), interfaceClass.getName())); } List<PluginExtension> exts = extensionsByInterfaceName.get(ext.getReferenceInterface()); if (exts == null) { exts = new ArrayList<>(1); } exts.add(ext); extensionsByInterfaceName.put(ext.getReferenceInterface(), exts); } catch (Exception e) { throw new CloudRuntimeException( String.format("%s, mark extension referred to interface [%s] in bean[name=%s, class=%s] as invalid." + " Checking the bean XML file to fix it", e.getMessage(), ext.getReferenceInterface(), ext.getBeanName(), ext.getBeanClassName()), e); } } } } @Override public void initialize() { buildPluginTree(); continueBuildTreeFromDSL(); sortPlugins(); createClassPluginInstanceMap(); logger.info("Plugin system has been initialized successfully"); } private void continueBuildTreeFromDSL() { for (Map.Entry<Class, PluginDefinition> e : PluginDSL.getPluginDefinition().entrySet()) { Class beanClass = e.getKey(); PluginDefinition definition = e.getValue(); ComponentLoader loader = Platform.getComponentLoader(); Object instance = loader.getComponent(beanClass); for (ExtensionDefinition extd : definition.extensions) { String ifaceName = extd.interfaceClass.getName(); PluginExtension ext = new PluginExtension(); ext.setInstance(instance); ext.setBeanClassName(instance.getClass().getName()); ext.setOrder(extd.order); ext.setReferenceInterface(extd.interfaceClass.getName()); ext.setAttributes(extd.attributes); if (!extd.interfaceClass.isInstance(ext.getInstance())) { throw new IllegalArgumentException( String.format("%s is not an instance of the interface %s", ext.getInstance().getClass().getCanonicalName(), extd.interfaceClass.getName())); } List<PluginExtension> exts = extensionsByInterfaceName.get(ifaceName); if (exts == null) { exts = new ArrayList<>(); extensionsByInterfaceName.put(ifaceName, exts); } logger.debug(String.format("Plugin[%s] declares an extension[%s] from static DSL", beanClass.getName(), extd.interfaceClass.getName())); exts.add(ext); } } } private void createClassPluginInstanceMap() { for (Map.Entry<String, List<PluginExtension>> e : extensionsByInterfaceName.entrySet()) { String className = e.getKey(); List<PluginExtension> exts = e.getValue(); try { Class clazz = Class.forName(className); List instances = new ArrayList(); for (PluginExtension ext : exts) { if (!instances.contains(ext.getInstance())) { instances.add(ext.getInstance()); } } extensionsByInterfaceClass.put(clazz, instances); } catch (Exception ex) { throw new CloudRuntimeException(ex); } } } @Override public <T> List<T> getExtensionList(Class<T> clazz) { List<T> exts = extensionsByInterfaceClass.get(clazz); return exts == null ? new ArrayList<>() : exts; } @Override public <T, K> void saveExtensionAsMap(Class<T> clazz, Function<K, T> func) { List<T> exts = getExtensionList(clazz); Map<Object, Object> m = new HashMap<>(); for (T ext : exts) { K key = func.call(ext); DebugUtils.Assert(key != null, "key cannot be null"); m.put(key, ext); } extensionAsMap.put(clazz, m); } @Override public <T> T getExtensionFromMap(Object key, Class<T> clazz) { Map<Object, Object> m = extensionAsMap.get(clazz); if (m == null) { return null; } return (T) m.get(key); } @Override public <T, K> void saveExtensionListAsMap(Class<T> clazz, Function<K, T> func) { List<T> exts = getExtensionList(clazz); Map<Object, List> m = new HashMap<>(); for (T ext : exts) { K key = func.call(ext); DebugUtils.Assert(key != null, "key cannot be null"); List lst = m.get(key); if (lst == null) { lst = new ArrayList(); m.put(key, lst); } lst.add(ext); } extensionListAsMap.put(clazz, m); } @Override public <T> List getExtensionListFromMap(Object key, Class<T> clazz) { Map<Object, List> m = extensionListAsMap.get(clazz); if (m == null) { return new ArrayList(); } return m.get(key); } @Override public void defineDynamicExtension(Class interfaceClass, Object instance) { List exts = extensionsByInterfaceClass.computeIfAbsent(interfaceClass, k -> new ArrayList()); exts.add(instance); } @Override public List<PluginExtension> getExtensionByInterfaceName(String interfaceName) { List<PluginExtension> exts = extensionsByInterfaceName.get(interfaceName); if (exts == null) { exts = new ArrayList<>(0); } return exts; } public void setExtensions(Map<String, List<PluginExtension>> extensions) { this.extensions = extensions; } }