Github user bbende commented on a diff in the pull request: https://github.com/apache/nifi-maven/pull/7#discussion_r240374218 --- Diff: src/main/java/org/apache/nifi/extension/definition/extraction/ExtensionDefinitionFactory.java --- @@ -0,0 +1,247 @@ +/* + * 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.extension.definition.extraction; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.nifi.extension.definition.ExtensionDefinition; +import org.apache.nifi.extension.definition.ExtensionType; +import org.apache.nifi.extension.definition.Restriction; +import org.apache.nifi.extension.definition.Restrictions; +import org.apache.nifi.extension.definition.ServiceAPIDefinition; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.URL; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class ExtensionDefinitionFactory { + private static final String SERVICES_DIRECTORY = "META-INF/services/"; + + private static final Map<ExtensionType, String> INTERFACE_NAMES = new HashMap<>(); + static { + INTERFACE_NAMES.put(ExtensionType.PROCESSOR, "org.apache.nifi.processor.Processor"); + INTERFACE_NAMES.put(ExtensionType.CONTROLLER_SERVICE, "org.apache.nifi.controller.ControllerService"); + INTERFACE_NAMES.put(ExtensionType.REPORTING_TASK, "org.apache.nifi.reporting.ReportingTask"); + } + + private final ClassLoader extensionClassLoader; + + public ExtensionDefinitionFactory(final ClassLoader classLoader) { + this.extensionClassLoader = classLoader; + } + + public Set<ExtensionDefinition> discoverExtensions(final ExtensionType extensionType) throws IOException { + final String interfaceName = INTERFACE_NAMES.get(extensionType); + final Set<String> classNames = discoverClassNames(interfaceName); + + if (classNames.isEmpty()) { + return Collections.emptySet(); + } + + final Set<ExtensionDefinition> definitions = new HashSet<>(); + for (final String className : classNames) { + try { + definitions.add(createExtensionDefinition(extensionType, className)); + } catch (final Exception e) { + throw new IOException("Failed to create Extension Definition for " + extensionType + " " + className, e); + } + } + + return definitions; + } + + private ExtensionDefinition createExtensionDefinition(final ExtensionType extensionType, final String className) throws ClassNotFoundException, + IllegalAccessException, NoSuchMethodException, InvocationTargetException { + + final Class<?> extensionClass = Class.forName(className, false, extensionClassLoader); + + final String capabilityDescription = getCapabilityDescription(extensionClass); + final Set<String> tags = getTags(extensionClass); + final Restrictions restrictions = getRestrictions(extensionClass); + final Set<ServiceAPIDefinition> serviceApis = getProvidedServiceAPIs(extensionType, extensionClass); + + return new StandardExtensionDefinition(extensionType, className, capabilityDescription, tags, restrictions, serviceApis); + } + + private Set<ServiceAPIDefinition> getProvidedServiceAPIs(final ExtensionType extensionType, final Class<?> extensionClass) throws ClassNotFoundException { + if (extensionType != ExtensionType.CONTROLLER_SERVICE) { + return Collections.emptySet(); + } + + final Set<ServiceAPIDefinition> serviceApis = new HashSet<>(); + final Class<?> controllerServiceClass = Class.forName("org.apache.nifi.controller.ControllerService", false, extensionClassLoader); + + for (final Class<?> implementedInterface : extensionClass.getInterfaces()) { + if (controllerServiceClass.isAssignableFrom(implementedInterface)) { + final ClassLoader interfaceClassLoader = implementedInterface.getClassLoader(); + if (interfaceClassLoader instanceof ExtensionClassLoader) { + final Artifact interfaceNarArtifact = ((ExtensionClassLoader) interfaceClassLoader).getNarArtifact(); + + final ServiceAPIDefinition serviceDefinition = new StandardServiceAPIDefinition(implementedInterface.getName(), + interfaceNarArtifact.getGroupId(), interfaceNarArtifact.getArtifactId(), interfaceNarArtifact.getVersion()); + + serviceApis.add(serviceDefinition); + } + } + } + + return serviceApis; + } + + private Restrictions getRestrictions(final Class<?> extensionClass) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException { --- End diff -- I'm not sure if it's worth removing since you already implemented these, but from tracing through the code, I think if we removed the XML/Property writers that are not being used, then we also don't need the tags, restrictions, and capability description in here and in the ExtensionDefinition interface, since the code in nifi-api would be obtaining all of that, right? Can then also remove the Restrictions and Restriction interfaces and their implementations.
---