This is an automated email from the ASF dual-hosted git repository.
exceptionfactory pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git
The following commit(s) were added to refs/heads/main by this push:
new 26e5f5a565 NIFI-12970 Generate documentation for Python Processors
26e5f5a565 is described below
commit 26e5f5a565950a4de8ded985df549c5b659a68a1
Author: Mark Bathori <[email protected]>
AuthorDate: Thu Mar 28 18:34:11 2024 +0100
NIFI-12970 Generate documentation for Python Processors
This closes #8579
Signed-off-by: David Handermann <[email protected]>
---
.../nifi-framework/nifi-documentation/pom.xml | 5 +
.../apache/nifi/documentation/DocGenerator.java | 55 +-
.../nifi/documentation/DocumentationWriter.java | 7 +-
.../html/AbstractHtmlDocumentationWriter.java | 355 +++++++++++
.../html/HtmlDocumentationWriter.java | 685 ++++++---------------
.../html/HtmlProcessorDocumentationWriter.java | 82 +--
.../HtmlPythonProcessorDocumentationWriter.java | 280 +++++++++
.../nifi/documentation/DocGeneratorTest.java | 1 +
...HtmlPythonProcessorDocumentationWriterTest.java | 187 ++++++
.../html/ProcessorDocumentationWriterTest.java | 30 +-
.../org/apache/nifi/web/server/JettyServer.java | 25 +-
.../nifi/web/docs/DocumentationController.java | 12 +-
12 files changed, 1152 insertions(+), 572 deletions(-)
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/pom.xml
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/pom.xml
index 9f9bdc60e3..86bacb3071 100644
---
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/pom.xml
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/pom.xml
@@ -26,6 +26,11 @@
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-framework-api</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.nifi</groupId>
+ <artifactId>nifi-python-framework-api</artifactId>
+ <version>2.0.0-SNAPSHOT</version>
+ </dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-server-api</artifactId>
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/DocGenerator.java
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/DocGenerator.java
index e9cef0b5cb..e934302c0b 100644
---
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/DocGenerator.java
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/DocGenerator.java
@@ -22,12 +22,14 @@ import org.apache.nifi.components.ConfigurableComponent;
import org.apache.nifi.controller.ControllerService;
import org.apache.nifi.documentation.html.HtmlDocumentationWriter;
import org.apache.nifi.documentation.html.HtmlProcessorDocumentationWriter;
+import
org.apache.nifi.documentation.html.HtmlPythonProcessorDocumentationWriter;
import org.apache.nifi.flowanalysis.FlowAnalysisRule;
import org.apache.nifi.nar.ExtensionDefinition;
import org.apache.nifi.nar.ExtensionManager;
import org.apache.nifi.nar.ExtensionMapping;
import org.apache.nifi.parameter.ParameterProvider;
import org.apache.nifi.processor.Processor;
+import org.apache.nifi.python.PythonProcessorDetails;
import org.apache.nifi.reporting.ReportingTask;
import org.apache.nifi.util.NiFiProperties;
import org.slf4j.Logger;
@@ -100,13 +102,26 @@ public class DocGenerator {
logger.debug("Documentation directory created [{}]",
componentDirectory);
}
- final Class<?> extensionClass =
extensionManager.getClass(extensionDefinition);
- final Class<? extends ConfigurableComponent> componentClass =
extensionClass.asSubclass(ConfigurableComponent.class);
- try {
- logger.debug("Documentation generation started: Component
Class [{}]", componentClass);
- document(extensionManager, componentDirectory,
componentClass, coordinate);
- } catch (Exception e) {
- logger.warn("Documentation generation failed: Component
Class [{}]", componentClass, e);
+ switch (extensionDefinition.getRuntime()) {
+ case PYTHON -> {
+ final String componentClass =
extensionDefinition.getImplementationClassName();
+ final PythonProcessorDetails processorDetails =
extensionManager.getPythonProcessorDetails(componentClass,
extensionDefinition.getVersion());
+ try {
+ documentPython(componentDirectory,
processorDetails);
+ } catch (Exception e) {
+ logger.warn("Documentation generation failed:
Component Class [{}]", componentClass, e);
+ }
+ }
+ case JAVA -> {
+ final Class<?> extensionClass =
extensionManager.getClass(extensionDefinition);
+ final Class<? extends ConfigurableComponent>
componentClass = extensionClass.asSubclass(ConfigurableComponent.class);
+ try {
+ logger.debug("Documentation generation started:
Component Class [{}]", componentClass);
+ document(extensionManager, componentDirectory,
componentClass, coordinate);
+ } catch (Exception e) {
+ logger.warn("Documentation generation failed:
Component Class [{}]", componentClass, e);
+ }
+ }
}
}
}
@@ -131,7 +146,7 @@ public class DocGenerator {
final String classType = componentClass.getCanonicalName();
final ConfigurableComponent component =
extensionManager.getTempComponent(classType, bundleCoordinate);
- final DocumentationWriter writer = getDocumentWriter(extensionManager,
componentClass);
+ final DocumentationWriter<ConfigurableComponent> writer =
getDocumentWriter(extensionManager, componentClass);
final File baseDocumentationFile = new File(componentDocsDir,
"index.html");
if (baseDocumentationFile.exists()) {
@@ -143,7 +158,29 @@ public class DocGenerator {
}
}
- private static DocumentationWriter getDocumentWriter(
+ /**
+ * Generates the documentation for a particular configurable component.
Will
+ * check to see if an "additionalDetails.html" file exists and will link
+ * that from the generated documentation.
+ *
+ * @param componentDocsDir the component documentation directory
+ * @param processorDetails the python processor to document
+ * @throws IOException ioe
+ */
+ private static void documentPython(final File componentDocsDir, final
PythonProcessorDetails processorDetails) throws IOException {
+ final DocumentationWriter<PythonProcessorDetails> writer = new
HtmlPythonProcessorDocumentationWriter();
+ final File baseDocumentationFile = new File(componentDocsDir,
"index.html");
+
+ if (baseDocumentationFile.exists()) {
+ logger.warn("Overwriting Component Documentation [{}]",
baseDocumentationFile);
+ }
+
+ try (final OutputStream output = new
BufferedOutputStream(Files.newOutputStream(baseDocumentationFile.toPath()))) {
+ writer.write(processorDetails, output,
hasAdditionalInfo(componentDocsDir));
+ }
+ }
+
+ private static DocumentationWriter<ConfigurableComponent>
getDocumentWriter(
final ExtensionManager extensionManager,
final Class<? extends ConfigurableComponent> componentClass
) {
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/DocumentationWriter.java
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/DocumentationWriter.java
index 391873d4aa..068658d67f 100644
---
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/DocumentationWriter.java
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/DocumentationWriter.java
@@ -19,15 +19,12 @@ package org.apache.nifi.documentation;
import java.io.IOException;
import java.io.OutputStream;
-import org.apache.nifi.components.ConfigurableComponent;
-
/**
* Generates documentation for an instance of a ConfigurableComponent
*
*
*/
-public interface DocumentationWriter {
+public interface DocumentationWriter<T> {
- void write(ConfigurableComponent configurableComponent, OutputStream
streamToWriteTo,
- boolean includesAdditionalDocumentation) throws IOException;
+ void write(T component, OutputStream streamToWriteTo, boolean
includesAdditionalDocumentation) throws IOException;
}
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/html/AbstractHtmlDocumentationWriter.java
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/html/AbstractHtmlDocumentationWriter.java
new file mode 100644
index 0000000000..dc526c9db8
--- /dev/null
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/html/AbstractHtmlDocumentationWriter.java
@@ -0,0 +1,355 @@
+/*
+ * 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.documentation.html;
+
+import org.apache.nifi.documentation.DocumentationWriter;
+import org.apache.nifi.util.StringUtils;
+
+import javax.xml.stream.FactoryConfigurationError;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+abstract class AbstractHtmlDocumentationWriter<T> implements
DocumentationWriter<T> {
+
+ /**
+ * The filename where additional user specified information may be stored.
+ */
+ public static final String ADDITIONAL_DETAILS_HTML =
"additionalDetails.html";
+
+ static final String NO_DESCRIPTION = "No description provided.";
+ static final String NO_TAGS = "No tags provided.";
+ static final String NO_PROPERTIES = "This component has no required or
optional properties.";
+
+ static final String H2 = "h2";
+ static final String H3 = "h3";
+ static final String H4 = "h4";
+ static final String P = "p";
+ static final String BR = "br";
+ static final String SPAN = "span";
+ static final String STRONG = "strong";
+ static final String TABLE = "table";
+ static final String TH = "th";
+ static final String TR = "tr";
+ static final String TD = "td";
+ static final String UL = "ul";
+ static final String LI = "li";
+ static final String ID = "id";
+
+ @Override
+ public void write(final T component, final OutputStream outputStream,
final boolean includesAdditionalDocumentation) throws IOException {
+ try {
+ XMLStreamWriter xmlStreamWriter =
XMLOutputFactory.newInstance().createXMLStreamWriter(outputStream, "UTF-8");
+ xmlStreamWriter.writeDTD("<!DOCTYPE html>");
+ xmlStreamWriter.writeStartElement("html");
+ xmlStreamWriter.writeAttribute("lang", "en");
+ writeHead(component, xmlStreamWriter);
+ writeBody(component, xmlStreamWriter,
includesAdditionalDocumentation);
+ xmlStreamWriter.writeEndElement();
+ xmlStreamWriter.close();
+ } catch (XMLStreamException | FactoryConfigurationError e) {
+ throw new IOException("Unable to create XMLOutputStream", e);
+ }
+ }
+
+ /**
+ * Writes the head portion of the HTML documentation.
+ *
+ * @param component the component to describe
+ * @param xmlStreamWriter the stream to write to
+ * @throws XMLStreamException thrown if there was a problem writing to the
stream
+ */
+ protected void writeHead(final T component, final XMLStreamWriter
xmlStreamWriter) throws XMLStreamException {
+ xmlStreamWriter.writeStartElement("head");
+ xmlStreamWriter.writeStartElement("meta");
+ xmlStreamWriter.writeAttribute("charset", "utf-8");
+ xmlStreamWriter.writeEndElement();
+ writeSimpleElement(xmlStreamWriter, "title", getTitle(component));
+
+ xmlStreamWriter.writeStartElement("link");
+ xmlStreamWriter.writeAttribute("rel", "stylesheet");
+ xmlStreamWriter.writeAttribute("href",
"../../../../../css/component-usage.css");
+ xmlStreamWriter.writeAttribute("type", "text/css");
+ xmlStreamWriter.writeEndElement();
+ xmlStreamWriter.writeEndElement();
+
+ xmlStreamWriter.writeStartElement("script");
+ xmlStreamWriter.writeAttribute("type", "text/javascript");
+ xmlStreamWriter.writeCharacters("window.onload =
function(){if(self==top) { " +
+ "document.getElementById('nameHeader').style.display =
\"inherit\"; } }");
+ xmlStreamWriter.writeEndElement();
+ }
+
+ /**
+ * Writes the body section of the documentation, this consists of the
component description, the tags, and the PropertyDescriptors.
+ *
+ * @param component the component to describe
+ * @param xmlStreamWriter the stream writer
+ * @param hasAdditionalDetails whether there are additional details
present or not
+ * @throws XMLStreamException thrown if there was a problem writing to the
XML stream
+ */
+ void writeBody(final T component, final XMLStreamWriter xmlStreamWriter,
final boolean hasAdditionalDetails) throws XMLStreamException {
+ xmlStreamWriter.writeStartElement("body");
+ writeHeader(component, xmlStreamWriter);
+ writeDeprecationWarning(component, xmlStreamWriter);
+ writeDescription(component, xmlStreamWriter, hasAdditionalDetails);
+ writeTags(component, xmlStreamWriter);
+ writeProperties(component, xmlStreamWriter);
+ writeDynamicProperties(component, xmlStreamWriter);
+ writeAdditionalBodyInfo(component, xmlStreamWriter);
+ writeStatefulInfo(component, xmlStreamWriter);
+ writeRestrictedInfo(component, xmlStreamWriter);
+ writeInputRequirementInfo(component, xmlStreamWriter);
+ writeUseCases(component, xmlStreamWriter);
+ writeMultiComponentUseCases(component, xmlStreamWriter);
+ writeSystemResourceConsiderationInfo(component, xmlStreamWriter);
+ writeSeeAlso(component, xmlStreamWriter);
+ xmlStreamWriter.writeEndElement();
+ }
+
+ /**
+ * Write the header to be displayed when loaded outside an iframe.
+ *
+ * @param component the component to describe
+ * @param xmlStreamWriter the stream writer to use
+ * @throws XMLStreamException thrown if there was a problem writing the XML
+ */
+ private void writeHeader(final T component, XMLStreamWriter
xmlStreamWriter) throws XMLStreamException {
+ xmlStreamWriter.writeStartElement("h1");
+ xmlStreamWriter.writeAttribute(ID, "nameHeader");
+ // Style will be overwritten on load if needed
+ xmlStreamWriter.writeAttribute("style", "display: none;");
+ xmlStreamWriter.writeCharacters(getTitle(component));
+ xmlStreamWriter.writeEndElement();
+ }
+
+ /**
+ * Writes a description of the component.
+ *
+ * @param component the component to describe
+ * @param xmlStreamWriter the stream writer
+ * @param hasAdditionalDetails whether there are additional details
available as 'additionalDetails.html'
+ * @throws XMLStreamException thrown if there was a problem writing to the
XML stream
+ */
+ protected void writeDescription(final T component, final XMLStreamWriter
xmlStreamWriter, final boolean hasAdditionalDetails) throws XMLStreamException {
+ writeSimpleElement(xmlStreamWriter, H2, "Description: ");
+ writeSimpleElement(xmlStreamWriter, P, getDescription(component));
+ if (hasAdditionalDetails) {
+ xmlStreamWriter.writeStartElement(P);
+
+ writeLink(xmlStreamWriter, "Additional Details...",
ADDITIONAL_DETAILS_HTML);
+
+ xmlStreamWriter.writeEndElement();
+ }
+ }
+
+ /**
+ * This method may be overridden by subclasses to write additional
information to the body of the documentation.
+ *
+ * @param component the component to describe
+ * @param xmlStreamWriter the stream writer
+ * @throws XMLStreamException thrown if there was a problem writing to the
XML stream
+ */
+ protected void writeAdditionalBodyInfo(final T component, final
XMLStreamWriter xmlStreamWriter) throws XMLStreamException {
+
+ }
+
+ /**
+ * Writes a begin element, an id attribute(if specified), then text, then
end element for element of the users choosing. Example: <p
+ * id="p-id">text</p>
+ *
+ * @param writer the stream writer to use
+ * @param elementName the name of the element
+ * @param characters the text of the element
+ * @param id the id of the element. specifying null will cause no
element to be written.
+ * @throws XMLStreamException xse
+ */
+ protected static void writeSimpleElement(final XMLStreamWriter writer,
final String elementName, final String characters, String id) throws
XMLStreamException {
+ writer.writeStartElement(elementName);
+
+ if (characters != null) {
+ if (id != null) {
+ writer.writeAttribute(ID, id);
+ }
+ writer.writeCharacters(characters);
+ }
+
+ writer.writeEndElement();
+ }
+
+ /**
+ * Writes a begin element, then text, then end element for the element of
a users choosing. Example: <p>text</p>
+ *
+ * @param writer the stream writer to use
+ * @param elementName the name of the element
+ * @param characters the characters to insert into the element
+ */
+ protected static void writeSimpleElement(final XMLStreamWriter writer,
final String elementName, final String characters) throws XMLStreamException {
+ writeSimpleElement(writer, elementName, characters, null);
+ }
+
+ /**
+ * A helper method to write a link
+ *
+ * @param xmlStreamWriter the stream to write to
+ * @param text the text of the link
+ * @param location the location of the link
+ * @throws XMLStreamException thrown if there was a problem writing to the
+ * stream
+ */
+ protected void writeLink(final XMLStreamWriter xmlStreamWriter, final
String text, final String location) throws XMLStreamException {
+ xmlStreamWriter.writeStartElement("a");
+ xmlStreamWriter.writeAttribute("href", location);
+ xmlStreamWriter.writeCharacters(text);
+ xmlStreamWriter.writeEndElement();
+ }
+
+ void writeUseCaseConfiguration(final String configuration, final
XMLStreamWriter xmlStreamWriter) throws XMLStreamException {
+ if (StringUtils.isEmpty(configuration)) {
+ return;
+ }
+
+ writeSimpleElement(xmlStreamWriter, H4, "Configuration:");
+
+ final String[] splits = configuration.split("\\n");
+ for (final String split : splits) {
+ xmlStreamWriter.writeStartElement(P);
+
+ final Matcher matcher = Pattern.compile("`(.*?)`").matcher(split);
+ int startIndex = 0;
+ while (matcher.find()) {
+ final int start = matcher.start();
+ if (start > 0) {
+
xmlStreamWriter.writeCharacters(split.substring(startIndex, start));
+ }
+
+ writeSimpleElement(xmlStreamWriter, "code", matcher.group(1));
+
+ startIndex = matcher.end();
+ }
+
+ if (split.length() > startIndex) {
+ if (startIndex == 0) {
+ xmlStreamWriter.writeCharacters(split);
+ } else {
+
xmlStreamWriter.writeCharacters(split.substring(startIndex));
+ }
+ }
+
+ xmlStreamWriter.writeEndElement();
+ }
+ }
+
+ /**
+ * Gets the class name of the component.
+ *
+ * @param component the component to describe
+ * @return the class name of the component
+ */
+ abstract String getTitle(final T component);
+
+ /**
+ * Writes a warning about the deprecation of a component.
+ *
+ * @param component the component to describe
+ * @param xmlStreamWriter the stream writer
+ * @throws XMLStreamException thrown if there was a problem writing to the
XML stream
+ */
+ abstract void writeDeprecationWarning(final T component, final
XMLStreamWriter xmlStreamWriter) throws XMLStreamException;
+
+ /**
+ * Gets a description of the component using the CapabilityDescription
annotation.
+ *
+ * @param component the component to describe
+ * @return a description of the component
+ */
+ abstract String getDescription(final T component);
+
+ /**
+ * Writes the tag list of the component.
+ *
+ * @param component the component to describe
+ * @param xmlStreamWriter the stream writer
+ * @throws XMLStreamException thrown if there was a problem writing to the
XML stream
+ */
+ abstract void writeTags(final T component, final XMLStreamWriter
xmlStreamWriter) throws XMLStreamException;
+
+ /**
+ * Writes the PropertyDescriptors out as a table.
+ *
+ * @param component the component to describe
+ * @param xmlStreamWriter the stream writer
+ * @throws XMLStreamException thrown if there was a problem writing to the
XML Stream
+ */
+ abstract void writeProperties(final T component, final XMLStreamWriter
xmlStreamWriter) throws XMLStreamException;
+
+ abstract void writeDynamicProperties(final T component, final
XMLStreamWriter xmlStreamWriter) throws XMLStreamException;
+
+ /**
+ * Write the description of the Stateful annotation if provided in this
component.
+ *
+ * @param component the component to describe
+ * @param xmlStreamWriter the stream writer to use
+ * @throws XMLStreamException thrown if there was a problem writing the XML
+ */
+ abstract void writeStatefulInfo(final T component, XMLStreamWriter
xmlStreamWriter) throws XMLStreamException;
+
+ /**
+ * Write the description of the Restricted annotation if provided in this
component.
+ *
+ * @param component the component to describe
+ * @param xmlStreamWriter the stream writer to use
+ * @throws XMLStreamException thrown if there was a problem writing the XML
+ */
+ abstract void writeRestrictedInfo(final T component, XMLStreamWriter
xmlStreamWriter) throws XMLStreamException;
+
+ /**
+ * Add in the documentation information regarding the component whether it
accepts an incoming relationship or not.
+ *
+ * @param component the component to describe
+ * @param xmlStreamWriter the stream writer to use
+ * @throws XMLStreamException thrown if there was a problem writing the XML
+ */
+ abstract void writeInputRequirementInfo(final T component, XMLStreamWriter
xmlStreamWriter) throws XMLStreamException;
+
+ abstract void writeUseCases(final T component, final XMLStreamWriter
xmlStreamWriter) throws XMLStreamException;
+
+ abstract void writeMultiComponentUseCases(final T component, final
XMLStreamWriter xmlStreamWriter) throws XMLStreamException;
+
+ /**
+ * Writes the list of components that may be linked from this component.
+ *
+ * @param component the component to describe
+ * @param xmlStreamWriter the stream writer to use
+ * @throws XMLStreamException thrown if there was a problem writing the XML
+ */
+ abstract void writeSeeAlso(final T component, XMLStreamWriter
xmlStreamWriter) throws XMLStreamException;
+
+ /**
+ * Writes all the system resource considerations for this component
+ *
+ * @param component the component to describe
+ * @param xmlStreamWriter the xml stream writer to use
+ * @throws XMLStreamException thrown if there was a problem writing the XML
+ */
+ abstract void writeSystemResourceConsiderationInfo(final T component,
XMLStreamWriter xmlStreamWriter) throws XMLStreamException;
+
+}
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/html/HtmlDocumentationWriter.java
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/html/HtmlDocumentationWriter.java
index 9bda0d5519..0d70b0ac13 100644
---
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/html/HtmlDocumentationWriter.java
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/html/HtmlDocumentationWriter.java
@@ -42,26 +42,19 @@ import
org.apache.nifi.components.resource.ResourceCardinality;
import org.apache.nifi.components.resource.ResourceDefinition;
import org.apache.nifi.components.resource.ResourceType;
import org.apache.nifi.controller.ControllerService;
-import org.apache.nifi.documentation.DocumentationWriter;
import org.apache.nifi.nar.ExtensionDefinition;
import org.apache.nifi.nar.ExtensionManager;
import org.apache.nifi.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import javax.xml.stream.FactoryConfigurationError;
-import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
-import java.io.IOException;
-import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
@@ -70,15 +63,10 @@ import java.util.stream.Collectors;
* they have no additional information.
*
*/
-public class HtmlDocumentationWriter implements DocumentationWriter {
+public class HtmlDocumentationWriter extends
AbstractHtmlDocumentationWriter<ConfigurableComponent> {
public static final Logger LOGGER =
LoggerFactory.getLogger(HtmlDocumentationWriter.class);
- /**
- * The filename where additional user specified information may be stored.
- */
- public static final String ADDITIONAL_DETAILS_HTML =
"additionalDetails.html";
-
private final ExtensionManager extensionManager;
public HtmlDocumentationWriter(final ExtensionManager extensionManager) {
@@ -86,128 +74,16 @@ public class HtmlDocumentationWriter implements
DocumentationWriter {
}
@Override
- public void write(final ConfigurableComponent configurableComponent, final
OutputStream streamToWriteTo,
- final boolean includesAdditionalDocumentation) throws IOException {
-
- try {
- XMLStreamWriter xmlStreamWriter =
XMLOutputFactory.newInstance().createXMLStreamWriter(
- streamToWriteTo, "UTF-8");
- xmlStreamWriter.writeDTD("<!DOCTYPE html>");
- xmlStreamWriter.writeStartElement("html");
- xmlStreamWriter.writeAttribute("lang", "en");
- writeHead(configurableComponent, xmlStreamWriter);
- writeBody(configurableComponent, xmlStreamWriter,
includesAdditionalDocumentation);
- xmlStreamWriter.writeEndElement();
- xmlStreamWriter.close();
- } catch (XMLStreamException | FactoryConfigurationError e) {
- throw new IOException("Unable to create XMLOutputStream", e);
- }
- }
-
- /**
- * Writes the head portion of the HTML documentation.
- *
- * @param configurableComponent the component to describe
- * @param xmlStreamWriter the stream to write to
- * @throws XMLStreamException thrown if there was a problem writing to the
- * stream
- */
- protected void writeHead(final ConfigurableComponent configurableComponent,
- final XMLStreamWriter xmlStreamWriter) throws XMLStreamException {
- xmlStreamWriter.writeStartElement("head");
- xmlStreamWriter.writeStartElement("meta");
- xmlStreamWriter.writeAttribute("charset", "utf-8");
- xmlStreamWriter.writeEndElement();
- writeSimpleElement(xmlStreamWriter, "title",
getTitle(configurableComponent));
-
- xmlStreamWriter.writeStartElement("link");
- xmlStreamWriter.writeAttribute("rel", "stylesheet");
- xmlStreamWriter.writeAttribute("href",
"../../../../../css/component-usage.css");
- xmlStreamWriter.writeAttribute("type", "text/css");
- xmlStreamWriter.writeEndElement();
- xmlStreamWriter.writeEndElement();
-
- xmlStreamWriter.writeStartElement("script");
- xmlStreamWriter.writeAttribute("type", "text/javascript");
- xmlStreamWriter.writeCharacters("window.onload =
function(){if(self==top) { " +
- "document.getElementById('nameHeader').style.display =
\"inherit\"; } }" );
- xmlStreamWriter.writeEndElement();
-
- }
-
- /**
- * Gets the class name of the component.
- *
- * @param configurableComponent the component to describe
- * @return the class name of the component
- */
protected String getTitle(final ConfigurableComponent
configurableComponent) {
return configurableComponent.getClass().getSimpleName();
}
- /**
- * Writes the body section of the documentation, this consists of the
- * component description, the tags, and the PropertyDescriptors.
- *
- * @param configurableComponent the component to describe
- * @param xmlStreamWriter the stream writer
- * @param hasAdditionalDetails whether there are additional details present
- * or not
- * @throws XMLStreamException thrown if there was a problem writing to the
- * XML stream
- */
- private void writeBody(final ConfigurableComponent configurableComponent,
- final XMLStreamWriter xmlStreamWriter, final boolean
hasAdditionalDetails)
- throws XMLStreamException {
- xmlStreamWriter.writeStartElement("body");
- writeHeader(configurableComponent, xmlStreamWriter);
- writeDeprecationWarning(configurableComponent, xmlStreamWriter);
- writeDescription(configurableComponent, xmlStreamWriter,
hasAdditionalDetails);
- writeTags(configurableComponent, xmlStreamWriter);
- writeProperties(configurableComponent, xmlStreamWriter);
- writeDynamicProperties(configurableComponent, xmlStreamWriter);
- writeAdditionalBodyInfo(configurableComponent, xmlStreamWriter);
- writeStatefulInfo(configurableComponent, xmlStreamWriter);
- writeRestrictedInfo(configurableComponent, xmlStreamWriter);
- writeInputRequirementInfo(configurableComponent, xmlStreamWriter);
- writeUseCases(configurableComponent, xmlStreamWriter);
- writeMultiComponentUseCases(configurableComponent, xmlStreamWriter);
- writeSystemResourceConsiderationInfo(configurableComponent,
xmlStreamWriter);
- writeSeeAlso(configurableComponent, xmlStreamWriter);
- xmlStreamWriter.writeEndElement();
- }
-
- /**
- * Write the header to be displayed when loaded outside an iframe.
- *
- * @param configurableComponent the component to describe
- * @param xmlStreamWriter the stream writer to use
- * @throws XMLStreamException thrown if there was a problem writing the XML
- */
- private void writeHeader(ConfigurableComponent configurableComponent,
XMLStreamWriter xmlStreamWriter)
- throws XMLStreamException {
- xmlStreamWriter.writeStartElement("h1");
- xmlStreamWriter.writeAttribute("id", "nameHeader");
- // Style will be overwritten on load if needed
- xmlStreamWriter.writeAttribute("style", "display: none;");
- xmlStreamWriter.writeCharacters(getTitle(configurableComponent));
- xmlStreamWriter.writeEndElement();
- }
-
- /**
- * Add in the documentation information regarding the component whether it
accepts an
- * incoming relationship or not.
- *
- * @param configurableComponent the component to describe
- * @param xmlStreamWriter the stream writer to use
- * @throws XMLStreamException thrown if there was a problem writing the XML
- */
- private void writeInputRequirementInfo(ConfigurableComponent
configurableComponent, XMLStreamWriter xmlStreamWriter)
- throws XMLStreamException {
+ @Override
+ void writeInputRequirementInfo(final ConfigurableComponent
configurableComponent, XMLStreamWriter xmlStreamWriter) throws
XMLStreamException {
final InputRequirement inputRequirement =
configurableComponent.getClass().getAnnotation(InputRequirement.class);
- if(inputRequirement != null) {
- writeSimpleElement(xmlStreamWriter, "h3", "Input requirement: ");
+ if (inputRequirement != null) {
+ writeSimpleElement(xmlStreamWriter, H3, "Input requirement: ");
switch (inputRequirement.value()) {
case INPUT_FORBIDDEN:
xmlStreamWriter.writeCharacters("This component does not
allow an incoming relationship.");
@@ -225,30 +101,23 @@ public class HtmlDocumentationWriter implements
DocumentationWriter {
}
}
- /**
- * Write the description of the Stateful annotation if provided in this
component.
- *
- * @param configurableComponent the component to describe
- * @param xmlStreamWriter the stream writer to use
- * @throws XMLStreamException thrown if there was a problem writing the XML
- */
- private void writeStatefulInfo(ConfigurableComponent
configurableComponent, XMLStreamWriter xmlStreamWriter)
- throws XMLStreamException {
+ @Override
+ void writeStatefulInfo(final ConfigurableComponent configurableComponent,
XMLStreamWriter xmlStreamWriter) throws XMLStreamException {
final Stateful stateful =
configurableComponent.getClass().getAnnotation(Stateful.class);
- writeSimpleElement(xmlStreamWriter, "h3", "State management: ");
+ writeSimpleElement(xmlStreamWriter, H3, "State management: ");
- if(stateful != null) {
- xmlStreamWriter.writeStartElement("table");
- xmlStreamWriter.writeAttribute("id", "stateful");
- xmlStreamWriter.writeStartElement("tr");
- writeSimpleElement(xmlStreamWriter, "th", "Scope");
- writeSimpleElement(xmlStreamWriter, "th", "Description");
+ if (stateful != null) {
+ xmlStreamWriter.writeStartElement(TABLE);
+ xmlStreamWriter.writeAttribute(ID, "stateful");
+ xmlStreamWriter.writeStartElement(TR);
+ writeSimpleElement(xmlStreamWriter, TH, "Scope");
+ writeSimpleElement(xmlStreamWriter, TH, "Description");
xmlStreamWriter.writeEndElement();
- xmlStreamWriter.writeStartElement("tr");
- writeSimpleElement(xmlStreamWriter, "td", join(stateful.scopes()));
- writeSimpleElement(xmlStreamWriter, "td", stateful.description());
+ xmlStreamWriter.writeStartElement(TR);
+ writeSimpleElement(xmlStreamWriter, TD, join(stateful.scopes()));
+ writeSimpleElement(xmlStreamWriter, TD, stateful.description());
xmlStreamWriter.writeEndElement();
xmlStreamWriter.writeEndElement();
@@ -257,20 +126,13 @@ public class HtmlDocumentationWriter implements
DocumentationWriter {
}
}
- /**
- * Write the description of the Restricted annotation if provided in this
component.
- *
- * @param configurableComponent the component to describe
- * @param xmlStreamWriter the stream writer to use
- * @throws XMLStreamException thrown if there was a problem writing the XML
- */
- private void writeRestrictedInfo(ConfigurableComponent
configurableComponent, XMLStreamWriter xmlStreamWriter)
- throws XMLStreamException {
+ @Override
+ void writeRestrictedInfo(final ConfigurableComponent
configurableComponent, XMLStreamWriter xmlStreamWriter) throws
XMLStreamException {
final Restricted restricted =
configurableComponent.getClass().getAnnotation(Restricted.class);
- writeSimpleElement(xmlStreamWriter, "h3", "Restricted: ");
+ writeSimpleElement(xmlStreamWriter, H3, "Restricted: ");
- if(restricted != null) {
+ if (restricted != null) {
final String value = restricted.value();
if (!StringUtils.isBlank(value)) {
@@ -279,17 +141,17 @@ public class HtmlDocumentationWriter implements
DocumentationWriter {
final Restriction[] restrictions = restricted.restrictions();
if (restrictions != null && restrictions.length > 0) {
- xmlStreamWriter.writeStartElement("table");
- xmlStreamWriter.writeAttribute("id", "restrictions");
- xmlStreamWriter.writeStartElement("tr");
- writeSimpleElement(xmlStreamWriter, "th", "Required
Permission");
- writeSimpleElement(xmlStreamWriter, "th", "Explanation");
+ xmlStreamWriter.writeStartElement(TABLE);
+ xmlStreamWriter.writeAttribute(ID, "restrictions");
+ xmlStreamWriter.writeStartElement(TR);
+ writeSimpleElement(xmlStreamWriter, TH, "Required Permission");
+ writeSimpleElement(xmlStreamWriter, TH, "Explanation");
xmlStreamWriter.writeEndElement();
for (Restriction restriction : restrictions) {
- xmlStreamWriter.writeStartElement("tr");
- writeSimpleElement(xmlStreamWriter, "td",
restriction.requiredPermission().getPermissionLabel());
- writeSimpleElement(xmlStreamWriter, "td",
restriction.explanation());
+ xmlStreamWriter.writeStartElement(TR);
+ writeSimpleElement(xmlStreamWriter, TD,
restriction.requiredPermission().getPermissionLabel());
+ writeSimpleElement(xmlStreamWriter, TD,
restriction.explanation());
xmlStreamWriter.writeEndElement();
}
@@ -302,36 +164,27 @@ public class HtmlDocumentationWriter implements
DocumentationWriter {
}
}
- /**
- * Writes a warning about the deprecation of a component.
- *
- * @param configurableComponent the component to describe
- * @param xmlStreamWriter the stream writer
- * @throws XMLStreamException thrown if there was a problem writing to the
- * XML stream
- */
- private void writeDeprecationWarning(final ConfigurableComponent
configurableComponent,
- final XMLStreamWriter
xmlStreamWriter) throws XMLStreamException {
+ @Override
+ void writeDeprecationWarning(final ConfigurableComponent
configurableComponent, final XMLStreamWriter xmlStreamWriter) throws
XMLStreamException {
final DeprecationNotice deprecationNotice =
configurableComponent.getClass().getAnnotation(DeprecationNotice.class);
+
if (deprecationNotice != null) {
- xmlStreamWriter.writeStartElement("h2");
+ xmlStreamWriter.writeStartElement(H2);
xmlStreamWriter.writeCharacters("Deprecation notice: ");
xmlStreamWriter.writeEndElement();
- xmlStreamWriter.writeStartElement("p");
+ xmlStreamWriter.writeStartElement(P);
xmlStreamWriter.writeCharacters("");
if (!StringUtils.isEmpty(deprecationNotice.reason())) {
xmlStreamWriter.writeCharacters(deprecationNotice.reason());
} else {
// Write a default note
- xmlStreamWriter.writeCharacters("Please be aware this
processor is deprecated and may be removed in " +
- "the near future.");
+ xmlStreamWriter.writeCharacters("Please be aware this
processor is deprecated and may be removed in the near future.");
}
xmlStreamWriter.writeEndElement();
- xmlStreamWriter.writeStartElement("p");
+ xmlStreamWriter.writeStartElement(P);
xmlStreamWriter.writeCharacters("Please consider using one the
following alternatives: ");
-
Class<? extends ConfigurableComponent>[] componentNames =
deprecationNotice.alternatives();
String[] classNames = deprecationNotice.classNames();
@@ -346,19 +199,13 @@ public class HtmlDocumentationWriter implements
DocumentationWriter {
}
}
- /**
- * Writes the list of components that may be linked from this component.
- *
- * @param configurableComponent the component to describe
- * @param xmlStreamWriter the stream writer to use
- * @throws XMLStreamException thrown if there was a problem writing the XML
- */
- private void writeSeeAlso(ConfigurableComponent configurableComponent,
XMLStreamWriter xmlStreamWriter)
- throws XMLStreamException {
+ @Override
+ void writeSeeAlso(final ConfigurableComponent configurableComponent,
XMLStreamWriter xmlStreamWriter) throws XMLStreamException {
final SeeAlso seeAlso =
configurableComponent.getClass().getAnnotation(SeeAlso.class);
+
if (seeAlso != null) {
- writeSimpleElement(xmlStreamWriter, "h3", "See Also:");
- xmlStreamWriter.writeStartElement("p");
+ writeSimpleElement(xmlStreamWriter, H3, "See Also:");
+ xmlStreamWriter.writeStartElement(P);
Class<? extends ConfigurableComponent>[] componentNames =
seeAlso.value();
String[] classNames = seeAlso.classNames();
@@ -366,40 +213,29 @@ public class HtmlDocumentationWriter implements
DocumentationWriter {
// Write alternatives
iterateAndLinkComponents(xmlStreamWriter, componentNames,
classNames, ", ", configurableComponent.getClass().getSimpleName());
} else {
- xmlStreamWriter.writeCharacters("No tags provided.");
+ xmlStreamWriter.writeCharacters(NO_TAGS);
}
xmlStreamWriter.writeEndElement();
}
}
- /**
- * This method may be overridden by sub classes to write additional
- * information to the body of the documentation.
- *
- * @param configurableComponent the component to describe
- * @param xmlStreamWriter the stream writer
- * @throws XMLStreamException thrown if there was a problem writing to the
- * XML stream
- */
- protected void writeAdditionalBodyInfo(final ConfigurableComponent
configurableComponent,
- final XMLStreamWriter xmlStreamWriter) throws XMLStreamException {
-
- }
-
- private void writeTags(final ConfigurableComponent configurableComponent,
- final XMLStreamWriter xmlStreamWriter) throws XMLStreamException {
+ @Override
+ void writeTags(final ConfigurableComponent configurableComponent, final
XMLStreamWriter xmlStreamWriter) throws XMLStreamException {
final Tags tags =
configurableComponent.getClass().getAnnotation(Tags.class);
- xmlStreamWriter.writeStartElement("h3");
+
+ xmlStreamWriter.writeStartElement(H3);
xmlStreamWriter.writeCharacters("Tags: ");
xmlStreamWriter.writeEndElement();
- xmlStreamWriter.writeStartElement("p");
+ xmlStreamWriter.writeStartElement(P);
+
if (tags != null) {
final String tagString = join(tags.value());
xmlStreamWriter.writeCharacters(tagString);
} else {
- xmlStreamWriter.writeCharacters("No tags provided.");
+ xmlStreamWriter.writeCharacters(NO_TAGS);
}
+
xmlStreamWriter.writeEndElement();
}
@@ -409,124 +245,95 @@ public class HtmlDocumentationWriter implements
DocumentationWriter {
.collect(Collectors.joining(", "));
}
- /**
- * Writes a description of the configurable component.
- *
- * @param configurableComponent the component to describe
- * @param xmlStreamWriter the stream writer
- * @param hasAdditionalDetails whether there are additional details
- * available as 'additionalDetails.html'
- * @throws XMLStreamException thrown if there was a problem writing to the
- * XML stream
- */
- protected void writeDescription(final ConfigurableComponent
configurableComponent,
- final XMLStreamWriter xmlStreamWriter, final boolean
hasAdditionalDetails)
- throws XMLStreamException {
- writeSimpleElement(xmlStreamWriter, "h2", "Description: ");
- writeSimpleElement(xmlStreamWriter, "p",
getDescription(configurableComponent));
- if (hasAdditionalDetails) {
- xmlStreamWriter.writeStartElement("p");
-
- writeLink(xmlStreamWriter, "Additional Details...",
ADDITIONAL_DETAILS_HTML);
-
- xmlStreamWriter.writeEndElement();
- }
- }
-
- /**
- * Gets a description of the ConfigurableComponent using the
- * CapabilityDescription annotation.
- *
- * @param configurableComponent the component to describe
- * @return a description of the configurableComponent
- */
- protected String getDescription(final ConfigurableComponent
configurableComponent) {
- final CapabilityDescription capabilityDescription =
configurableComponent.getClass().getAnnotation(
- CapabilityDescription.class);
+ @Override
+ String getDescription(final ConfigurableComponent configurableComponent) {
+ final CapabilityDescription capabilityDescription =
configurableComponent.getClass().getAnnotation(CapabilityDescription.class);
final String description;
if (capabilityDescription != null) {
description = capabilityDescription.value();
} else {
- description = "No description provided.";
+ description = NO_DESCRIPTION;
}
return description;
}
- protected void writeUseCases(final ConfigurableComponent component, final
XMLStreamWriter xmlStreamWriter) throws XMLStreamException {
+ @Override
+ void writeUseCases(final ConfigurableComponent component, final
XMLStreamWriter xmlStreamWriter) throws XMLStreamException {
final UseCase[] useCases =
component.getClass().getAnnotationsByType(UseCase.class);
if (useCases.length == 0) {
return;
}
- writeSimpleElement(xmlStreamWriter, "h2", "Example Use Cases:");
+ writeSimpleElement(xmlStreamWriter, H2, "Example Use Cases:");
for (final UseCase useCase : useCases) {
- writeSimpleElement(xmlStreamWriter, "h3", "Use Case:");
- writeSimpleElement(xmlStreamWriter, "p", useCase.description());
+ writeSimpleElement(xmlStreamWriter, H3, "Use Case:");
+ writeSimpleElement(xmlStreamWriter, P, useCase.description());
final String notes = useCase.notes();
if (!StringUtils.isEmpty(notes)) {
- writeSimpleElement(xmlStreamWriter, "h4", "Notes:");
+ writeSimpleElement(xmlStreamWriter, H4, "Notes:");
final String[] splits = notes.split("\\n");
for (final String split : splits) {
- writeSimpleElement(xmlStreamWriter, "p", split);
+ writeSimpleElement(xmlStreamWriter, P, split);
}
}
final String[] keywords = useCase.keywords();
if (keywords.length > 0) {
- writeSimpleElement(xmlStreamWriter, "h4", "Keywords:");
+ writeSimpleElement(xmlStreamWriter, H4, "Keywords:");
xmlStreamWriter.writeCharacters(String.join(", ", keywords));
}
final Requirement inputRequirement = useCase.inputRequirement();
if (inputRequirement != Requirement.INPUT_ALLOWED) {
- writeSimpleElement(xmlStreamWriter, "h4", "Input
Requirement:");
+ writeSimpleElement(xmlStreamWriter, H4, "Input Requirement:");
xmlStreamWriter.writeCharacters(inputRequirement.toString());
}
final String configuration = useCase.configuration();
writeUseCaseConfiguration(configuration, xmlStreamWriter);
- writeSimpleElement(xmlStreamWriter, "br", null);
+ writeSimpleElement(xmlStreamWriter, BR, null);
}
}
- protected void writeMultiComponentUseCases(final ConfigurableComponent
component, final XMLStreamWriter xmlStreamWriter) throws XMLStreamException {
+ @Override
+ void writeMultiComponentUseCases(final ConfigurableComponent component,
final XMLStreamWriter xmlStreamWriter) throws XMLStreamException {
final MultiProcessorUseCase[] useCases =
component.getClass().getAnnotationsByType(MultiProcessorUseCase.class);
if (useCases.length == 0) {
return;
}
- writeSimpleElement(xmlStreamWriter, "h2", "Example Use Cases Involving
Other Components:");
+ writeSimpleElement(xmlStreamWriter, H2, "Example Use Cases Involving
Other Components:");
for (final MultiProcessorUseCase useCase : useCases) {
- writeSimpleElement(xmlStreamWriter, "h3", "Use Case:");
- writeSimpleElement(xmlStreamWriter, "p", useCase.description());
+ writeSimpleElement(xmlStreamWriter, H3, "Use Case:");
+ writeSimpleElement(xmlStreamWriter, P, useCase.description());
final String notes = useCase.notes();
if (!StringUtils.isEmpty(notes)) {
- writeSimpleElement(xmlStreamWriter, "h4", "Notes:");
+ writeSimpleElement(xmlStreamWriter, H4, "Notes:");
final String[] splits = notes.split("\\n");
for (final String split : splits) {
- writeSimpleElement(xmlStreamWriter, "p", split);
+ writeSimpleElement(xmlStreamWriter, P, split);
}
}
final String[] keywords = useCase.keywords();
if (keywords.length > 0) {
- writeSimpleElement(xmlStreamWriter, "h4", "Keywords:");
+ writeSimpleElement(xmlStreamWriter, H4, "Keywords:");
xmlStreamWriter.writeCharacters(String.join(", ", keywords));
}
- writeSimpleElement(xmlStreamWriter, "h4", "Components involved:");
+ writeSimpleElement(xmlStreamWriter, H4, "Components involved:");
final ProcessorConfiguration[] processorConfigurations =
useCase.configurations();
for (final ProcessorConfiguration processorConfiguration :
processorConfigurations) {
- writeSimpleElement(xmlStreamWriter, "strong", "Component Type:
");
+ writeSimpleElement(xmlStreamWriter, STRONG, "Component Type:
");
final String extensionClassName;
if (processorConfiguration.processorClassName().isEmpty()) {
@@ -535,73 +342,29 @@ public class HtmlDocumentationWriter implements
DocumentationWriter {
extensionClassName =
processorConfiguration.processorClassName();
}
- writeSimpleElement(xmlStreamWriter, "span",
extensionClassName);
+ writeSimpleElement(xmlStreamWriter, SPAN, extensionClassName);
final String configuration =
processorConfiguration.configuration();
writeUseCaseConfiguration(configuration, xmlStreamWriter);
- writeSimpleElement(xmlStreamWriter, "br", null);
+ writeSimpleElement(xmlStreamWriter, BR, null);
}
- writeSimpleElement(xmlStreamWriter, "br", null);
- }
- }
-
- private void writeUseCaseConfiguration(final String configuration, final
XMLStreamWriter xmlStreamWriter) throws XMLStreamException {
- if (StringUtils.isEmpty(configuration)) {
- return;
- }
-
- writeSimpleElement(xmlStreamWriter, "h4", "Configuration:");
-
- final String[] splits = configuration.split("\\n");
- for (final String split : splits) {
- xmlStreamWriter.writeStartElement("p");
-
- final Matcher matcher = Pattern.compile("`(.*?)`").matcher(split);
- int startIndex = 0;
- while (matcher.find()) {
- final int start = matcher.start();
- if (start > 0) {
-
xmlStreamWriter.writeCharacters(split.substring(startIndex, start));
- }
-
- writeSimpleElement(xmlStreamWriter, "code", matcher.group(1));
-
- startIndex = matcher.end();
- }
- if (split.length() > startIndex) {
- if (startIndex == 0) {
- xmlStreamWriter.writeCharacters(split);
- } else {
-
xmlStreamWriter.writeCharacters(split.substring(startIndex));
- }
- }
-
- xmlStreamWriter.writeEndElement();
+ writeSimpleElement(xmlStreamWriter, BR, null);
}
}
- /**
- * Writes the PropertyDescriptors out as a table.
- *
- * @param configurableComponent the component to describe
- * @param xmlStreamWriter the stream writer
- * @throws XMLStreamException thrown if there was a problem writing to the
- * XML Stream
- */
- protected void writeProperties(final ConfigurableComponent
configurableComponent,
- final XMLStreamWriter xmlStreamWriter) throws XMLStreamException {
-
+ @Override
+ void writeProperties(final ConfigurableComponent configurableComponent,
final XMLStreamWriter xmlStreamWriter) throws XMLStreamException {
final List<PropertyDescriptor> properties =
configurableComponent.getPropertyDescriptors();
- writeSimpleElement(xmlStreamWriter, "h3", "Properties: ");
+ writeSimpleElement(xmlStreamWriter, H3, "Properties: ");
- if (properties.size() > 0) {
+ if (!properties.isEmpty()) {
final boolean containsExpressionLanguage =
containsExpressionLanguage(configurableComponent);
- xmlStreamWriter.writeStartElement("p");
+ xmlStreamWriter.writeStartElement(P);
xmlStreamWriter.writeCharacters("In the list below, the names of
required properties appear in ");
- writeSimpleElement(xmlStreamWriter, "strong", "bold");
+ writeSimpleElement(xmlStreamWriter, STRONG, "bold");
xmlStreamWriter.writeCharacters(". Any other properties (not in
bold) are considered optional. " +
"The table also indicates any default values");
if (containsExpressionLanguage) {
@@ -611,54 +374,54 @@ public class HtmlDocumentationWriter implements
DocumentationWriter {
xmlStreamWriter.writeCharacters(".");
xmlStreamWriter.writeEndElement();
- xmlStreamWriter.writeStartElement("table");
- xmlStreamWriter.writeAttribute("id", "properties");
+ xmlStreamWriter.writeStartElement(TABLE);
+ xmlStreamWriter.writeAttribute(ID, "properties");
// write the header row
- xmlStreamWriter.writeStartElement("tr");
- writeSimpleElement(xmlStreamWriter, "th", "Display Name");
- writeSimpleElement(xmlStreamWriter, "th", "API Name");
- writeSimpleElement(xmlStreamWriter, "th", "Default Value");
- writeSimpleElement(xmlStreamWriter, "th", "Allowable Values");
- writeSimpleElement(xmlStreamWriter, "th", "Description");
+ xmlStreamWriter.writeStartElement(TR);
+ writeSimpleElement(xmlStreamWriter, TH, "Display Name");
+ writeSimpleElement(xmlStreamWriter, TH, "API Name");
+ writeSimpleElement(xmlStreamWriter, TH, "Default Value");
+ writeSimpleElement(xmlStreamWriter, TH, "Allowable Values");
+ writeSimpleElement(xmlStreamWriter, TH, "Description");
xmlStreamWriter.writeEndElement();
// write the individual properties
for (PropertyDescriptor property : properties) {
- xmlStreamWriter.writeStartElement("tr");
- xmlStreamWriter.writeStartElement("td");
- xmlStreamWriter.writeAttribute("id", "name");
+ xmlStreamWriter.writeStartElement(TR);
+ xmlStreamWriter.writeStartElement(TD);
+ xmlStreamWriter.writeAttribute(ID, "name");
if (property.isRequired()) {
- writeSimpleElement(xmlStreamWriter, "strong",
property.getDisplayName());
+ writeSimpleElement(xmlStreamWriter, STRONG,
property.getDisplayName());
} else {
xmlStreamWriter.writeCharacters(property.getDisplayName());
}
xmlStreamWriter.writeEndElement();
- writeSimpleElement(xmlStreamWriter, "td", property.getName());
- writeSimpleElement(xmlStreamWriter, "td",
getDefaultValue(property), "default-value");
- xmlStreamWriter.writeStartElement("td");
- xmlStreamWriter.writeAttribute("id", "allowable-values");
+ writeSimpleElement(xmlStreamWriter, TD, property.getName());
+ writeSimpleElement(xmlStreamWriter, TD,
getDefaultValue(property), "default-value");
+ xmlStreamWriter.writeStartElement(TD);
+ xmlStreamWriter.writeAttribute(ID, "allowable-values");
writeValidValues(xmlStreamWriter, property);
xmlStreamWriter.writeEndElement();
- xmlStreamWriter.writeStartElement("td");
- xmlStreamWriter.writeAttribute("id", "description");
- if (property.getDescription() != null &&
property.getDescription().trim().length() > 0) {
+ xmlStreamWriter.writeStartElement(TD);
+ xmlStreamWriter.writeAttribute(ID, "description");
+ if (property.getDescription() != null &&
!property.getDescription().trim().isEmpty()) {
xmlStreamWriter.writeCharacters(property.getDescription());
} else {
- xmlStreamWriter.writeCharacters("No Description
Provided.");
+ xmlStreamWriter.writeCharacters(NO_DESCRIPTION);
}
if (property.isSensitive()) {
- xmlStreamWriter.writeEmptyElement("br");
- writeSimpleElement(xmlStreamWriter, "strong", "Sensitive
Property: true");
+ xmlStreamWriter.writeEmptyElement(BR);
+ writeSimpleElement(xmlStreamWriter, STRONG, "Sensitive
Property: true");
}
final ResourceDefinition resourceDefinition =
property.getResourceDefinition();
if (resourceDefinition != null) {
- xmlStreamWriter.writeEmptyElement("br");
- xmlStreamWriter.writeEmptyElement("br");
- xmlStreamWriter.writeStartElement("strong");
+ xmlStreamWriter.writeEmptyElement(BR);
+ xmlStreamWriter.writeEmptyElement(BR);
+ xmlStreamWriter.writeStartElement(STRONG);
final ResourceCardinality cardinality =
resourceDefinition.getCardinality();
final Set<ResourceType> resourceTypes =
resourceDefinition.getResourceTypes();
@@ -667,32 +430,32 @@ public class HtmlDocumentationWriter implements
DocumentationWriter {
xmlStreamWriter.writeCharacters("This property
expects a comma-separated list of " + resourceTypes.iterator().next() + "
resources");
} else {
xmlStreamWriter.writeCharacters("This property
expects a comma-separated list of resources. Each of the resources may be of
any of the following types: " +
-
StringUtils.join(resourceDefinition.getResourceTypes(), ", "));
+
StringUtils.join(resourceDefinition.getResourceTypes(), ", "));
}
} else {
if (resourceTypes.size() == 1) {
xmlStreamWriter.writeCharacters("This property
requires exactly one " + resourceTypes.iterator().next() + " to be provided.");
} else {
xmlStreamWriter.writeCharacters("This property
requires exactly one resource to be provided. That resource may be any of the
following types: " +
-
StringUtils.join(resourceDefinition.getResourceTypes(), ", "));
+
StringUtils.join(resourceDefinition.getResourceTypes(), ", "));
}
}
xmlStreamWriter.writeCharacters(".");
xmlStreamWriter.writeEndElement();
- xmlStreamWriter.writeEmptyElement("br");
+ xmlStreamWriter.writeEmptyElement(BR);
}
if (property.isExpressionLanguageSupported()) {
- xmlStreamWriter.writeEmptyElement("br");
+ xmlStreamWriter.writeEmptyElement(BR);
String text = "Supports Expression Language: true";
final String perFF = " (will be evaluated using flow file
attributes and Environment variables)";
final String registry = " (will be evaluated using
Environment variables only)";
final InputRequirement inputRequirement =
configurableComponent.getClass().getAnnotation(InputRequirement.class);
- switch(property.getExpressionLanguageScope()) {
+ switch (property.getExpressionLanguageScope()) {
case FLOWFILE_ATTRIBUTES:
- if(inputRequirement != null &&
inputRequirement.value().equals(Requirement.INPUT_FORBIDDEN)) {
+ if (inputRequirement != null &&
inputRequirement.value().equals(Requirement.INPUT_FORBIDDEN)) {
text += registry;
} else {
text += perFF;
@@ -708,21 +471,21 @@ public class HtmlDocumentationWriter implements
DocumentationWriter {
break;
}
- writeSimpleElement(xmlStreamWriter, "strong", text);
+ writeSimpleElement(xmlStreamWriter, STRONG, text);
}
final Set<PropertyDependency> dependencies =
property.getDependencies();
if (!dependencies.isEmpty()) {
- xmlStreamWriter.writeEmptyElement("br");
- xmlStreamWriter.writeEmptyElement("br");
+ xmlStreamWriter.writeEmptyElement(BR);
+ xmlStreamWriter.writeEmptyElement(BR);
final boolean capitalizeThe;
if (dependencies.size() == 1) {
- writeSimpleElement(xmlStreamWriter, "strong", "This
Property is only considered if ");
+ writeSimpleElement(xmlStreamWriter, STRONG, "This
Property is only considered if ");
capitalizeThe = false;
} else {
- writeSimpleElement(xmlStreamWriter, "strong", "This
Property is only considered if all of the following conditions are met:");
- xmlStreamWriter.writeStartElement("ul");
+ writeSimpleElement(xmlStreamWriter, STRONG, "This
Property is only considered if all of the following conditions are met:");
+ xmlStreamWriter.writeStartElement(UL);
capitalizeThe = true;
}
@@ -778,7 +541,7 @@ public class HtmlDocumentationWriter implements
DocumentationWriter {
}
}
- final String elementName = dependencies.size() > 1 ?
"li" : "strong";
+ final String elementName = dependencies.size() > 1 ?
LI : STRONG;
writeSimpleElement(xmlStreamWriter, elementName,
prefix + suffix);
}
@@ -795,7 +558,7 @@ public class HtmlDocumentationWriter implements
DocumentationWriter {
xmlStreamWriter.writeEndElement();
} else {
- writeSimpleElement(xmlStreamWriter, "p", "This component has no
required or optional properties.");
+ writeSimpleElement(xmlStreamWriter, P, NO_PROPERTIES);
}
}
@@ -819,10 +582,10 @@ public class HtmlDocumentationWriter implements
DocumentationWriter {
}
/**
- * Indicates whether or not the component contains at least one property
that supports Expression Language.
+ * Indicates whether the component contains at least one property that
supports Expression Language.
*
* @param component the component to interrogate
- * @return whether or not the component contains at least one sensitive
property.
+ * @return whether the component contains at least one sensitive property.
*/
private boolean containsExpressionLanguage(final ConfigurableComponent
component) {
for (PropertyDescriptor descriptor :
component.getPropertyDescriptors()) {
@@ -833,43 +596,42 @@ public class HtmlDocumentationWriter implements
DocumentationWriter {
return false;
}
-
- private void writeDynamicProperties(final ConfigurableComponent
configurableComponent,
- final XMLStreamWriter xmlStreamWriter) throws XMLStreamException {
-
+ @Override
+ void writeDynamicProperties(final ConfigurableComponent
configurableComponent, final XMLStreamWriter xmlStreamWriter) throws
XMLStreamException {
final List<DynamicProperty> dynamicProperties =
getDynamicProperties(configurableComponent);
- if (dynamicProperties.size() > 0) {
- writeSimpleElement(xmlStreamWriter, "h3", "Dynamic Properties: ");
+ if (!dynamicProperties.isEmpty()) {
+ writeSimpleElement(xmlStreamWriter, H3, "Dynamic Properties: ");
writeSupportsSensitiveDynamicProperties(configurableComponent,
xmlStreamWriter);
- xmlStreamWriter.writeStartElement("p");
- xmlStreamWriter
- .writeCharacters("Dynamic Properties allow the user to
specify both the name and value of a property.");
- xmlStreamWriter.writeStartElement("table");
- xmlStreamWriter.writeAttribute("id", "dynamic-properties");
- xmlStreamWriter.writeStartElement("tr");
- writeSimpleElement(xmlStreamWriter, "th", "Name");
- writeSimpleElement(xmlStreamWriter, "th", "Value");
- writeSimpleElement(xmlStreamWriter, "th", "Description");
+ xmlStreamWriter.writeStartElement(P);
+ xmlStreamWriter.writeCharacters("Dynamic Properties allow the user
to specify both the name and value of a property.");
+ xmlStreamWriter.writeStartElement(TABLE);
+ xmlStreamWriter.writeAttribute(ID, "dynamic-properties");
+ xmlStreamWriter.writeStartElement(TR);
+ writeSimpleElement(xmlStreamWriter, TH, "Name");
+ writeSimpleElement(xmlStreamWriter, TH, "Value");
+ writeSimpleElement(xmlStreamWriter, TH, "Description");
xmlStreamWriter.writeEndElement();
for (final DynamicProperty dynamicProperty : dynamicProperties) {
- xmlStreamWriter.writeStartElement("tr");
- writeSimpleElement(xmlStreamWriter, "td",
dynamicProperty.name(), "name");
- writeSimpleElement(xmlStreamWriter, "td",
dynamicProperty.value(), "value");
- xmlStreamWriter.writeStartElement("td");
+ xmlStreamWriter.writeStartElement(TR);
+ writeSimpleElement(xmlStreamWriter, TD,
dynamicProperty.name(), "name");
+ writeSimpleElement(xmlStreamWriter, TD,
dynamicProperty.value(), "value");
+ xmlStreamWriter.writeStartElement(TD);
xmlStreamWriter.writeCharacters(dynamicProperty.description());
- xmlStreamWriter.writeEmptyElement("br");
+ xmlStreamWriter.writeEmptyElement(BR);
final String text = switch
(dynamicProperty.expressionLanguageScope()) {
- case FLOWFILE_ATTRIBUTES -> "Supports Expression Language:
true (will be evaluated using flow file attributes and Environment variables)";
- case ENVIRONMENT -> "Supports Expression Language: true
(will be evaluated using Environment variables only)";
+ case FLOWFILE_ATTRIBUTES ->
+ "Supports Expression Language: true (will be
evaluated using flow file attributes and Environment variables)";
+ case ENVIRONMENT ->
+ "Supports Expression Language: true (will be
evaluated using Environment variables only)";
default -> "Supports Expression Language: false";
};
- writeSimpleElement(xmlStreamWriter, "strong", text);
+ writeSimpleElement(xmlStreamWriter, STRONG, text);
xmlStreamWriter.writeEndElement();
xmlStreamWriter.writeEndElement();
}
@@ -883,15 +645,15 @@ public class HtmlDocumentationWriter implements
DocumentationWriter {
final boolean supportsSensitiveDynamicProperties =
configurableComponent.getClass().isAnnotationPresent(SupportsSensitiveDynamicProperties.class);
final String sensitiveDynamicPropertiesLabel =
supportsSensitiveDynamicProperties ? "Yes" : "No";
- writer.writeStartElement("p");
+ writer.writeStartElement(P);
writer.writeCharacters("Supports Sensitive Dynamic Properties: ");
- writeSimpleElement(writer, "strong", sensitiveDynamicPropertiesLabel);
+ writeSimpleElement(writer, STRONG, sensitiveDynamicPropertiesLabel);
writer.writeEndElement();
}
- private List<DynamicProperty> getDynamicProperties(ConfigurableComponent
configurableComponent) {
+ private List<DynamicProperty> getDynamicProperties(final
ConfigurableComponent configurableComponent) {
final List<DynamicProperty> dynamicProperties = new ArrayList<>();
final DynamicProperties dynProps =
configurableComponent.getClass().getAnnotation(DynamicProperties.class);
if (dynProps != null) {
@@ -906,32 +668,27 @@ public class HtmlDocumentationWriter implements
DocumentationWriter {
return dynamicProperties;
}
- private void writeValidValueDescription(XMLStreamWriter xmlStreamWriter,
String description)
- throws XMLStreamException {
+ private void writeValidValueDescription(XMLStreamWriter xmlStreamWriter,
String description) throws XMLStreamException {
xmlStreamWriter.writeCharacters(" ");
xmlStreamWriter.writeStartElement("img");
xmlStreamWriter.writeAttribute("src",
"../../../../../html/images/iconInfo.png");
xmlStreamWriter.writeAttribute("alt", description);
xmlStreamWriter.writeAttribute("title", description);
xmlStreamWriter.writeEndElement();
-
}
/**
- * Interrogates a PropertyDescriptor to get a list of AllowableValues, if
- * there are none, nothing is written to the stream.
+ * Interrogates a PropertyDescriptor to get a list of AllowableValues, if
there are none, nothing is written to the stream.
*
* @param xmlStreamWriter the stream writer to use
- * @param property the property to describe
- * @throws XMLStreamException thrown if there was a problem writing to the
- * XML Stream
+ * @param property the property to describe
+ * @throws XMLStreamException thrown if there was a problem writing to the
XML Stream
*/
- protected void writeValidValues(XMLStreamWriter xmlStreamWriter,
PropertyDescriptor property)
- throws XMLStreamException {
- if (property.getAllowableValues() != null &&
property.getAllowableValues().size() > 0) {
- xmlStreamWriter.writeStartElement("ul");
+ protected void writeValidValues(XMLStreamWriter xmlStreamWriter,
PropertyDescriptor property) throws XMLStreamException {
+ if (property.getAllowableValues() != null &&
!property.getAllowableValues().isEmpty()) {
+ xmlStreamWriter.writeStartElement(UL);
for (AllowableValue value : property.getAllowableValues()) {
- xmlStreamWriter.writeStartElement("li");
+ xmlStreamWriter.writeStartElement(LI);
xmlStreamWriter.writeCharacters(value.getDisplayName());
if (value.getDescription() != null) {
@@ -944,16 +701,16 @@ public class HtmlDocumentationWriter implements
DocumentationWriter {
} else if (property.getControllerServiceDefinition() != null) {
Class<? extends ControllerService> controllerServiceClass =
property.getControllerServiceDefinition();
- writeSimpleElement(xmlStreamWriter, "strong", "Controller Service
API: ");
- xmlStreamWriter.writeEmptyElement("br");
+ writeSimpleElement(xmlStreamWriter, STRONG, "Controller Service
API: ");
+ xmlStreamWriter.writeEmptyElement(BR);
xmlStreamWriter.writeCharacters(controllerServiceClass.getSimpleName());
final Class<? extends ControllerService>[] serviceImplementations
= lookupControllerServiceImpls(controllerServiceClass);
- xmlStreamWriter.writeEmptyElement("br");
+ xmlStreamWriter.writeEmptyElement(BR);
if (serviceImplementations.length > 0) {
final String title = serviceImplementations.length > 1 ?
"Implementations: " : "Implementation: ";
- writeSimpleElement(xmlStreamWriter, "strong", title);
+ writeSimpleElement(xmlStreamWriter, STRONG, title);
iterateAndLinkComponents(xmlStreamWriter,
serviceImplementations, null, "<br>", controllerServiceClass.getSimpleName());
} else {
xmlStreamWriter.writeCharacters("No implementations found.");
@@ -961,86 +718,22 @@ public class HtmlDocumentationWriter implements
DocumentationWriter {
}
}
- /**
- * Writes a begin element, an id attribute(if specified), then text, then
- * end element for element of the users choosing. Example: <p
- * id="p-id">text</p>
- *
- * @param writer the stream writer to use
- * @param elementName the name of the element
- * @param characters the text of the element
- * @param id the id of the element. specifying null will cause no element
to
- * be written.
- * @throws XMLStreamException xse
- */
- protected static void writeSimpleElement(final XMLStreamWriter writer,
final String elementName,
- final String characters, String id) throws XMLStreamException {
- writer.writeStartElement(elementName);
-
- if (characters != null) {
- if (id != null) {
- writer.writeAttribute("id", id);
- }
- writer.writeCharacters(characters);
- }
-
- writer.writeEndElement();
- }
-
- /**
- * Writes a begin element, then text, then end element for the element of a
- * users choosing. Example: <p>text</p>
- *
- * @param writer the stream writer to use
- * @param elementName the name of the element
- * @param characters the characters to insert into the element
- */
- protected static void writeSimpleElement(final XMLStreamWriter writer,
final String elementName,
- final String characters) throws XMLStreamException {
- writeSimpleElement(writer, elementName, characters, null);
- }
-
- /**
- * A helper method to write a link
- *
- * @param xmlStreamWriter the stream to write to
- * @param text the text of the link
- * @param location the location of the link
- * @throws XMLStreamException thrown if there was a problem writing to the
- * stream
- */
- protected void writeLink(final XMLStreamWriter xmlStreamWriter, final
String text, final String location)
- throws XMLStreamException {
- xmlStreamWriter.writeStartElement("a");
- xmlStreamWriter.writeAttribute("href", location);
- xmlStreamWriter.writeCharacters(text);
- xmlStreamWriter.writeEndElement();
- }
-
- /**
- * Writes all the system resource considerations for this component
- *
- * @param configurableComponent the component to describe
- * @param xmlStreamWriter the xml stream writer to use
- * @throws XMLStreamException thrown if there was a problem writing the XML
- */
- private void writeSystemResourceConsiderationInfo(ConfigurableComponent
configurableComponent, XMLStreamWriter xmlStreamWriter)
- throws XMLStreamException {
-
+ @Override
+ void writeSystemResourceConsiderationInfo(final ConfigurableComponent
configurableComponent, XMLStreamWriter xmlStreamWriter) throws
XMLStreamException {
SystemResourceConsideration[] systemResourceConsiderations =
configurableComponent.getClass().getAnnotationsByType(SystemResourceConsideration.class);
- writeSimpleElement(xmlStreamWriter, "h3", "System Resource
Considerations:");
+ writeSimpleElement(xmlStreamWriter, H3, "System Resource
Considerations:");
if (systemResourceConsiderations.length > 0) {
- xmlStreamWriter.writeStartElement("table");
- xmlStreamWriter.writeAttribute("id",
"system-resource-considerations");
- xmlStreamWriter.writeStartElement("tr");
- writeSimpleElement(xmlStreamWriter, "th", "Resource");
- writeSimpleElement(xmlStreamWriter, "th", "Description");
+ xmlStreamWriter.writeStartElement(TABLE);
+ xmlStreamWriter.writeAttribute(ID,
"system-resource-considerations");
+ xmlStreamWriter.writeStartElement(TR);
+ writeSimpleElement(xmlStreamWriter, TH, "Resource");
+ writeSimpleElement(xmlStreamWriter, TH, "Description");
xmlStreamWriter.writeEndElement();
for (SystemResourceConsideration systemResourceConsideration :
systemResourceConsiderations) {
- xmlStreamWriter.writeStartElement("tr");
- writeSimpleElement(xmlStreamWriter, "td",
systemResourceConsideration.resource().name());
- writeSimpleElement(xmlStreamWriter, "td",
systemResourceConsideration.description().trim().isEmpty()
+ xmlStreamWriter.writeStartElement(TR);
+ writeSimpleElement(xmlStreamWriter, TD,
systemResourceConsideration.resource().name());
+ writeSimpleElement(xmlStreamWriter, TD,
systemResourceConsideration.description().trim().isEmpty()
? "Not Specified" :
systemResourceConsideration.description());
xmlStreamWriter.writeEndElement();
}
@@ -1059,9 +752,7 @@ public class HtmlDocumentationWriter implements
DocumentationWriter {
* @return an array of controller services that implement the controller
service API
*/
@SuppressWarnings("unchecked")
- private Class<? extends ControllerService>[] lookupControllerServiceImpls(
- final Class<? extends ControllerService> parent) {
-
+ private Class<? extends ControllerService>[]
lookupControllerServiceImpls(final Class<? extends ControllerService> parent) {
final List<Class<? extends ControllerService>> implementations = new
ArrayList<>();
// first get all ControllerService implementations
@@ -1082,38 +773,34 @@ public class HtmlDocumentationWriter implements
DocumentationWriter {
/**
* Writes a link to another configurable component
*
- * @param xmlStreamWriter the xml stream writer
- * @param linkedComponents the array of configurable component to link to
- * @param classNames the array of class names in string format to link to
- * @param separator a separator used to split the values (in case more
than 1. If the separator is enclosed in
- * between "<" and ">" (.e.g "<br>" it is treated as a
tag and written to the xmlStreamWriter as an
- * empty tag
+ * @param xmlStreamWriter the xml stream writer
+ * @param linkedComponents the array of configurable component to link to
+ * @param classNames the array of class names in string format to
link to
+ * @param separator a separator used to split the values (in case
more than 1. If the separator is enclosed in
+ * between "<" and ">" (.e.g "<br>" it is treated
as a tag and written to the xmlStreamWriter as an empty tag
* @param sourceContextName the source context/name of the item being
linked
* @throws XMLStreamException thrown if there is a problem writing the XML
*/
protected void iterateAndLinkComponents(final XMLStreamWriter
xmlStreamWriter, final Class<? extends ConfigurableComponent>[]
linkedComponents,
- final String[] classNames, final String separator, final String
sourceContextName)
- throws XMLStreamException {
+ final String[] classNames, final
String separator, final String sourceContextName) throws XMLStreamException {
String effectiveSeparator = separator;
// Treat the the possible separators
final boolean separatorIsElement = effectiveSeparator.startsWith("<")
&& effectiveSeparator.endsWith(">");
// Whatever the result, strip the possible < and > characters
- effectiveSeparator = effectiveSeparator.replaceAll("<([^>]*)>","$1");
+ effectiveSeparator = effectiveSeparator.replaceAll("<([^>]*)>", "$1");
int index = 0;
- for (final Class<? extends ConfigurableComponent> linkedComponent :
linkedComponents ) {
+ for (final Class<? extends ConfigurableComponent> linkedComponent :
linkedComponents) {
final String linkedComponentName = linkedComponent.getName();
final List<Bundle> linkedComponentBundles =
extensionManager.getBundles(linkedComponentName);
- if (linkedComponentBundles != null &&
linkedComponentBundles.size() > 0) {
- final Bundle firstLinkedComponentBundle =
linkedComponentBundles.get(0);
+ if (linkedComponentBundles != null &&
!linkedComponentBundles.isEmpty()) {
+ final Bundle firstLinkedComponentBundle =
linkedComponentBundles.getFirst();
final BundleCoordinate coordinate =
firstLinkedComponentBundle.getBundleDetails().getCoordinate();
final String group = coordinate.getGroup();
final String id = coordinate.getId();
final String version = coordinate.getVersion();
-
-
if (index != 0) {
if (separatorIsElement) {
xmlStreamWriter.writeEmptyElement(effectiveSeparator);
@@ -1129,7 +816,7 @@ public class HtmlDocumentationWriter implements
DocumentationWriter {
}
}
- if (classNames!= null) {
+ if (classNames != null) {
for (final String className : classNames) {
if (index != 0) {
if (separatorIsElement) {
@@ -1141,8 +828,8 @@ public class HtmlDocumentationWriter implements
DocumentationWriter {
final List<Bundle> linkedComponentBundles =
extensionManager.getBundles(className);
- if (linkedComponentBundles != null &&
linkedComponentBundles.size() > 0) {
- final Bundle firstBundle = linkedComponentBundles.get(0);
+ if (linkedComponentBundles != null &&
!linkedComponentBundles.isEmpty()) {
+ final Bundle firstBundle =
linkedComponentBundles.getFirst();
final BundleCoordinate firstCoordinate =
firstBundle.getBundleDetails().getCoordinate();
final String group = firstCoordinate.getGroup();
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/html/HtmlProcessorDocumentationWriter.java
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/html/HtmlProcessorDocumentationWriter.java
index 78166da3c9..1e7d7cca38 100644
---
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/html/HtmlProcessorDocumentationWriter.java
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/html/HtmlProcessorDocumentationWriter.java
@@ -85,20 +85,20 @@ public class HtmlProcessorDocumentationWriter extends
HtmlDocumentationWriter {
throws XMLStreamException {
List<ReadsAttribute> attributesRead = getReadsAttributes(processor);
- writeSimpleElement(xmlStreamWriter, "h3", "Reads Attributes: ");
- if (attributesRead.size() > 0) {
- xmlStreamWriter.writeStartElement("table");
- xmlStreamWriter.writeAttribute("id", "reads-attributes");
- xmlStreamWriter.writeStartElement("tr");
- writeSimpleElement(xmlStreamWriter, "th", "Name");
- writeSimpleElement(xmlStreamWriter, "th", "Description");
+ writeSimpleElement(xmlStreamWriter, H3, "Reads Attributes: ");
+ if (!attributesRead.isEmpty()) {
+ xmlStreamWriter.writeStartElement(TABLE);
+ xmlStreamWriter.writeAttribute(ID, "reads-attributes");
+ xmlStreamWriter.writeStartElement(TR);
+ writeSimpleElement(xmlStreamWriter, TH, "Name");
+ writeSimpleElement(xmlStreamWriter, TH, "Description");
xmlStreamWriter.writeEndElement();
for (ReadsAttribute attribute : attributesRead) {
- xmlStreamWriter.writeStartElement("tr");
- writeSimpleElement(xmlStreamWriter, "td",
+ xmlStreamWriter.writeStartElement(TR);
+ writeSimpleElement(xmlStreamWriter, TD,
defaultIfBlank(attribute.attribute(), "Not
Specified"));
// TODO allow for HTML characters here.
- writeSimpleElement(xmlStreamWriter, "td",
+ writeSimpleElement(xmlStreamWriter, TD,
defaultIfBlank(attribute.description(), "Not
Specified"));
xmlStreamWriter.writeEndElement();
@@ -121,20 +121,20 @@ public class HtmlProcessorDocumentationWriter extends
HtmlDocumentationWriter {
throws XMLStreamException {
List<WritesAttribute> attributesRead = getWritesAttributes(processor);
- writeSimpleElement(xmlStreamWriter, "h3", "Writes Attributes: ");
- if (attributesRead.size() > 0) {
- xmlStreamWriter.writeStartElement("table");
- xmlStreamWriter.writeAttribute("id", "writes-attributes");
- xmlStreamWriter.writeStartElement("tr");
- writeSimpleElement(xmlStreamWriter, "th", "Name");
- writeSimpleElement(xmlStreamWriter, "th", "Description");
+ writeSimpleElement(xmlStreamWriter, H3, "Writes Attributes: ");
+ if (!attributesRead.isEmpty()) {
+ xmlStreamWriter.writeStartElement(TABLE);
+ xmlStreamWriter.writeAttribute(ID, "writes-attributes");
+ xmlStreamWriter.writeStartElement(TR);
+ writeSimpleElement(xmlStreamWriter, TH, "Name");
+ writeSimpleElement(xmlStreamWriter, TH, "Description");
xmlStreamWriter.writeEndElement();
for (WritesAttribute attribute : attributesRead) {
- xmlStreamWriter.writeStartElement("tr");
- writeSimpleElement(xmlStreamWriter, "td",
+ xmlStreamWriter.writeStartElement(TR);
+ writeSimpleElement(xmlStreamWriter, TD,
defaultIfBlank(attribute.attribute(), "Not
Specified"));
// TODO allow for HTML characters here.
- writeSimpleElement(xmlStreamWriter, "td",
+ writeSimpleElement(xmlStreamWriter, TD,
defaultIfBlank(attribute.description(), "Not
Specified"));
xmlStreamWriter.writeEndElement();
}
@@ -199,20 +199,20 @@ public class HtmlProcessorDocumentationWriter extends
HtmlDocumentationWriter {
private void writeRelationships(final Processor processor, final
XMLStreamWriter xmlStreamWriter)
throws XMLStreamException {
- writeSimpleElement(xmlStreamWriter, "h3", "Relationships: ");
+ writeSimpleElement(xmlStreamWriter, H3, "Relationships: ");
- if (processor.getRelationships().size() > 0) {
- xmlStreamWriter.writeStartElement("table");
- xmlStreamWriter.writeAttribute("id", "relationships");
- xmlStreamWriter.writeStartElement("tr");
- writeSimpleElement(xmlStreamWriter, "th", "Name");
- writeSimpleElement(xmlStreamWriter, "th", "Description");
+ if (!processor.getRelationships().isEmpty()) {
+ xmlStreamWriter.writeStartElement(TABLE);
+ xmlStreamWriter.writeAttribute(ID, "relationships");
+ xmlStreamWriter.writeStartElement(TR);
+ writeSimpleElement(xmlStreamWriter, TH, "Name");
+ writeSimpleElement(xmlStreamWriter, TH, "Description");
xmlStreamWriter.writeEndElement();
for (Relationship relationship : processor.getRelationships()) {
- xmlStreamWriter.writeStartElement("tr");
- writeSimpleElement(xmlStreamWriter, "td",
relationship.getName());
- writeSimpleElement(xmlStreamWriter, "td",
relationship.getDescription());
+ xmlStreamWriter.writeStartElement(TR);
+ writeSimpleElement(xmlStreamWriter, TD,
relationship.getName());
+ writeSimpleElement(xmlStreamWriter, TD,
relationship.getDescription());
xmlStreamWriter.writeEndElement();
}
xmlStreamWriter.writeEndElement();
@@ -225,21 +225,21 @@ public class HtmlProcessorDocumentationWriter extends
HtmlDocumentationWriter {
List<DynamicRelationship> dynamicRelationships =
getDynamicRelationships(processor);
- if (dynamicRelationships.size() > 0) {
- writeSimpleElement(xmlStreamWriter, "h3", "Dynamic Relationships:
");
- xmlStreamWriter.writeStartElement("p");
+ if (!dynamicRelationships.isEmpty()) {
+ writeSimpleElement(xmlStreamWriter, H3, "Dynamic Relationships: ");
+ xmlStreamWriter.writeStartElement(P);
xmlStreamWriter.writeCharacters("A Dynamic Relationship may be
created based on how the user configures the Processor.");
- xmlStreamWriter.writeStartElement("table");
- xmlStreamWriter.writeAttribute("id", "dynamic-relationships");
- xmlStreamWriter.writeStartElement("tr");
- writeSimpleElement(xmlStreamWriter, "th", "Name");
- writeSimpleElement(xmlStreamWriter, "th", "Description");
+ xmlStreamWriter.writeStartElement(TABLE);
+ xmlStreamWriter.writeAttribute(ID, "dynamic-relationships");
+ xmlStreamWriter.writeStartElement(TR);
+ writeSimpleElement(xmlStreamWriter, TH, "Name");
+ writeSimpleElement(xmlStreamWriter, TH, "Description");
xmlStreamWriter.writeEndElement();
for (DynamicRelationship dynamicRelationship :
dynamicRelationships) {
- xmlStreamWriter.writeStartElement("tr");
- writeSimpleElement(xmlStreamWriter, "td",
dynamicRelationship.name());
- writeSimpleElement(xmlStreamWriter, "td",
dynamicRelationship.description());
+ xmlStreamWriter.writeStartElement(TR);
+ writeSimpleElement(xmlStreamWriter, TD,
dynamicRelationship.name());
+ writeSimpleElement(xmlStreamWriter, TD,
dynamicRelationship.description());
xmlStreamWriter.writeEndElement();
}
xmlStreamWriter.writeEndElement();
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/html/HtmlPythonProcessorDocumentationWriter.java
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/html/HtmlPythonProcessorDocumentationWriter.java
new file mode 100644
index 0000000000..437b556470
--- /dev/null
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/html/HtmlPythonProcessorDocumentationWriter.java
@@ -0,0 +1,280 @@
+/*
+ * 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.documentation.html;
+
+import org.apache.nifi.expression.ExpressionLanguageScope;
+import org.apache.nifi.python.PythonProcessorDetails;
+import
org.apache.nifi.python.processor.documentation.MultiProcessorUseCaseDetails;
+import
org.apache.nifi.python.processor.documentation.ProcessorConfigurationDetails;
+import org.apache.nifi.python.processor.documentation.PropertyDescription;
+import org.apache.nifi.python.processor.documentation.UseCaseDetails;
+import org.apache.nifi.util.StringUtils;
+
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import java.util.List;
+
+import static org.apache.nifi.expression.ExpressionLanguageScope.NONE;
+
+public class HtmlPythonProcessorDocumentationWriter extends
AbstractHtmlDocumentationWriter<PythonProcessorDetails> {
+
+ @Override
+ String getTitle(final PythonProcessorDetails processorDetails) {
+ return processorDetails.getProcessorType();
+ }
+
+ @Override
+ void writeDeprecationWarning(final PythonProcessorDetails
processorDetails, XMLStreamWriter xmlStreamWriter) {
+ // Not supported
+ }
+
+ @Override
+ String getDescription(final PythonProcessorDetails processorDetails) {
+ return processorDetails.getCapabilityDescription() != null ?
processorDetails.getCapabilityDescription() : NO_DESCRIPTION;
+ }
+
+ @Override
+ void writeInputRequirementInfo(final PythonProcessorDetails
processorDetails, XMLStreamWriter xmlStreamWriter) {
+ // Not supported
+ }
+
+ @Override
+ void writeStatefulInfo(final PythonProcessorDetails processorDetails,
XMLStreamWriter xmlStreamWriter) {
+ // Not supported
+ }
+
+ @Override
+ void writeRestrictedInfo(final PythonProcessorDetails processorDetails,
XMLStreamWriter xmlStreamWriter) {
+ // Not supported
+ }
+
+ @Override
+ void writeSeeAlso(final PythonProcessorDetails processorDetails,
XMLStreamWriter xmlStreamWriter) {
+ // Not supported
+ }
+
+ @Override
+ void writeTags(final PythonProcessorDetails processorDetails,
XMLStreamWriter xmlStreamWriter) throws XMLStreamException {
+ final List<String> tags = processorDetails.getTags();
+
+ xmlStreamWriter.writeStartElement(H3);
+ xmlStreamWriter.writeCharacters("Tags: ");
+ xmlStreamWriter.writeEndElement();
+ xmlStreamWriter.writeStartElement(P);
+
+ if (tags != null && !tags.isEmpty()) {
+ final String tagString = String.join(", ", tags);
+ xmlStreamWriter.writeCharacters(tagString);
+ } else {
+ xmlStreamWriter.writeCharacters(NO_TAGS);
+ }
+
+ xmlStreamWriter.writeEndElement();
+ }
+
+ @Override
+ void writeUseCases(final PythonProcessorDetails processorDetails,
XMLStreamWriter xmlStreamWriter) throws XMLStreamException {
+ final List<UseCaseDetails> useCaseDetailsList =
processorDetails.getUseCases();
+ if (useCaseDetailsList.isEmpty()) {
+ return;
+ }
+
+ writeSimpleElement(xmlStreamWriter, H2, "Example Use Cases:");
+
+ for (final UseCaseDetails useCaseDetails : useCaseDetailsList) {
+ writeSimpleElement(xmlStreamWriter, H3, "Use Case:");
+ writeSimpleElement(xmlStreamWriter, P,
useCaseDetails.getDescription());
+
+ final String notes = useCaseDetails.getNotes();
+ if (!StringUtils.isEmpty(notes)) {
+ writeSimpleElement(xmlStreamWriter, H4, "Notes:");
+
+ final String[] splits = notes.split("\\n");
+ for (final String split : splits) {
+ writeSimpleElement(xmlStreamWriter, P, split);
+ }
+ }
+
+ final List<String> keywords = useCaseDetails.getKeywords();
+ if (!keywords.isEmpty()) {
+ writeSimpleElement(xmlStreamWriter, H4, "Keywords:");
+ xmlStreamWriter.writeCharacters(String.join(", ", keywords));
+ }
+
+ final String configuration = useCaseDetails.getConfiguration();
+ writeUseCaseConfiguration(configuration, xmlStreamWriter);
+
+ writeSimpleElement(xmlStreamWriter, BR, null);
+ }
+ }
+
+ @Override
+ void writeMultiComponentUseCases(final PythonProcessorDetails
processorDetails, XMLStreamWriter xmlStreamWriter) throws XMLStreamException {
+ final List<MultiProcessorUseCaseDetails> useCaseDetailsList =
processorDetails.getMultiProcessorUseCases();
+ if (useCaseDetailsList.isEmpty()) {
+ return;
+ }
+
+ writeSimpleElement(xmlStreamWriter, H2, "Example Use Cases Involving
Other Components:");
+
+ for (final MultiProcessorUseCaseDetails useCase : useCaseDetailsList) {
+ writeSimpleElement(xmlStreamWriter, H3, "Use Case:");
+ writeSimpleElement(xmlStreamWriter, P, useCase.getDescription());
+
+ final String notes = useCase.getNotes();
+ if (!StringUtils.isEmpty(notes)) {
+ writeSimpleElement(xmlStreamWriter, H4, "Notes:");
+
+ final String[] splits = notes.split("\\n");
+ for (final String split : splits) {
+ writeSimpleElement(xmlStreamWriter, P, split);
+ }
+ }
+
+ final List<String> keywords = useCase.getKeywords();
+ if (!keywords.isEmpty()) {
+ writeSimpleElement(xmlStreamWriter, H4, "Keywords:");
+ xmlStreamWriter.writeCharacters(String.join(", ", keywords));
+ }
+
+ writeSimpleElement(xmlStreamWriter, H4, "Components involved:");
+ final List<ProcessorConfigurationDetails> processorConfigurations
= useCase.getConfigurations();
+ for (final ProcessorConfigurationDetails processorConfiguration :
processorConfigurations) {
+ writeSimpleElement(xmlStreamWriter, STRONG, "Component Type:
");
+ writeSimpleElement(xmlStreamWriter, SPAN,
processorConfiguration.getProcessorType());
+
+ final String configuration =
processorConfiguration.getConfiguration();
+ writeUseCaseConfiguration(configuration, xmlStreamWriter);
+
+ writeSimpleElement(xmlStreamWriter, BR, null);
+ }
+
+ writeSimpleElement(xmlStreamWriter, BR, null);
+ }
+ }
+
+ @Override
+ void writeProperties(final PythonProcessorDetails processorDetails,
XMLStreamWriter xmlStreamWriter) throws XMLStreamException {
+ final List<PropertyDescription> properties =
processorDetails.getPropertyDescriptions();
+ writeSimpleElement(xmlStreamWriter, H3, "Properties: ");
+
+ if (!properties.isEmpty()) {
+ final boolean containsExpressionLanguage =
containsExpressionLanguage(processorDetails);
+ xmlStreamWriter.writeStartElement(P);
+ xmlStreamWriter.writeCharacters("In the list below, the names of
required properties appear in ");
+ writeSimpleElement(xmlStreamWriter, STRONG, "bold");
+ xmlStreamWriter.writeCharacters(". Any other properties (not in
bold) are considered optional. " +
+ "The table also indicates any default values");
+ if (containsExpressionLanguage) {
+ xmlStreamWriter.writeCharacters(", and whether a property
supports the ");
+ writeLink(xmlStreamWriter, "NiFi Expression Language",
"../../../../../html/expression-language-guide.html");
+ }
+ xmlStreamWriter.writeCharacters(".");
+ xmlStreamWriter.writeEndElement();
+
+ xmlStreamWriter.writeStartElement(TABLE);
+ xmlStreamWriter.writeAttribute(ID, "properties");
+
+ // write the header row
+ xmlStreamWriter.writeStartElement(TR);
+ writeSimpleElement(xmlStreamWriter, TH, "Display Name");
+ writeSimpleElement(xmlStreamWriter, TH, "API Name");
+ writeSimpleElement(xmlStreamWriter, TH, "Default Value");
+ writeSimpleElement(xmlStreamWriter, TH, "Description");
+ xmlStreamWriter.writeEndElement();
+
+ // write the individual properties
+ for (PropertyDescription property : properties) {
+ xmlStreamWriter.writeStartElement(TR);
+ xmlStreamWriter.writeStartElement(TD);
+ xmlStreamWriter.writeAttribute(ID, "name");
+ if (property.isRequired()) {
+ writeSimpleElement(xmlStreamWriter, STRONG,
property.getDisplayName());
+ } else {
+ xmlStreamWriter.writeCharacters(property.getDisplayName());
+ }
+
+ xmlStreamWriter.writeEndElement();
+ writeSimpleElement(xmlStreamWriter, TD, property.getName());
+ writeSimpleElement(xmlStreamWriter, TD,
property.getDefaultValue(), "default-value");
+ xmlStreamWriter.writeStartElement(TD);
+ xmlStreamWriter.writeAttribute(ID, "description");
+ if (property.getDescription() != null &&
!property.getDescription().trim().isEmpty()) {
+ xmlStreamWriter.writeCharacters(property.getDescription());
+ } else {
+ xmlStreamWriter.writeCharacters(NO_DESCRIPTION);
+ }
+
+ if (property.isSensitive()) {
+ xmlStreamWriter.writeEmptyElement(BR);
+ writeSimpleElement(xmlStreamWriter, STRONG, "Sensitive
Property: true");
+ }
+
+ final ExpressionLanguageScope expressionLanguageScope =
ExpressionLanguageScope.valueOf(property.getExpressionLanguageScope());
+ if (!expressionLanguageScope.equals(NONE)) {
+ xmlStreamWriter.writeEmptyElement(BR);
+ String text = "Supports Expression Language: true";
+ final String perFF = " (will be evaluated using flow file
attributes and Environment variables)";
+ final String registry = " (will be evaluated using
Environment variables only)";
+ final String undefined = " (undefined scope)";
+
+ switch (expressionLanguageScope) {
+ case FLOWFILE_ATTRIBUTES -> text += perFF;
+ case ENVIRONMENT -> text += registry;
+ default -> text += undefined;
+ }
+
+ writeSimpleElement(xmlStreamWriter, STRONG, text);
+ }
+
+ xmlStreamWriter.writeEndElement();
+
+ xmlStreamWriter.writeEndElement();
+ }
+
+ xmlStreamWriter.writeEndElement();
+
+ } else {
+ writeSimpleElement(xmlStreamWriter, P, NO_PROPERTIES);
+ }
+ }
+
+ @Override
+ void writeDynamicProperties(final PythonProcessorDetails processorDetails,
XMLStreamWriter xmlStreamWriter) {
+ // Not supported
+ }
+
+ @Override
+ void writeSystemResourceConsiderationInfo(final PythonProcessorDetails
processorDetails, XMLStreamWriter xmlStreamWriter) {
+ // Not supported
+ }
+
+ /**
+ * Indicates whether the component contains at least one property that
supports Expression Language.
+ *
+ * @param processorDetails the component to interrogate
+ * @return whether the component contains at least one sensitive property.
+ */
+ private boolean containsExpressionLanguage(final PythonProcessorDetails
processorDetails) {
+ for (PropertyDescription description :
processorDetails.getPropertyDescriptions()) {
+ if
(!ExpressionLanguageScope.valueOf(description.getExpressionLanguageScope()).equals(NONE))
{
+ return true;
+ }
+ }
+ return false;
+ }
+}
\ No newline at end of file
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/DocGeneratorTest.java
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/DocGeneratorTest.java
index 2aa2c1c090..097bdd13c6 100644
---
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/DocGeneratorTest.java
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/DocGeneratorTest.java
@@ -81,6 +81,7 @@ public class DocGeneratorTest {
.bundle(bundle)
.extensionType(Processor.class)
.implementationClassName(PROCESSOR_CLASS.getName())
+ .runtime(ExtensionDefinition.ExtensionRuntime.JAVA)
.build();
final Set<ExtensionDefinition> extensions =
Collections.singleton(definition);
when(extensionManager.getExtensions(eq(Processor.class))).thenReturn(extensions);
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/html/HtmlPythonProcessorDocumentationWriterTest.java
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/html/HtmlPythonProcessorDocumentationWriterTest.java
new file mode 100644
index 0000000000..0e5fc964d7
--- /dev/null
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/html/HtmlPythonProcessorDocumentationWriterTest.java
@@ -0,0 +1,187 @@
+/*
+ * 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.documentation.html;
+
+import org.apache.nifi.documentation.DocumentationWriter;
+import org.apache.nifi.python.PythonProcessorDetails;
+import
org.apache.nifi.python.processor.documentation.MultiProcessorUseCaseDetails;
+import
org.apache.nifi.python.processor.documentation.ProcessorConfigurationDetails;
+import org.apache.nifi.python.processor.documentation.PropertyDescription;
+import org.apache.nifi.python.processor.documentation.UseCaseDetails;
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.List;
+
+import static
org.apache.nifi.documentation.html.AbstractHtmlDocumentationWriter.NO_DESCRIPTION;
+import static
org.apache.nifi.documentation.html.AbstractHtmlDocumentationWriter.NO_PROPERTIES;
+import static
org.apache.nifi.documentation.html.AbstractHtmlDocumentationWriter.NO_TAGS;
+import static org.apache.nifi.documentation.html.XmlValidator.assertContains;
+import static
org.apache.nifi.documentation.html.XmlValidator.assertNotContains;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class HtmlPythonProcessorDocumentationWriterTest {
+
+ @Test
+ public void testProcessorDocumentation() throws IOException {
+ final PythonProcessorDetails processorDetails =
getPythonProcessorDetails();
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+
+ final DocumentationWriter<PythonProcessorDetails> writer = new
HtmlPythonProcessorDocumentationWriter();
+ writer.write(processorDetails, outputStream, false);
+
+ final String results = outputStream.toString();
+ XmlValidator.assertXmlValid(results);
+
+ assertContains(results, "This is a test capability description");
+ assertContains(results, "tag1, tag2, tag3");
+
+ final List<PropertyDescription> propertyDescriptions =
getPropertyDescriptions();
+ propertyDescriptions.forEach(propertyDescription -> {
+ assertContains(results, propertyDescription.getDisplayName());
+ assertContains(results, propertyDescription.getDescription());
+ assertContains(results, propertyDescription.getDefaultValue());
+ });
+
+ assertContains(results, "Supports Expression Language: true (will be
evaluated using Environment variables only)");
+ assertContains(results, "Supports Expression Language: true (will be
evaluated using flow file attributes and Environment variables)");
+
+ final List<UseCaseDetails> useCases = getUseCases();
+ useCases.forEach(useCase -> {
+ assertContains(results, useCase.getDescription());
+ assertContains(results, useCase.getNotes());
+ assertContains(results, String.join(", ", useCase.getKeywords()));
+ assertContains(results, useCase.getConfiguration());
+ });
+
+ final List<MultiProcessorUseCaseDetails> multiProcessorUseCases =
getMultiProcessorUseCases();
+ multiProcessorUseCases.forEach(multiProcessorUseCase -> {
+ assertContains(results, multiProcessorUseCase.getDescription());
+ assertContains(results, multiProcessorUseCase.getNotes());
+ assertContains(results, String.join(", ",
multiProcessorUseCase.getKeywords()));
+
+ multiProcessorUseCase.getConfigurations().forEach(configuration ->
{
+ assertContains(results, configuration.getProcessorType());
+ assertContains(results, configuration.getConfiguration());
+ });
+ });
+
+ assertNotContains(results, NO_PROPERTIES);
+ assertNotContains(results, "No description provided.");
+ assertNotContains(results, NO_TAGS);
+ }
+
+ @Test
+ public void testEmptyProcessor() throws IOException {
+ final PythonProcessorDetails processorDetails =
mock(PythonProcessorDetails.class);
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+
+ final DocumentationWriter<PythonProcessorDetails> writer = new
HtmlPythonProcessorDocumentationWriter();
+ writer.write(processorDetails, outputStream, false);
+
+ final String results = outputStream.toString();
+ XmlValidator.assertXmlValid(results);
+
+ assertContains(results, NO_DESCRIPTION);
+ assertContains(results, NO_TAGS);
+ assertContains(results, NO_PROPERTIES);
+ }
+
+ private PythonProcessorDetails getPythonProcessorDetails() {
+ final PythonProcessorDetails processorDetails =
mock(PythonProcessorDetails.class);
+
when(processorDetails.getProcessorType()).thenReturn("TestPythonProcessor");
+ when(processorDetails.getProcessorVersion()).thenReturn("1.0.0");
+
when(processorDetails.getSourceLocation()).thenReturn("/source/location/TestPythonProcessor.py");
+ when(processorDetails.getCapabilityDescription()).thenReturn("This is
a test capability description");
+ when(processorDetails.getTags()).thenReturn(List.of("tag1", "tag2",
"tag3"));
+
when(processorDetails.getDependencies()).thenReturn(List.of("dependency1==0.1",
"dependency2==0.2"));
+
when(processorDetails.getInterface()).thenReturn("org.apache.nifi.python.processor.FlowFileTransform");
+ when(processorDetails.getUseCases()).thenAnswer(invocation ->
getUseCases());
+
when(processorDetails.getMultiProcessorUseCases()).thenAnswer(invocation ->
getMultiProcessorUseCases());
+ when(processorDetails.getPropertyDescriptions()).thenAnswer(invocation
-> getPropertyDescriptions());
+
+ return processorDetails;
+ }
+
+ private List<UseCaseDetails> getUseCases() {
+ final UseCaseDetails useCaseDetails = mock(UseCaseDetails.class);
+ when(useCaseDetails.getDescription()).thenReturn("Test use case
description");
+ when(useCaseDetails.getNotes()).thenReturn("Test use case notes");
+ when(useCaseDetails.getKeywords()).thenReturn(List.of("use case
keyword1", "use case keyword2"));
+ when(useCaseDetails.getConfiguration()).thenReturn("Test use case
configuration");
+
+ return List.of(useCaseDetails);
+ }
+
+ private List<MultiProcessorUseCaseDetails> getMultiProcessorUseCases() {
+ final ProcessorConfigurationDetails configurationDetails1 =
mock(ProcessorConfigurationDetails.class);
+ when(configurationDetails1.getProcessorType()).thenReturn("Test
processor type 1");
+ when(configurationDetails1.getConfiguration()).thenReturn("Test
configuration 1");
+
+ final ProcessorConfigurationDetails configurationDetails2 =
mock(ProcessorConfigurationDetails.class);
+ when(configurationDetails2.getProcessorType()).thenReturn("Test
processor type 2");
+ when(configurationDetails2.getConfiguration()).thenReturn("Test
configuration 2");
+
+ final MultiProcessorUseCaseDetails useCaseDetails1 =
mock(MultiProcessorUseCaseDetails.class);
+ when(useCaseDetails1.getDescription()).thenReturn("Test description
1");
+ when(useCaseDetails1.getNotes()).thenReturn("Test notes 1");
+ when(useCaseDetails1.getKeywords()).thenReturn(List.of("keyword1",
"keyword2"));
+
when(useCaseDetails1.getConfigurations()).thenReturn(List.of(configurationDetails1,
configurationDetails2));
+
+ final MultiProcessorUseCaseDetails useCaseDetails2 =
mock(MultiProcessorUseCaseDetails.class);
+ when(useCaseDetails2.getDescription()).thenReturn("Test description
2");
+ when(useCaseDetails2.getNotes()).thenReturn("Test notes 2");
+ when(useCaseDetails2.getKeywords()).thenReturn(List.of("keyword3",
"keyword4"));
+
when(useCaseDetails2.getConfigurations()).thenReturn(List.of(configurationDetails1,
configurationDetails2));
+
+ return List.of(useCaseDetails1, useCaseDetails2);
+ }
+
+ private List<PropertyDescription> getPropertyDescriptions() {
+ final PropertyDescription description1 =
mock(PropertyDescription.class);
+ when(description1.getName()).thenReturn("Property Description 1");
+ when(description1.getDisplayName()).thenReturn("Property Description
Display name 1");
+ when(description1.getDescription()).thenReturn("This is a test
description for Property Description 1");
+
when(description1.getExpressionLanguageScope()).thenReturn("FLOWFILE_ATTRIBUTES");
+ when(description1.getDefaultValue()).thenReturn("Test default value
1");
+ when(description1.isRequired()).thenReturn(true);
+ when(description1.isSensitive()).thenReturn(false);
+
+ final PropertyDescription description2 =
mock(PropertyDescription.class);
+ when(description2.getName()).thenReturn("Property Description 2");
+ when(description2.getDisplayName()).thenReturn("Property Description
Display name 2");
+ when(description2.getDescription()).thenReturn("This is a test
description for Property Description 2");
+
when(description2.getExpressionLanguageScope()).thenReturn("ENVIRONMENT");
+ when(description2.getDefaultValue()).thenReturn("Test default value
2");
+ when(description2.isRequired()).thenReturn(false);
+ when(description2.isSensitive()).thenReturn(true);
+
+ final PropertyDescription description3 =
mock(PropertyDescription.class);
+ when(description3.getName()).thenReturn("Property Description 3");
+ when(description3.getDisplayName()).thenReturn("Property Description
Display name 3");
+ when(description3.getDescription()).thenReturn("This is a test
description for Property Description 3");
+ when(description3.getExpressionLanguageScope()).thenReturn("NONE");
+ when(description3.getDefaultValue()).thenReturn("Test default value
3");
+ when(description3.isRequired()).thenReturn(true);
+ when(description3.isSensitive()).thenReturn(true);
+
+ return List.of(description1, description2, description3);
+ }
+
+}
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/html/ProcessorDocumentationWriterTest.java
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/html/ProcessorDocumentationWriterTest.java
index f89a2a7ac0..bd5135fa29 100644
---
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/html/ProcessorDocumentationWriterTest.java
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/html/ProcessorDocumentationWriterTest.java
@@ -19,6 +19,7 @@ package org.apache.nifi.documentation.html;
import org.apache.nifi.annotation.behavior.SystemResource;
import org.apache.nifi.annotation.behavior.SystemResourceConsideration;
import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.components.ConfigurableComponent;
import org.apache.nifi.components.RequiredPermission;
import org.apache.nifi.documentation.DocumentationWriter;
import org.apache.nifi.documentation.example.DeprecatedProcessor;
@@ -33,6 +34,9 @@ import org.junit.jupiter.api.Test;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import static
org.apache.nifi.documentation.html.AbstractHtmlDocumentationWriter.NO_DESCRIPTION;
+import static
org.apache.nifi.documentation.html.AbstractHtmlDocumentationWriter.NO_PROPERTIES;
+import static
org.apache.nifi.documentation.html.AbstractHtmlDocumentationWriter.NO_TAGS;
import static org.apache.nifi.documentation.html.XmlValidator.assertContains;
import static
org.apache.nifi.documentation.html.XmlValidator.assertNotContains;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -47,7 +51,7 @@ public class ProcessorDocumentationWriterTest {
ProcessorInitializer initializer = new
ProcessorInitializer(extensionManager);
initializer.initialize(processor);
- DocumentationWriter writer = new
HtmlProcessorDocumentationWriter(extensionManager);
+ DocumentationWriter<ConfigurableComponent> writer = new
HtmlProcessorDocumentationWriter(extensionManager);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -84,10 +88,10 @@ public class ProcessorDocumentationWriterTest {
assertNotContains(results, "iconSecure.png");
assertContains(results,
FullyDocumentedProcessor.class.getAnnotation(CapabilityDescription.class)
.value());
- assertNotContains(results, "This component has no required or optional
properties.");
- assertNotContains(results, "No description provided.");
+ assertNotContains(results, NO_PROPERTIES);
+ assertNotContains(results, NO_DESCRIPTION);
- assertNotContains(results, "No tags provided.");
+ assertNotContains(results, NO_TAGS);
assertNotContains(results, "Additional Details...");
// check expression language scope
@@ -129,7 +133,7 @@ public class ProcessorDocumentationWriterTest {
ProcessorInitializer initializer = new
ProcessorInitializer(extensionManager);
initializer.initialize(processor);
- DocumentationWriter writer = new
HtmlProcessorDocumentationWriter(extensionManager);
+ DocumentationWriter<ConfigurableComponent> writer = new
HtmlProcessorDocumentationWriter(extensionManager);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -140,13 +144,13 @@ public class ProcessorDocumentationWriterTest {
XmlValidator.assertXmlValid(results);
// no description
- assertContains(results, "No description provided.");
+ assertContains(results, NO_DESCRIPTION);
// no tags
- assertContains(results, "No tags provided.");
+ assertContains(results, NO_TAGS);
// properties
- assertContains(results, "This component has no required or optional
properties.");
+ assertContains(results, NO_PROPERTIES);
// relationships
assertContains(results, "This processor has no relationships.");
@@ -169,7 +173,7 @@ public class ProcessorDocumentationWriterTest {
ProcessorInitializer initializer = new
ProcessorInitializer(extensionManager);
initializer.initialize(processor);
- DocumentationWriter writer = new
HtmlProcessorDocumentationWriter(extensionManager);
+ DocumentationWriter<ConfigurableComponent> writer = new
HtmlProcessorDocumentationWriter(extensionManager);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -189,7 +193,7 @@ public class ProcessorDocumentationWriterTest {
ProcessorInitializer initializer = new
ProcessorInitializer(extensionManager);
initializer.initialize(processor);
- DocumentationWriter writer = new
HtmlProcessorDocumentationWriter(extensionManager);
+ DocumentationWriter<ConfigurableComponent> writer = new
HtmlProcessorDocumentationWriter(extensionManager);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -229,9 +233,9 @@ public class ProcessorDocumentationWriterTest {
assertContains(results, "Deprecation notice: ");
// assertContains(results,
DeprecatedProcessor.class.getAnnotation(DeprecationNotice.class.));
- assertNotContains(results, "This component has no required or optional
properties.");
- assertNotContains(results, "No description provided.");
- assertNotContains(results, "No tags provided.");
+ assertNotContains(results, NO_PROPERTIES);
+ assertNotContains(results, NO_DESCRIPTION);
+ assertNotContains(results, NO_TAGS);
assertNotContains(results, "Additional Details...");
// verify the right OnRemoved and OnShutdown methods were called
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
index fe83b16d2b..9d8a26d830 100644
---
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
@@ -38,6 +38,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -54,6 +55,7 @@ import jakarta.servlet.ServletContext;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.NiFiServer;
import org.apache.nifi.bundle.Bundle;
+import org.apache.nifi.bundle.BundleCoordinate;
import org.apache.nifi.bundle.BundleDetails;
import org.apache.nifi.cluster.ClusterDetailsFactory;
import org.apache.nifi.controller.DecommissionTask;
@@ -73,6 +75,7 @@ import
org.apache.nifi.flow.resource.ExternalResourceProviderService;
import org.apache.nifi.flow.resource.ExternalResourceProviderServiceBuilder;
import
org.apache.nifi.flow.resource.PropertyBasedExternalResourceProviderInitializationContext;
import org.apache.nifi.lifecycle.LifeCycleStartException;
+import org.apache.nifi.nar.ExtensionDefinition;
import org.apache.nifi.nar.ExtensionManager;
import org.apache.nifi.nar.ExtensionManagerHolder;
import org.apache.nifi.nar.ExtensionMapping;
@@ -84,6 +87,7 @@ import org.apache.nifi.nar.NarThreadContextClassLoader;
import org.apache.nifi.nar.NarUnpackMode;
import org.apache.nifi.nar.StandardExtensionDiscoveringManager;
import org.apache.nifi.nar.StandardNarLoader;
+import org.apache.nifi.processor.Processor;
import org.apache.nifi.security.util.TlsException;
import org.apache.nifi.services.FlowService;
import org.apache.nifi.ui.extension.UiExtension;
@@ -122,6 +126,8 @@ import org.springframework.context.ApplicationContext;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
+import static org.apache.nifi.nar.ExtensionDefinition.ExtensionRuntime.PYTHON;
+
/**
* Encapsulates the Jetty instance.
*/
@@ -746,9 +752,6 @@ public class JettyServer implements NiFiServer,
ExtensionUiLoader {
// Set the extension manager into the holder which makes it
available to the Spring context via a factory bean
ExtensionManagerHolder.init(extensionManager);
- // Generate docs for extensions
- DocGenerator.generate(props, extensionManager, extensionMapping);
-
// Additionally loaded NARs and collected flow resources must be
in place before starting the flows
narProviderService = new
ExternalResourceProviderServiceBuilder("NAR Auto-Loader Provider",
extensionManager)
.providers(buildExternalResourceProviders(extensionManager,
NAR_PROVIDER_PREFIX, descriptor ->
descriptor.getLocation().toLowerCase().endsWith(".nar")))
@@ -826,10 +829,26 @@ public class JettyServer implements NiFiServer,
ExtensionUiLoader {
clusterDetailsFactory =
webApplicationContext.getBean("clusterDetailsFactory",
ClusterDetailsFactory.class);
}
+ // Generate docs for extensions
+ DocGenerator.generate(props, extensionManager, extensionMapping);
+
// ensure the web document war was loaded and provide the
extension mapping
if (webDocsContext != null) {
+ final Map<String, Set<BundleCoordinate>>
pythonExtensionMapping = new HashMap<>();
+
+ final Set<ExtensionDefinition> extensionDefinitions =
extensionManager.getExtensions(Processor.class)
+ .stream()
+ .filter(extension ->
extension.getRuntime().equals(PYTHON))
+ .collect(Collectors.toSet());
+
+ extensionDefinitions.forEach(
+ extensionDefinition ->
+
pythonExtensionMapping.computeIfAbsent(extensionDefinition.getImplementationClassName(),
+ name -> new
HashSet<>()).add(extensionDefinition.getBundle().getBundleDetails().getCoordinate()));
+
final ServletContext webDocsServletContext =
webDocsContext.getServletHandler().getServletContext();
webDocsServletContext.setAttribute("nifi-extension-mapping",
extensionMapping);
+
webDocsServletContext.setAttribute("nifi-python-extension-mapping",
pythonExtensionMapping);
}
// if this nifi is a node in a cluster, start the flow service and
load the flow - the
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-docs/src/main/java/org/apache/nifi/web/docs/DocumentationController.java
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-docs/src/main/java/org/apache/nifi/web/docs/DocumentationController.java
index dc8f9b1c69..ff08393059 100644
---
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-docs/src/main/java/org/apache/nifi/web/docs/DocumentationController.java
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-docs/src/main/java/org/apache/nifi/web/docs/DocumentationController.java
@@ -17,6 +17,7 @@
package org.apache.nifi.web.docs;
import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.bundle.BundleCoordinate;
import org.apache.nifi.nar.ExtensionMapping;
import jakarta.servlet.ServletConfig;
@@ -27,8 +28,10 @@ import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.text.Collator;
+import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
+import java.util.Set;
import java.util.TreeMap;
/**
@@ -58,11 +61,16 @@ public class DocumentationController extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse
response) throws ServletException, IOException {
final ExtensionMapping extensionMappings = (ExtensionMapping)
servletContext.getAttribute("nifi-extension-mapping");
+ final Map<String, Set<BundleCoordinate>> pythonExtensionMappings =
(Map<String, Set<BundleCoordinate>>)
servletContext.getAttribute("nifi-python-extension-mapping");
+ final Map<String, Set<BundleCoordinate>> processorNames = new
HashMap<>();
+ processorNames.putAll(extensionMappings.getProcessorNames());
+ processorNames.putAll(pythonExtensionMappings);
+
final Collator collator = Collator.getInstance(Locale.US);
// create the processors lookup
final Map<String, String> processors = new TreeMap<>(collator);
- for (final String processorClass :
extensionMappings.getProcessorNames().keySet()) {
+ for (final String processorClass : processorNames.keySet()) {
processors.put(StringUtils.substringAfterLast(processorClass,
"."), processorClass);
}
@@ -92,7 +100,7 @@ public class DocumentationController extends HttpServlet {
// make the available components available to the documentation jsp
request.setAttribute("processors", processors);
- request.setAttribute("processorBundleLookup",
extensionMappings.getProcessorNames());
+ request.setAttribute("processorBundleLookup", processorNames);
request.setAttribute("controllerServices", controllerServices);
request.setAttribute("controllerServiceBundleLookup",
extensionMappings.getControllerServiceNames());
request.setAttribute("reportingTasks", reportingTasks);