[ https://issues.apache.org/jira/browse/METRON-777?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16126079#comment-16126079 ]
ASF GitHub Bot commented on METRON-777: --------------------------------------- Github user mattf-horton commented on a diff in the pull request: https://github.com/apache/metron/pull/530#discussion_r133016174 --- Diff: bundles-lib/src/main/java/org/apache/metron/bundles/ExtensionManager.java --- @@ -0,0 +1,436 @@ +/* + * 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.metron.bundles; + + +import org.apache.commons.vfs2.FileObject; +import org.apache.commons.vfs2.FileSystemException; +import org.apache.commons.vfs2.FileSystemManager; +import org.apache.metron.bundles.bundle.Bundle; +import org.apache.metron.bundles.bundle.BundleCoordinates; +import org.apache.metron.bundles.bundle.BundleDetails; +import org.apache.metron.bundles.util.BundleProperties; +import org.apache.metron.bundles.util.FileUtils; +import org.apache.metron.bundles.util.StringUtils; +import org.apache.metron.bundles.annotation.behavior.RequiresInstanceClassLoading; + +import org.atteo.classindex.ClassIndex; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.lang.reflect.Modifier; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Scans through the classpath to load all extension components using the service provider API and + * running through all classloaders (root, BUNDLEs). + * + * @ThreadSafe - is immutable + */ +@SuppressWarnings("rawtypes") +public class ExtensionManager { + + private static final Logger logger = LoggerFactory.getLogger(ExtensionManager.class); + + public static final BundleCoordinates SYSTEM_BUNDLE_COORDINATE = new BundleCoordinates( + BundleCoordinates.DEFAULT_GROUP, "system", BundleCoordinates.DEFAULT_VERSION); + + // Maps a service definition (interface) to those classes that implement the interface + private static final Map<Class, Set<Class>> definitionMap = new HashMap<>(); + + private static final Map<String, List<Bundle>> classNameBundleLookup = new HashMap<>(); + private static final Map<BundleCoordinates, Bundle> bundleCoordinateBundleLookup = new HashMap<>(); + private static final Map<ClassLoader, Bundle> classLoaderBundleLookup = new HashMap<>(); + + private static final Set<String> requiresInstanceClassLoading = new HashSet<>(); + private static final Map<String, ClassLoader> instanceClassloaderLookup = new ConcurrentHashMap<>(); + + private static AtomicBoolean inited = new AtomicBoolean(false); + + // should initialize class definitions + public static void initClassDefinitions(final List<Class> classes) { + if (classes != null) { + for (Class clazz : classes) { + definitionMap.put(clazz, new HashSet<>()); + } + } + inited.set(true); + } + + public static void resetClassDefinitions() { + definitionMap.clear(); + inited.set(false); + } + + /** + * Loads all extension class types that can be found on the bootstrap classloader and by creating + * classloaders for all BUNDLES found within the classpath. + * + * @param bundles the bundles to scan through in search of extensions + */ + public static void discoverExtensions(final Bundle systemBundle, final Set<Bundle> bundles) + throws NotInitializedException { + checkInitialized(); + // get the current context class loader + ClassLoader currentContextClassLoader = Thread.currentThread().getContextClassLoader(); + + // load the system bundle first so that any extensions found in JARs directly in lib will be registered as + // being from the system bundle and not from all the other Bundles + loadExtensions(systemBundle); + bundleCoordinateBundleLookup.put(systemBundle.getBundleDetails().getCoordinates(), systemBundle); + + // consider each bundle class loader + for (final Bundle bundle : bundles) { + // Must set the context class loader to the bundle classloader itself + // so that static initialization techniques that depend on the context class loader will work properly + final ClassLoader ncl = bundle.getClassLoader(); + Thread.currentThread().setContextClassLoader(ncl); + loadExtensions(bundle); + + // Create a look-up from withCoordinates to bundle + bundleCoordinateBundleLookup.put(bundle.getBundleDetails().getCoordinates(), bundle); + } + + // restore the current context class loader if appropriate + if (currentContextClassLoader != null) { + Thread.currentThread().setContextClassLoader(currentContextClassLoader); + } + } + + /** + * Returns a bundle representing the system class loader. + * + * @param bundleProperties a BundleProperties instance which will be used to obtain the default + * Bundle library path, which will become the working directory of the returned bundle + * @return a bundle for the system class loader + */ + public static Bundle createSystemBundle(final FileSystemManager fileSystemManager, + final BundleProperties bundleProperties) throws FileSystemException, URISyntaxException { + final ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); + + final String bundlesLibraryDirectory = bundleProperties + .getProperty(BundleProperties.BUNDLE_LIBRARY_DIRECTORY); + if (StringUtils.isBlank(bundlesLibraryDirectory)) { + throw new IllegalStateException( + "Unable to create system bundle because " + BundleProperties.BUNDLE_LIBRARY_DIRECTORY + + " was null or empty"); + } + final URI bundlesLibraryDirURI = bundleProperties.getBundleLibraryDirectory(); + FileObject bundleDir = fileSystemManager.resolveFile(bundlesLibraryDirURI); + + // Test if the source Bundles can be read + FileUtils.ensureDirectoryExistAndCanRead(bundleDir); + + final BundleDetails systemBundleDetails = new BundleDetails.Builder() + .withBundleFile(bundleDir) --- End diff -- Or perhaps just commenting. > Create a plugin system for Metron based on 'NAR' > ------------------------------------------------ > > Key: METRON-777 > URL: https://issues.apache.org/jira/browse/METRON-777 > Project: Metron > Issue Type: New Feature > Reporter: Otto Fowler > Assignee: Otto Fowler > > The success of the Metron project will be greatly dependent on community > participation, and with that the ability to adapt and extend Metron without > having to maintain a fork of the project. > As organizations and individuals look to extend the Metron system with custom > parsers, enrichments, and stellar functions that may be proprietary in > nature, the ability to develop and deploy these extensions outside the Metron > code base is critically important. > To that end, and after community discussion and proposal we create or > formalize the 'plugin' development story in Metron. > The proposal is to adapt the Apache Nifi NAR system for use in Metron. This > will provide the system with: > * archetype(s) for developer projects and independent development > * defined packaging and metadata for 'plugin' products > * loading and instantiation with classloader isolation capabilities > * removing the necessity for shading plugin jars > These capabilities will also enable other features, such as plugin lifecycle, > plugin configuration+redeployment, and other things. > The plugin archetypes and their installation will be a followon -- This message was sent by Atlassian JIRA (v6.4.14#64029)