/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 org.apache.ignite.internal.processors.plugin; import java.io.Serializable; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.UUID; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.IgniteException; import org.apache.ignite.configuration.IgniteConfiguration; import org.apache.ignite.internal.GridKernalContext; import org.apache.ignite.internal.GridPluginContext; import org.apache.ignite.internal.processors.GridProcessorAdapter; import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.plugin.Extension; import org.apache.ignite.plugin.ExtensionRegistry; import org.apache.ignite.plugin.PluginContext; import org.apache.ignite.plugin.PluginProvider; import org.apache.ignite.spi.discovery.DiscoveryDataBag; import org.apache.ignite.spi.discovery.DiscoveryDataBag.GridDiscoveryData; import org.apache.ignite.spi.discovery.DiscoveryDataBag.JoiningNodeDiscoveryData; import org.jetbrains.annotations.Nullable; import static org.apache.ignite.internal.GridComponent.DiscoveryDataExchangeType.PLUGIN; /** * */ public class IgnitePluginProcessor extends GridProcessorAdapter { /** */ private final Map<String, PluginProvider> plugins = new LinkedHashMap<>(); /** */ private final Map<PluginProvider, GridPluginContext> pluginCtxMap = new IdentityHashMap<>(); /** */ private volatile Map<Class<?>, Object[]> extensions; /** * * @param ctx Kernal context. * @param cfg Ignite configuration. * @param providers Plugin providers. */ @SuppressWarnings("TypeMayBeWeakened") public IgnitePluginProcessor(GridKernalContext ctx, IgniteConfiguration cfg, List<PluginProvider> providers) throws IgniteCheckedException { super(ctx); ExtensionRegistryImpl registry = new ExtensionRegistryImpl(); for (PluginProvider provider : providers) { GridPluginContext pluginCtx = new GridPluginContext(ctx, cfg); if (F.isEmpty(provider.name())) throw new IgniteException("Plugin name can not be empty."); if (plugins.containsKey(provider.name())) throw new IgniteException("Duplicated plugin name: " + provider.name()); plugins.put(provider.name(), provider); pluginCtxMap.put(provider, pluginCtx); provider.initExtensions(pluginCtx, registry); if (provider.plugin() == null) throw new IgniteException("Plugin is null."); } extensions = registry.createExtensionMap(); } /** * @param extensionItf Extension interface class. * @return Returns implementation for provided extension from all plugins. */ @Nullable public <T extends Extension> T[] extensions(Class<T> extensionItf) { Map<Class<?>, Object[]> extensions = this.extensions; return (T[])extensions.get(extensionItf); } /** * @param name Plugin name. * @return Plugin provider. */ @SuppressWarnings("unchecked") @Nullable public <T extends PluginProvider> T pluginProvider(String name) { return (T)plugins.get(name); } /** * @return All plugin providers. */ public Collection<PluginProvider> allProviders() { return plugins.values(); } /** * @param provider Plugin context. * @return Plugin context. */ @SuppressWarnings("unchecked") public <T extends PluginContext> T pluginContextForProvider(PluginProvider provider) { return (T)pluginCtxMap.get(provider); } /** * @param cls Component class. * @param <T> Component type. * @return Component class instance or {@code null} if no one plugin override this component. */ public <T> T createComponent(Class<T> cls) { for (PluginProvider plugin : plugins.values()) { PluginContext ctx = pluginContextForProvider(plugin); T comp = (T)plugin.createComponent(ctx, cls); if (comp != null) return comp; } return null; } /** {@inheritDoc} */ @Override public void start(boolean activeOnStart) throws IgniteCheckedException { ackPluginsInfo(); } /** {@inheritDoc} */ @Nullable @Override public DiscoveryDataExchangeType discoveryDataType() { return PLUGIN; } /** {@inheritDoc} */ @Override public void collectJoiningNodeData(DiscoveryDataBag dataBag) { Serializable pluginsData = getDiscoveryData(dataBag.joiningNodeId()); if (pluginsData != null) dataBag.addJoiningNodeData(PLUGIN.ordinal(), pluginsData); } /** {@inheritDoc} */ @Override public void collectGridNodeData(DiscoveryDataBag dataBag) { Serializable pluginsData = getDiscoveryData(dataBag.joiningNodeId()); if (pluginsData != null) dataBag.addNodeSpecificData(PLUGIN.ordinal(), pluginsData); } /** * @param joiningNodeId Joining node id. */ private Serializable getDiscoveryData(UUID joiningNodeId) { HashMap<String, Serializable> pluginsData = null; for (Map.Entry<String, PluginProvider> e : plugins.entrySet()) { Serializable data = e.getValue().provideDiscoveryData(joiningNodeId); if (data != null) { if (pluginsData == null) pluginsData = new HashMap<>(); pluginsData.put(e.getKey(), data); } } return pluginsData; } /** {@inheritDoc} */ @Override public void onJoiningNodeDataReceived(JoiningNodeDiscoveryData data) { if (data.hasJoiningNodeData()) { Map<String, Serializable> pluginsData = (Map<String, Serializable>) data.joiningNodeData(); applyPluginsData(data.joiningNodeId(), pluginsData); } } /** {@inheritDoc} */ @Override public void onGridDataReceived(GridDiscoveryData data) { Map<UUID, Serializable> nodeSpecificData = data.nodeSpecificData(); if (nodeSpecificData != null) { UUID joiningNodeId = data.joiningNodeId(); for (Serializable v : nodeSpecificData.values()) { if (v != null) { Map<String, Serializable> pluginsData = (Map<String, Serializable>) v; applyPluginsData(joiningNodeId, pluginsData); } } } } /** * @param nodeId Node id. * @param pluginsData Plugins data. */ private void applyPluginsData(UUID nodeId, Map<String, Serializable> pluginsData) { for (Map.Entry<String, Serializable> e : pluginsData.entrySet()) { PluginProvider provider = plugins.get(e.getKey()); if (provider != null) provider.receiveDiscoveryData(nodeId, e.getValue()); else U.warn(log, "Received discovery data for unknown plugin: " + e.getKey()); } } /** * Print plugins information. */ private void ackPluginsInfo() { U.quietAndInfo(log, "Configured plugins:"); if (plugins.isEmpty()) { U.quietAndInfo(log, " ^-- None"); U.quietAndInfo(log, ""); } else { for (PluginProvider plugin : plugins.values()) { U.quietAndInfo(log, " ^-- " + plugin.name() + " " + plugin.version()); U.quietAndInfo(log, " ^-- " + plugin.copyright()); U.quietAndInfo(log, ""); } } } /** * */ private static class ExtensionRegistryImpl implements ExtensionRegistry { /** */ private final Map<Class<?>, List<Object>> extensionsCollector = new HashMap<>(); /** {@inheritDoc} */ @Override public <T extends Extension> void registerExtension(Class<T> extensionItf, T extensionImpl) { List<Object> list = extensionsCollector.get(extensionItf); if (list == null) { list = new ArrayList<>(); extensionsCollector.put(extensionItf, list); } list.add(extensionImpl); } /** * @return Map extension interface to array of implementation. */ Map<Class<?>, Object[]> createExtensionMap() { Map<Class<?>, Object[]> extensions = new HashMap<>(extensionsCollector.size() * 2, 0.5f); for (Map.Entry<Class<?>, List<Object>> entry : extensionsCollector.entrySet()) { Class<?> extensionItf = entry.getKey(); List<Object> implementations = entry.getValue(); Object[] implArr = (Object[])Array.newInstance(extensionItf, implementations.size()); implArr = implementations.toArray(implArr); extensions.put(extensionItf, implArr); } return extensions; } } }