You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@nifi.apache.org by bbende <gi...@git.apache.org> on 2017/10/24 14:58:18 UTC

[GitHub] nifi-registry pull request #27: NIFIREG-43 Provide configurable extension di...

GitHub user bbende opened a pull request:

    https://github.com/apache/nifi-registry/pull/27

    NIFIREG-43 Provide configurable extension directories with class load…

    …er isolation

You can merge this pull request into a Git repository by running:

    $ git pull https://github.com/bbende/nifi-registry NIFIREG-43

Alternatively you can review and apply these changes as the patch at:

    https://github.com/apache/nifi-registry/pull/27.patch

To close this pull request, make a commit to your master/trunk branch
with (at least) the following in the commit message:

    This closes #27
    
----
commit b9c7173b3b27bfd698644b0c9e2134020f68815c
Author: Bryan Bende <bb...@apache.org>
Date:   2017-10-23T20:08:49Z

    NIFIREG-43 Provide configurable extension directories with class loader isolation

----


---

[GitHub] nifi-registry pull request #27: NIFIREG-43 Provide configurable extension di...

Posted by asfgit <gi...@git.apache.org>.
Github user asfgit closed the pull request at:

    https://github.com/apache/nifi-registry/pull/27


---

[GitHub] nifi-registry issue #27: NIFIREG-43 Provide configurable extension directori...

Posted by bbende <gi...@git.apache.org>.
Github user bbende commented on the issue:

    https://github.com/apache/nifi-registry/pull/27
  
    Thanks, I'll change that string so its not called system anymore, and then I'll merge to master.


---

[GitHub] nifi-registry pull request #27: NIFIREG-43 Provide configurable extension di...

Posted by kevdoran <gi...@git.apache.org>.
Github user kevdoran commented on a diff in the pull request:

    https://github.com/apache/nifi-registry/pull/27#discussion_r146961424
  
    --- Diff: nifi-registry-framework/src/main/java/org/apache/nifi/registry/extension/ExtensionManager.java ---
    @@ -0,0 +1,213 @@
    +/*
    + * 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.nifi.registry.extension;
    +
    +import org.apache.commons.lang3.StringUtils;
    +import org.apache.nifi.registry.authorization.AccessPolicyProvider;
    +import org.apache.nifi.registry.authorization.Authorizer;
    +import org.apache.nifi.registry.authorization.UserGroupProvider;
    +import org.apache.nifi.registry.flow.FlowPersistenceProvider;
    +import org.apache.nifi.registry.properties.NiFiRegistryProperties;
    +import org.slf4j.Logger;
    +import org.slf4j.LoggerFactory;
    +import org.springframework.beans.factory.annotation.Autowired;
    +import org.springframework.stereotype.Component;
    +
    +import javax.annotation.PostConstruct;
    +import java.io.File;
    +import java.net.MalformedURLException;
    +import java.net.URL;
    +import java.net.URLClassLoader;
    +import java.util.ArrayList;
    +import java.util.Collections;
    +import java.util.HashMap;
    +import java.util.LinkedList;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.ServiceLoader;
    +import java.util.Set;
    +import java.util.concurrent.atomic.AtomicBoolean;
    +
    +@Component
    +public class ExtensionManager {
    +
    +    static final Logger LOGGER = LoggerFactory.getLogger(ExtensionManager.class);
    +
    +    private static final List<Class> EXTENSION_CLASSES;
    +    static {
    +        final List<Class> classes = new ArrayList<>();
    +        classes.add(FlowPersistenceProvider.class);
    +        classes.add(UserGroupProvider.class);
    +        classes.add(AccessPolicyProvider.class);
    +        classes.add(Authorizer.class);
    +        EXTENSION_CLASSES = Collections.unmodifiableList(classes);
    +    }
    +
    +    private final NiFiRegistryProperties properties;
    +    private final Map<String,ExtensionClassLoader> classLoaderMap = new HashMap<>();
    +    private final AtomicBoolean loaded = new AtomicBoolean(false);
    +
    +    @Autowired
    +    public ExtensionManager(final NiFiRegistryProperties properties) {
    +        this.properties = properties;
    +    }
    +
    +    @PostConstruct
    +    public synchronized void discoverExtensions() {
    +        if (!loaded.get()) {
    +            // get the list of class loaders to consider
    +            final List<ExtensionClassLoader> classLoaders = getClassLoaders();
    +
    +            // for each class loader, attempt to load each extension class using the ServiceLoader
    +            for (final ExtensionClassLoader extensionClassLoader : classLoaders) {
    +                for (final Class extensionClass : EXTENSION_CLASSES) {
    +                    loadExtensions(extensionClass, extensionClassLoader);
    +                }
    +            }
    +
    +            loaded.set(true);
    +        }
    +    }
    +
    +    public ClassLoader getExtensionClassLoader(final String canonicalClassName) {
    +        if (StringUtils.isBlank(canonicalClassName)) {
    +            throw new IllegalArgumentException("Class name can not be null");
    +        }
    +
    +        return classLoaderMap.get(canonicalClassName);
    +    }
    +
    +    /**
    +     * Loads implementations of the given extension class from the given class loader.
    +     *
    +     * @param extensionClass the extension/service class
    +     * @param extensionClassLoader the class loader to search
    +     */
    +    private void loadExtensions(final Class extensionClass, final ExtensionClassLoader extensionClassLoader) {
    +        final ServiceLoader<?> serviceLoader = ServiceLoader.load(extensionClass, extensionClassLoader);
    +        for (final Object o : serviceLoader) {
    +            final String extensionClassName = o.getClass().getCanonicalName();
    +            if (classLoaderMap.containsKey(extensionClassName)) {
    +                final String currDir = extensionClassLoader.getRootDir();
    +                final String existingDir = classLoaderMap.get(extensionClassName).getRootDir();
    +                LOGGER.warn("Skipping {} from {} which was already found in {}", new Object[]{extensionClassName, currDir, existingDir});
    +            } else {
    +                classLoaderMap.put(o.getClass().getCanonicalName(), extensionClassLoader);
    +            }
    +        }
    +    }
    +
    +    /**
    +     * Gets all of the class loaders to consider for loading extensions.
    +     *
    +     * Includes the class loader of the web-app running the framework, plus a class loader for each additional
    +     * directory specified in nifi-registry.properties.
    +     *
    +     * @return a list of extension class loaders
    +     */
    +    private List<ExtensionClassLoader> getClassLoaders() {
    +        final List<ExtensionClassLoader> classLoaders = new ArrayList<>();
    +
    +        // start with the class loader that loaded ExtensionManager, should be WebAppClassLoader for API WAR
    +        final ExtensionClassLoader frameworkClassLoader = new ExtensionClassLoader("system", new URL[0], this.getClass().getClassLoader());
    --- End diff --
    
    "system" -> "framework"


---