All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.siddhi.core.util.SiddhiExtensionLoader Maven / Gradle / Ivy

/*
 * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * WSO2 Inc. 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 io.siddhi.core.util;

import io.siddhi.annotation.Extension;
import io.siddhi.core.executor.function.FunctionExecutor;
import io.siddhi.core.executor.incremental.IncrementalAggregateBaseTimeFunctionExecutor;
import io.siddhi.core.executor.incremental.IncrementalShouldUpdateFunctionExecutor;
import io.siddhi.core.executor.incremental.IncrementalStartTimeEndTimeFunctionExecutor;
import io.siddhi.core.executor.incremental.IncrementalTimeGetTimeZone;
import io.siddhi.core.executor.incremental.IncrementalUnixTimeFunctionExecutor;
import io.siddhi.core.function.Script;
import io.siddhi.core.query.processor.stream.StreamProcessor;
import io.siddhi.core.query.processor.stream.function.StreamFunctionProcessor;
import io.siddhi.core.query.processor.stream.window.WindowProcessor;
import io.siddhi.core.query.selector.attribute.aggregator.AttributeAggregatorExecutor;
import io.siddhi.core.query.selector.attribute.aggregator.incremental.IncrementalAttributeAggregator;
import io.siddhi.core.stream.input.source.Source;
import io.siddhi.core.stream.input.source.SourceMapper;
import io.siddhi.core.stream.output.sink.Sink;
import io.siddhi.core.stream.output.sink.SinkMapper;
import io.siddhi.core.stream.output.sink.distributed.DistributionStrategy;
import io.siddhi.core.table.Table;
import io.siddhi.core.util.extension.holder.AbstractExtensionHolder;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.atteo.classindex.ClassIndex;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleListener;
import org.osgi.framework.wiring.BundleWiring;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Class used to load Siddhi extensions.
 */
public class SiddhiExtensionLoader {

    private static final Logger log = LogManager.getLogger(SiddhiExtensionLoader.class);
    private static final Class ATTRIBUTE_AGGREGATOR_EXECUTOR_CLASS = AttributeAggregatorExecutor.class;
    private static final Class DISTRIBUTION_STRATEGY_CLASS = DistributionStrategy.class;
    private static final Class FUNCTION_EXECUTOR_CLASS = FunctionExecutor.class;
    private static final Class INCREMENTAL_ATTRIBUTE_AGGREGATOR_CLASS = IncrementalAttributeAggregator.class;
    private static final Class SCRIPT_CLASS = Script.class;
    private static final Class SINK_CLASS = Sink.class;
    private static final Class SINK_MAPPER_CLASS = SinkMapper.class;
    private static final Class SOURCE_CLASS = Source.class;
    private static final Class SOURCE_MAPPER_CLASS = SourceMapper.class;
    private static final Class STREAM_FUNCTION_PROCESSOR_CLASS = StreamFunctionProcessor.class;
    private static final Class STREAM_PROCESSOR_CLASS = StreamProcessor.class;
    private static final Class TABLE_CLASS = Table.class;
    private static final Class WINDOW_PROCESSOR_CLASS = WindowProcessor.class;
    private static List extensionNameSpaceList = new ArrayList<>();

    static {
        extensionNameSpaceList.add(DISTRIBUTION_STRATEGY_CLASS);
        extensionNameSpaceList.add(SCRIPT_CLASS);
        extensionNameSpaceList.add(SINK_CLASS);
        extensionNameSpaceList.add(SINK_MAPPER_CLASS);
        extensionNameSpaceList.add(SOURCE_CLASS);
        extensionNameSpaceList.add(SOURCE_MAPPER_CLASS);
        extensionNameSpaceList.add(TABLE_CLASS);
        extensionNameSpaceList.add(ATTRIBUTE_AGGREGATOR_EXECUTOR_CLASS);
        extensionNameSpaceList.add(FUNCTION_EXECUTOR_CLASS);
        extensionNameSpaceList.add(INCREMENTAL_ATTRIBUTE_AGGREGATOR_CLASS);
        extensionNameSpaceList.add(STREAM_FUNCTION_PROCESSOR_CLASS);
        extensionNameSpaceList.add(STREAM_PROCESSOR_CLASS);
        extensionNameSpaceList.add(WINDOW_PROCESSOR_CLASS);
    }

    /**
     * Helper method to load the Siddhi extensions.
     *
     * @param siddhiExtensionsMap           reference map for the Siddhi extension
     * @param extensionHolderMap            reference map for the Siddhi extension holder
     * @param deprecatedSiddhiExtensionsMap reference map for the deprecated Siddhi extensions
     */
    public static void loadSiddhiExtensions(Map siddhiExtensionsMap,
                                            ConcurrentHashMap extensionHolderMap,
                                            Map deprecatedSiddhiExtensionsMap) {
        loadLocalExtensions(siddhiExtensionsMap, extensionHolderMap, deprecatedSiddhiExtensionsMap);
        BundleContext bundleContext = ReferenceHolder.getInstance().getBundleContext();
        if (bundleContext != null) {
            loadExtensionOSGI(bundleContext, siddhiExtensionsMap, extensionHolderMap, deprecatedSiddhiExtensionsMap);
        }
    }

    /**
     * Load Extensions in OSGi environment.
     *
     * @param bundleContext                 OSGi bundleContext
     * @param siddhiExtensionsMap           reference map for the Siddhi extension
     * @param extensionHolderMap            reference map for the Siddhi extension holder
     * @param deprecatedSiddhiExtensionsMap reference map for the deprecated Siddhi extensions
     */
    private static void loadExtensionOSGI(BundleContext bundleContext,
                                          Map siddhiExtensionsMap,
                                          ConcurrentHashMap extensionHolderMap,
                                          Map deprecatedSiddhiExtensionsMap) {
        ExtensionBundleListener extensionBundleListener
                = new ExtensionBundleListener(siddhiExtensionsMap, extensionHolderMap, deprecatedSiddhiExtensionsMap);
        bundleContext.addBundleListener(extensionBundleListener);
        extensionBundleListener.loadAllExtensions(bundleContext);
    }

    /**
     * Load Siddhi extensions in java non OSGi environment.
     *
     * @param siddhiExtensionsMap           reference map for the Siddhi extension
     * @param extensionHolderMap            reference map for the Siddhi extension holder
     * @param deprecatedSiddhiExtensionsMap reference map for the deprecated Siddhi extensions
     */
    private static void loadLocalExtensions(Map siddhiExtensionsMap,
                                            ConcurrentHashMap extensionHolderMap,
                                            Map deprecatedSiddhiExtensionsMap) {
        Iterable> extensions = ClassIndex.getAnnotated(Extension.class);
        for (Class extension : extensions) {
            addExtensionToMap(extension, siddhiExtensionsMap, extensionHolderMap, deprecatedSiddhiExtensionsMap);
        }

        // load extensions related to incremental aggregation
        addExtensionToMap("incrementalAggregator:startTimeEndTime",
                IncrementalStartTimeEndTimeFunctionExecutor.class, siddhiExtensionsMap);
        addExtensionToMap("incrementalAggregator:timestampInMilliseconds",
                IncrementalUnixTimeFunctionExecutor.class, siddhiExtensionsMap);
        addExtensionToMap("incrementalAggregator:getTimeZone",
                IncrementalTimeGetTimeZone.class, siddhiExtensionsMap);
        addExtensionToMap("incrementalAggregator:getAggregationStartTime",
                IncrementalAggregateBaseTimeFunctionExecutor.class, siddhiExtensionsMap);
        addExtensionToMap("incrementalAggregator:shouldUpdate",
                IncrementalShouldUpdateFunctionExecutor.class, siddhiExtensionsMap);
    }

    /**
     * Adding extensions to Siddhi siddhiExtensionsMap.
     *
     * @param extensionClass                extension class
     * @param siddhiExtensionsMap           reference map for the Siddhi extension
     * @param extensionHolderMap            reference map for the Siddhi extension holder
     * @param deprecatedSiddhiExtensionsMap reference map for the deprecated Siddhi extensions
     */
    private static void addExtensionToMap(Class extensionClass, Map siddhiExtensionsMap,
                                          ConcurrentHashMap extensionHolderMap,
                                          Map deprecatedSiddhiExtensionsMap) {
        Extension siddhiExtensionAnnotation = (Extension) extensionClass.getAnnotation(Extension.class);
        if (siddhiExtensionAnnotation != null) {
            if (!siddhiExtensionAnnotation.name().isEmpty()) {
                Class previousClass = null;
                if (!siddhiExtensionAnnotation.namespace().isEmpty()) {
                    String key = siddhiExtensionAnnotation.namespace() + SiddhiConstants.EXTENSION_SEPARATOR +
                            siddhiExtensionAnnotation.name();
                    Class existingValue = siddhiExtensionsMap.get(key);
                    if (existingValue == null) {
                        previousClass = siddhiExtensionsMap.put(key, extensionClass);
                        if (siddhiExtensionAnnotation.deprecated()) {
                            deprecatedSiddhiExtensionsMap.put(key, extensionClass);
                        }
                        for (Class clazz : extensionNameSpaceList) {
                            putToExtensionHolderMap(clazz, extensionClass, key,
                                    extensionHolderMap);
                        }
                    }
                    if (previousClass != null) {
                        log.warn("Dropping extension '" + previousClass + "' as '" + extensionClass + "' is " +
                                "loaded with the same namespace and name '" +
                                siddhiExtensionAnnotation.namespace() + SiddhiConstants.EXTENSION_SEPARATOR +
                                siddhiExtensionAnnotation.name() + "'");
                    }
                } else {
                    previousClass = siddhiExtensionsMap.put(siddhiExtensionAnnotation.name(), extensionClass);
                    if (siddhiExtensionAnnotation.deprecated()) {
                        deprecatedSiddhiExtensionsMap.put(siddhiExtensionAnnotation.name(), extensionClass);
                    }
                    if (previousClass != null) {
                        log.warn("Dropping extension '" + previousClass + "' as '" + extensionClass + "' is " +
                                "loaded with the same name '" + siddhiExtensionAnnotation.name() + "'");
                    }
                }
            } else {
                log.error("Unable to load extension " + extensionClass.getName() + ", missing Extension annotation.");
            }
        } else {
            log.error("Unable to load extension " + extensionClass.getName() + ", empty name element given in " +
                    "Extension annotation.");
        }
    }

    /**
     * Adding extensions to Siddhi siddhiExtensionsHolderMap.
     *
     * @param extensionSuperClass              extension super class (eg:Source)
     * @param extension                        extension class (eg:HttpSource)
     * @param extensionKey                     fully qualified extension name (namespace:extensionName)
     * @param extensionHolderConcurrentHashMap reference map for the Siddhi extension holder
     */
    private static void putToExtensionHolderMap(
            Class extensionSuperClass, Class extension, String extensionKey,
            ConcurrentHashMap extensionHolderConcurrentHashMap) {
        if (extensionSuperClass.isAssignableFrom(extension)) {
            AbstractExtensionHolder extensionHolder = extensionHolderConcurrentHashMap.get(extensionSuperClass);
            if (extensionHolder != null) {
                if (extensionHolder.getExtension(extensionKey) != null) {
                    log.error("Extension class " + extension.getName() + " not loaded, as there is already an" +
                            " matching extension '" + extensionKey + "' implemented as " + extensionHolder
                            .getExtension(extensionKey).getName());
                } else {
                    extensionHolder.addExtension(extensionKey, extension);
                }
            }
        }
    }

    /**
     * Remove extensions to Siddhi siddhiExtensionsHolderMap.
     *
     * @param extensionKey                     fully qualified extension name (namespace:extensionName)
     * @param extension                        extension class (eg:HttpSource)
     * @param extensionHolderConcurrentHashMap reference map for the Siddhi extension holder
     */
    private static void removeFromExtensionHolderMap(
            String extensionKey,
            Class extension,
            ConcurrentHashMap extensionHolderConcurrentHashMap) {
        for (Class extensionSuperClass : extensionNameSpaceList) {
            if (extensionSuperClass.isAssignableFrom(extension)) {
                AbstractExtensionHolder extensionHolder = extensionHolderConcurrentHashMap.get(extensionSuperClass);
                if (extensionHolder != null) {
                    extensionHolder.removeExtension(extensionKey);
                }
            }
        }
    }

    /**
     * Adding extensions to Siddhi siddhiExtensionsMap.
     *
     * @param fqExtensionName     fully qualified extension name (namespace:extensionName or extensionName)
     * @param extensionClass      extension class
     * @param siddhiExtensionsMap reference map for the Siddhi extension
     */
    private static void addExtensionToMap(String fqExtensionName, Class extensionClass,
                                          Map siddhiExtensionsMap) {
        Class previousClass = null;
        Class existingValue = siddhiExtensionsMap.get(fqExtensionName);
        if (existingValue == null) {
            previousClass = siddhiExtensionsMap.put(fqExtensionName, extensionClass);
        }
        if (previousClass != null) {
            log.warn("Dropping extension '" + extensionClass + "' as '" + previousClass + "' was already " +
                    "loaded with the same namespace and name '" + fqExtensionName + "'");
        }
    }

    /**
     * Class to listen to Bundle changes to update available extensions.
     */
    private static class ExtensionBundleListener implements BundleListener {

        private Map bundleExtensions = new HashMap();
        private Map siddhiExtensionsMap;
        private Map deprecatedSiddhiExtensionsMap;
        private ConcurrentHashMap extensionHolderConcurrentHashMap;

        ExtensionBundleListener(Map siddhiExtensionsMap,
                                ConcurrentHashMap extensionHolderConcurrentHashMap,
                                Map deprecatedSiddhiExtensionsMap) {
            this.siddhiExtensionsMap = siddhiExtensionsMap;
            this.extensionHolderConcurrentHashMap = extensionHolderConcurrentHashMap;
            this.deprecatedSiddhiExtensionsMap = deprecatedSiddhiExtensionsMap;
        }

        @Override
        public void bundleChanged(BundleEvent bundleEvent) {
            if (bundleEvent.getType() == BundleEvent.STARTED) {
                addExtensions(bundleEvent.getBundle());
            } else {
                removeExtensions(bundleEvent.getBundle());
            }
        }

        private void addExtensions(Bundle bundle) {
            ClassLoader classLoader = bundle.adapt(BundleWiring.class).getClassLoader();
            Iterable> extensions = ClassIndex.getAnnotated(Extension.class, classLoader);
            for (Class extension : extensions) {
                addExtensionToMap(extension, siddhiExtensionsMap,
                        extensionHolderConcurrentHashMap, deprecatedSiddhiExtensionsMap);
                bundleExtensions.put(extension, (int) bundle.getBundleId());
            }
        }

        private void removeExtensions(Bundle bundle) {
            bundleExtensions.entrySet().stream().filter(entry -> entry.getValue() ==
                    bundle.getBundleId()).forEachOrdered(entry -> {
                siddhiExtensionsMap.remove(entry.getKey());
            });
            bundleExtensions.entrySet().removeIf(entry -> entry.getValue() ==
                    bundle.getBundleId());
            BundleWiring bundleWiring = bundle.adapt(BundleWiring.class);
            if (bundleWiring != null) {
                ClassLoader classLoader = bundleWiring.getClassLoader();
                Iterable> extensions = ClassIndex.getAnnotated(Extension.class, classLoader);
                for (Class extension : extensions) {
                    Extension siddhiExtensionAnnotation = (Extension) extension.getAnnotation(Extension.class);
                    if (!siddhiExtensionAnnotation.namespace().isEmpty()) {
                        String key = siddhiExtensionAnnotation.namespace() + SiddhiConstants.EXTENSION_SEPARATOR +
                                siddhiExtensionAnnotation.name();
                        removeFromExtensionHolderMap(key, extension, extensionHolderConcurrentHashMap);
                    }
                }
            }
        }

        void loadAllExtensions(BundleContext bundleContext) {
            for (Bundle b : bundleContext.getBundles()) {
                if (b.getState() == Bundle.ACTIVE) {
                    addExtensions(b);
                }
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy