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"
---