/* * 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 org.wso2.siddhi.core.util; import org.apache.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 org.wso2.siddhi.annotation.Extension; import java.util.HashMap; import java.util.Map; /** * Class used to load Siddhi extensions */ public class SiddhiExtensionLoader { private static final Logger log = Logger.getLogger(SiddhiExtensionLoader.class); /** * Helper method to load the Siddhi extensions * * @param siddhiExtensionsMap reference map for the Siddhi extension */ public static void loadSiddhiExtensions(Map<String, Class> siddhiExtensionsMap) { loadLocalExtensions(siddhiExtensionsMap); BundleContext bundleContext = SiddhiManagerComponent.getBundleContext(); if (bundleContext != null) { loadExtensionOSGI(bundleContext, siddhiExtensionsMap); } } /** * Load Extensions in OSGi environment * * @param bundleContext OSGi bundleContext * @param siddhiExtensionsMap reference map for the Siddhi extension */ private static void loadExtensionOSGI(BundleContext bundleContext, Map<String, Class> siddhiExtensionsMap) { ExtensionBundleListener extensionBundleListener = new ExtensionBundleListener(siddhiExtensionsMap); bundleContext.addBundleListener(extensionBundleListener); extensionBundleListener.loadAllExtensions(bundleContext); } /** * Load Siddhi extensions in java non OSGi environment * * @param siddhiExtensionsMap reference map for the Siddhi extension */ private static void loadLocalExtensions(Map<String, Class> siddhiExtensionsMap) { Iterable<Class<?>> extensions = ClassIndex.getAnnotated(Extension.class); for (Class extension : extensions) { addExtensionToMap(extension, siddhiExtensionsMap); } } /** * Adding extensions to Siddhi siddhiExtensionsMap * * @param extensionClass extension class * @param siddhiExtensionsMap reference map for the Siddhi extension */ private static void addExtensionToMap(Class extensionClass, Map<String, Class> siddhiExtensionsMap) { Extension siddhiExtensionAnnotation = (Extension) extensionClass.getAnnotation(Extension.class); if (siddhiExtensionAnnotation != null) { if (!siddhiExtensionAnnotation.name().isEmpty()) { Class previousClass; if (!siddhiExtensionAnnotation.namespace().isEmpty()) { previousClass = siddhiExtensionsMap.putIfAbsent(siddhiExtensionAnnotation.namespace() + SiddhiConstants.EXTENSION_SEPARATOR + siddhiExtensionAnnotation.name(), extensionClass); if (previousClass != null) { log.warn("Dropping extension '" + extensionClass + "' as '" + previousClass + "' was already " + "loaded with the same namespace and name '" + siddhiExtensionAnnotation.namespace() + SiddhiConstants.EXTENSION_SEPARATOR + siddhiExtensionAnnotation.name() + "'"); } } else { previousClass = siddhiExtensionsMap.put(siddhiExtensionAnnotation.name(), extensionClass); if (previousClass != null) { log.warn("Dropping extension '" + extensionClass + "' as '" + previousClass + "' was already " + "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."); } } /** * Class to listen to Bundle changes to update available extensions */ private static class ExtensionBundleListener implements BundleListener { private Map<Class, Integer> bundleExtensions = new HashMap<Class, Integer>(); private Map<String, Class> siddhiExtensionsMap; ExtensionBundleListener(Map<String, Class> siddhiExtensionsMap) { this.siddhiExtensionsMap = siddhiExtensionsMap; } @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<Class<?>> extensions = ClassIndex.getAnnotated(Extension.class, classLoader); for (Class extension : extensions) { addExtensionToMap(extension, siddhiExtensionsMap); 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()); } void loadAllExtensions(BundleContext bundleContext) { for (Bundle b : bundleContext.getBundles()) { if (b.getState() == Bundle.ACTIVE) { addExtensions(b); } } } } }