diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/pom.xml b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/pom.xml
new file mode 100644
index 0000000000..f35c8cd83c
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/pom.xml
@@ -0,0 +1,49 @@
+
+
+
+ 4.0.0
+
+ org.apache.nifi
+ nifi-framework
+ 0.0.2-incubating-SNAPSHOT
+
+ nifi-documentation
+
+
+ org.apache.nifi
+ nifi-nar-utils
+
+
+ org.apache.nifi
+ nifi-api
+
+
+ org.apache.nifi
+ nifi-properties
+
+
+ commons-io
+ commons-io
+
+
+ org.apache.commons
+ commons-lang3
+
+
+ org.apache.nifi
+ nifi-processor-utils
+ test
+
+
+
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/ConfigurableComponentInitializer.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/ConfigurableComponentInitializer.java
new file mode 100644
index 0000000000..164d212bd0
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/ConfigurableComponentInitializer.java
@@ -0,0 +1,38 @@
+/*
+ * 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;
+
+import org.apache.nifi.components.ConfigurableComponent;
+import org.apache.nifi.reporting.InitializationException;
+
+/**
+ * An interface for initializing a ConfigurableComponent. It is up to the
+ * implementer to call "init" so that you can call
+ * ConfigurableComponent.getPropertyDescriptors()
+ *
+ */
+public interface ConfigurableComponentInitializer {
+
+ /**
+ * Initializes a configurable component to the point that you can call
+ * getPropertyDescriptors() on it
+ *
+ * @param component the component to initialize
+ * @throws InitializationException if the component could not be initialized
+ */
+ void initialize(ConfigurableComponent component) throws InitializationException;
+}
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/DocGenerator.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/DocGenerator.java
new file mode 100644
index 0000000000..157d95edf5
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/DocGenerator.java
@@ -0,0 +1,182 @@
+/*
+ * 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;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.HashSet;
+import java.util.Set;
+
+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.init.ControllerServiceInitializer;
+import org.apache.nifi.documentation.init.ProcessorInitializer;
+import org.apache.nifi.documentation.init.ReportingTaskingInitializer;
+import org.apache.nifi.nar.ExtensionManager;
+import org.apache.nifi.processor.Processor;
+import org.apache.nifi.reporting.InitializationException;
+import org.apache.nifi.reporting.ReportingTask;
+import org.apache.nifi.util.NiFiProperties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Uses the ExtensionManager to get a list of Processor, ControllerService, and
+ * Reporting Task classes that were loaded and generate documentation for them.
+ *
+ *
+ */
+public class DocGenerator {
+
+ private static final Logger logger = LoggerFactory.getLogger(DocGenerator.class);
+
+ /**
+ * Generates documentation into the work/docs dir specified by
+ * NiFiProperties.
+ *
+ * @param properties
+ */
+ public static void generate(final NiFiProperties properties) {
+ @SuppressWarnings("rawtypes")
+ final Set extensionClasses = new HashSet<>();
+ extensionClasses.addAll(ExtensionManager.getExtensions(Processor.class));
+ extensionClasses.addAll(ExtensionManager.getExtensions(ControllerService.class));
+ extensionClasses.addAll(ExtensionManager.getExtensions(ReportingTask.class));
+
+ final File explodedNiFiDocsDir = properties.getComponentDocumentationWorkingDirectory();
+
+ logger.debug("Generating documentation for: " + extensionClasses.size() + " components in: "
+ + explodedNiFiDocsDir);
+
+ for (final Class> extensionClass : extensionClasses) {
+ if (ConfigurableComponent.class.isAssignableFrom(extensionClass)) {
+ final Class extends ConfigurableComponent> componentClass = extensionClass
+ .asSubclass(ConfigurableComponent.class);
+ try {
+ logger.debug("Documenting: " + componentClass);
+ document(explodedNiFiDocsDir, componentClass);
+ } catch (Exception e) {
+ logger.warn("Unable to document: " + componentClass);
+ }
+ }
+ }
+ }
+
+ /**
+ * Generates the documentation for a particular configurable comopnent. Will
+ * check to see if an "additionalDetails.html" file exists and will link
+ * that from the generated documentation.
+ *
+ * @param docsDir
+ * the work\docs\components dir to stick component documentation
+ * in
+ * @param componentClass
+ * the class to document
+ * @throws InstantiationException
+ * @throws IllegalAccessException
+ * @throws IOException
+ * @throws InitializationException
+ */
+ private static void document(final File docsDir, final Class extends ConfigurableComponent> componentClass)
+ throws InstantiationException, IllegalAccessException, IOException, InitializationException {
+
+ final ConfigurableComponent component = componentClass.newInstance();
+ final ConfigurableComponentInitializer initializer = getComponentInitializer(componentClass);
+ initializer.initialize(component);
+
+ final DocumentationWriter writer = getDocumentWriter(componentClass);
+
+ final File directory = new File(docsDir, componentClass.getCanonicalName());
+ directory.mkdirs();
+
+ final File baseDocumenationFile = new File(directory, "index.html");
+ if (baseDocumenationFile.exists()) {
+ logger.warn(baseDocumenationFile + " already exists, overwriting!");
+ }
+
+ try (final OutputStream output = new BufferedOutputStream(new FileOutputStream(baseDocumenationFile))) {
+ writer.write(component, output, hasAdditionalInfo(directory));
+ }
+ }
+
+ /**
+ * Returns the DocumentationWriter for the type of component. Currently
+ * Processor, ControllerService, and ReportingTask are supported.
+ *
+ * @param componentClass
+ * the class that requires a DocumentationWriter
+ * @return a DocumentationWriter capable of generating documentation for
+ * that specific type of class
+ */
+ private static DocumentationWriter getDocumentWriter(final Class extends ConfigurableComponent> componentClass) {
+ if (Processor.class.isAssignableFrom(componentClass)) {
+ return new HtmlProcessorDocumentationWriter();
+ } else if (ControllerService.class.isAssignableFrom(componentClass)) {
+ return new HtmlDocumentationWriter();
+ } else if (ReportingTask.class.isAssignableFrom(componentClass)) {
+ return new HtmlDocumentationWriter();
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns a ConfigurableComponentInitializer for the type of component.
+ * Currently Processor, ControllerService and ReportingTask are supported.
+ *
+ * @param componentClass
+ * the class that requires a ConfigurableComponentInitializer
+ * @return a ConfigurableComponentInitializer capable of initializing that
+ * specific type of class
+ */
+ private static ConfigurableComponentInitializer getComponentInitializer(
+ final Class extends ConfigurableComponent> componentClass) {
+ if (Processor.class.isAssignableFrom(componentClass)) {
+ return new ProcessorInitializer();
+ } else if (ControllerService.class.isAssignableFrom(componentClass)) {
+ return new ControllerServiceInitializer();
+ } else if (ReportingTask.class.isAssignableFrom(componentClass)) {
+ return new ReportingTaskingInitializer();
+ }
+
+ return null;
+ }
+
+ /**
+ * Checks to see if a directory to write to has an additionalDetails.html in
+ * it already.
+ *
+ * @param directory
+ * @return true if additionalDetails.html exists, false otherwise.
+ */
+ private static boolean hasAdditionalInfo(File directory) {
+ return directory.list(new FilenameFilter() {
+
+ @Override
+ public boolean accept(File dir, String name) {
+ return name.equalsIgnoreCase(HtmlDocumentationWriter.ADDITIONAL_DETAILS_HTML);
+ }
+
+ }).length > 0;
+ }
+}
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/DocumentationWriter.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/DocumentationWriter.java
new file mode 100644
index 0000000000..5a3c5d8a80
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/DocumentationWriter.java
@@ -0,0 +1,33 @@
+/*
+ * 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;
+
+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 {
+
+ void write(ConfigurableComponent configurableComponent, OutputStream streamToWriteTo,
+ boolean includesAdditionalDocumentation) throws IOException;
+}
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/html/HtmlDocumentationWriter.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/html/HtmlDocumentationWriter.java
new file mode 100644
index 0000000000..c192759b12
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/html/HtmlDocumentationWriter.java
@@ -0,0 +1,417 @@
+/*
+ * 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 java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+
+import javax.xml.stream.FactoryConfigurationError;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.components.AllowableValue;
+import org.apache.nifi.components.ConfigurableComponent;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.controller.ControllerService;
+import org.apache.nifi.documentation.DocumentationWriter;
+
+/**
+ * Generates HTML documentation for a ConfigurableComponent. This class is used
+ * to generate documentation for ControllerService and ReportingTask because
+ * they have no additional information.
+ *
+ *
+ */
+public class HtmlDocumentationWriter implements DocumentationWriter {
+
+ /**
+ * The filename where additional user specified information may be stored.
+ */
+ public static final String ADDITIONAL_DETAILS_HTML = "additionalDetails.html";
+
+ @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("");
+ 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();
+ }
+
+ /**
+ * 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 final void writeBody(final ConfigurableComponent configurableComponent,
+ final XMLStreamWriter xmlStreamWriter, final boolean hasAdditionalDetails)
+ throws XMLStreamException {
+ xmlStreamWriter.writeStartElement("body");
+ writeDescription(configurableComponent, xmlStreamWriter, hasAdditionalDetails);
+ writeTags(configurableComponent, xmlStreamWriter);
+ writeProperties(configurableComponent, xmlStreamWriter);
+ writeAdditionalBodyInfo(configurableComponent, xmlStreamWriter);
+ 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 {
+ }
+
+ /**
+ * Writes the tags attached to a ConfigurableComponent.
+ *
+ * @param configurableComponent
+ * @param xmlStreamWriter
+ * @throws XMLStreamException
+ */
+ private void writeTags(final ConfigurableComponent configurableComponent,
+ final XMLStreamWriter xmlStreamWriter) throws XMLStreamException {
+ final Tags tags = configurableComponent.getClass().getAnnotation(Tags.class);
+ xmlStreamWriter.writeStartElement("h3");
+ xmlStreamWriter.writeCharacters("Tags: ");
+ xmlStreamWriter.writeEndElement();
+ xmlStreamWriter.writeStartElement("p");
+ if (tags != null) {
+ final String tagString = StringUtils.join(tags.value(), ", ");
+ xmlStreamWriter.writeCharacters(tagString);
+ } else {
+ xmlStreamWriter.writeCharacters("None.");
+ }
+ xmlStreamWriter.writeEndElement();
+
+ }
+
+ /**
+ * 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);
+
+ final String description;
+ if (capabilityDescription != null) {
+ description = capabilityDescription.value();
+ } else {
+ description = "No description provided.";
+ }
+
+ return description;
+ }
+
+ /**
+ * 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 {
+ writeSimpleElement(xmlStreamWriter, "h3", "Properties: ");
+ 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, whether a property supports the ");
+ writeLink(xmlStreamWriter, "NiFi Expression Language",
+ "../../html/expression-language-guide.html");
+ xmlStreamWriter.writeCharacters(", and whether a property is considered "
+ + "\"sensitive\", meaning that its value will be encrypted. Before entering a "
+ + "value in a sensitive property, ensure that the ");
+ writeSimpleElement(xmlStreamWriter, "strong", "nifi.properties");
+ xmlStreamWriter.writeCharacters(" file has " + "an entry for the property ");
+ writeSimpleElement(xmlStreamWriter, "strong", "nifi.sensitive.props.key");
+ xmlStreamWriter.writeCharacters(".");
+ xmlStreamWriter.writeEndElement();
+
+
+ List properties = configurableComponent.getPropertyDescriptors();
+ if (properties.size() > 0) {
+ xmlStreamWriter.writeStartElement("table");
+
+ // write the header row
+ xmlStreamWriter.writeStartElement("tr");
+ writeSimpleElement(xmlStreamWriter, "th", "Name");
+ writeSimpleElement(xmlStreamWriter, "th", "Default Value");
+ writeSimpleElement(xmlStreamWriter, "th", "Valid Values");
+ writeSimpleElement(xmlStreamWriter, "th", "Description");
+ xmlStreamWriter.writeEndElement();
+
+ // write the individual properties
+ for (PropertyDescriptor property : properties) {
+ xmlStreamWriter.writeStartElement("tr");
+ xmlStreamWriter.writeStartElement("td");
+ if (property.isRequired()) {
+ writeSimpleElement(xmlStreamWriter, "strong", property.getDisplayName());
+ } else {
+ xmlStreamWriter.writeCharacters(property.getDisplayName());
+ }
+
+ xmlStreamWriter.writeEndElement();
+ writeSimpleElement(xmlStreamWriter, "td", property.getDefaultValue());
+ xmlStreamWriter.writeStartElement("td");
+ writeValidValues(xmlStreamWriter, property);
+ xmlStreamWriter.writeEndElement();
+ xmlStreamWriter.writeStartElement("td");
+ if (property.getDescription() != null && property.getDescription().trim().length() > 0) {
+ xmlStreamWriter.writeCharacters(property.getDescription());
+ } else {
+ xmlStreamWriter.writeCharacters("No Description Provided.");
+ }
+
+ if (property.isSensitive()) {
+ xmlStreamWriter.writeEmptyElement("br");
+ writeSimpleElement(xmlStreamWriter, "strong", "Sensitive Property: true");
+ }
+
+ if (property.isExpressionLanguageSupported()) {
+ xmlStreamWriter.writeEmptyElement("br");
+ writeSimpleElement(xmlStreamWriter, "strong", "Supports Expression Language: true");
+ }
+ xmlStreamWriter.writeEndElement();
+
+ xmlStreamWriter.writeEndElement();
+ }
+
+ // TODO support dynamic properties...
+ xmlStreamWriter.writeEndElement();
+
+ } else {
+ writeSimpleElement(xmlStreamWriter, "p", "This component has no required or optional properties.");
+ }
+ }
+
+ 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.
+ *
+ * @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
+ */
+ protected void writeValidValues(XMLStreamWriter xmlStreamWriter, PropertyDescriptor property)
+ throws XMLStreamException {
+ if (property.getAllowableValues() != null && property.getAllowableValues().size() > 0) {
+ xmlStreamWriter.writeStartElement("ul");
+ for (AllowableValue value : property.getAllowableValues()) {
+ xmlStreamWriter.writeStartElement("li");
+ xmlStreamWriter.writeCharacters(value.getDisplayName());
+
+ if (value.getDescription() != null) {
+ writeValidValueDescription(xmlStreamWriter, value.getDescription());
+ }
+ xmlStreamWriter.writeEndElement();
+
+ }
+ xmlStreamWriter.writeEndElement();
+ } else if (property.getControllerServiceDefinition() != null) {
+ Class extends ControllerService> controllerServiceClass = property.getControllerServiceDefinition();
+ writeSimpleElement(xmlStreamWriter, "strong", "Controller Service: ");
+ xmlStreamWriter.writeEmptyElement("br");
+ xmlStreamWriter.writeCharacters(controllerServiceClass.getSimpleName());
+ }
+ }
+
+ /**
+ * 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
+ * @param strong
+ * whether the characters should be strong or not.
+ * @throws XMLStreamException
+ * thrown if there was a problem writing to the stream.
+ */
+ protected final static void writeSimpleElement(final XMLStreamWriter writer, final String elementName,
+ final String characters, boolean strong) throws XMLStreamException {
+ writer.writeStartElement(elementName);
+ if (strong) {
+ writer.writeStartElement("strong");
+ }
+ writer.writeCharacters(characters);
+ if (strong) {
+ writer.writeEndElement();
+ }
+ 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
+ * @throws XMLStreamException
+ * thrown if there was a problem writing to the stream
+ */
+ protected final static void writeSimpleElement(final XMLStreamWriter writer, final String elementName,
+ final String characters) throws XMLStreamException {
+ writeSimpleElement(writer, elementName, characters, false);
+ }
+
+ /**
+ * 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();
+ }
+}
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/html/HtmlProcessorDocumentationWriter.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/html/HtmlProcessorDocumentationWriter.java
new file mode 100644
index 0000000000..4cf6ba850e
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/html/HtmlProcessorDocumentationWriter.java
@@ -0,0 +1,58 @@
+/*
+ * 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 javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+
+import org.apache.nifi.components.ConfigurableComponent;
+import org.apache.nifi.processor.Processor;
+import org.apache.nifi.processor.Relationship;
+
+/**
+ * Writes documentation specific for a Processor. This includes everything for a
+ * ConfigurableComponent as well as Relationship information.
+ *
+ *
+ */
+public class HtmlProcessorDocumentationWriter extends HtmlDocumentationWriter {
+
+ @Override
+ protected void writeAdditionalBodyInfo(final ConfigurableComponent configurableComponent,
+ final XMLStreamWriter xmlStreamWriter) throws XMLStreamException {
+ final Processor processor = (Processor) configurableComponent;
+ writeSimpleElement(xmlStreamWriter, "h3", "Relationships: ");
+
+ if (processor.getRelationships().size() > 0) {
+ xmlStreamWriter.writeStartElement("table");
+ 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.writeEndElement();
+ }
+ xmlStreamWriter.writeEndElement();
+ } else {
+ xmlStreamWriter.writeCharacters("This processor has no relationships.");
+ }
+ }
+}
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/init/ControllerServiceInitializer.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/init/ControllerServiceInitializer.java
new file mode 100644
index 0000000000..123a39c81d
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/init/ControllerServiceInitializer.java
@@ -0,0 +1,38 @@
+/*
+ * 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.init;
+
+import org.apache.nifi.components.ConfigurableComponent;
+import org.apache.nifi.controller.ControllerService;
+import org.apache.nifi.documentation.ConfigurableComponentInitializer;
+import org.apache.nifi.documentation.mock.MockControllerServiceInitializationContext;
+import org.apache.nifi.reporting.InitializationException;
+
+/**
+ * Initializes a ControllerService using a
+ * MockControllerServiceInitializationContext
+ *
+ *
+ */
+public class ControllerServiceInitializer implements ConfigurableComponentInitializer {
+
+ @Override
+ public void initialize(ConfigurableComponent component) throws InitializationException {
+ ControllerService controllerService = (ControllerService) component;
+ controllerService.initialize(new MockControllerServiceInitializationContext());
+ }
+}
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/init/ProcessorInitializer.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/init/ProcessorInitializer.java
new file mode 100644
index 0000000000..a33f7b9acc
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/init/ProcessorInitializer.java
@@ -0,0 +1,37 @@
+/*
+ * 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.init;
+
+import org.apache.nifi.components.ConfigurableComponent;
+import org.apache.nifi.documentation.ConfigurableComponentInitializer;
+import org.apache.nifi.documentation.mock.MockProcessorInitializationContext;
+import org.apache.nifi.processor.Processor;
+
+/**
+ * Initializes a Procesor using a MockProcessorInitializationContext
+ *
+ *
+ */
+public class ProcessorInitializer implements ConfigurableComponentInitializer {
+
+ @Override
+ public void initialize(ConfigurableComponent component) {
+ Processor processor = (Processor) component;
+ processor.initialize(new MockProcessorInitializationContext());
+ }
+
+}
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/init/ReportingTaskingInitializer.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/init/ReportingTaskingInitializer.java
new file mode 100644
index 0000000000..ff915cfa0b
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/init/ReportingTaskingInitializer.java
@@ -0,0 +1,37 @@
+/*
+ * 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.init;
+
+import org.apache.nifi.components.ConfigurableComponent;
+import org.apache.nifi.documentation.ConfigurableComponentInitializer;
+import org.apache.nifi.documentation.mock.MockReportingInitializationContext;
+import org.apache.nifi.reporting.InitializationException;
+import org.apache.nifi.reporting.ReportingTask;
+
+/**
+ * Initializes a ReportingTask using a MockReportingInitializationContext;
+ *
+ *
+ */
+public class ReportingTaskingInitializer implements ConfigurableComponentInitializer {
+
+ @Override
+ public void initialize(ConfigurableComponent component) throws InitializationException {
+ ReportingTask reportingTask = (ReportingTask) component;
+ reportingTask.initialize(new MockReportingInitializationContext());
+ }
+}
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockControllerServiceInitializationContext.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockControllerServiceInitializationContext.java
new file mode 100644
index 0000000000..6153a8effe
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockControllerServiceInitializationContext.java
@@ -0,0 +1,40 @@
+/*
+ * 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.mock;
+
+import org.apache.nifi.controller.ControllerServiceInitializationContext;
+import org.apache.nifi.controller.ControllerServiceLookup;
+
+/**
+ * A Mock ControllerServiceInitializationContext so that ControllerServices can
+ * be initialized for the purpose of generating documentation.
+ *
+ *
+ */
+public class MockControllerServiceInitializationContext implements ControllerServiceInitializationContext {
+
+ @Override
+ public String getIdentifier() {
+ return "";
+ }
+
+ @Override
+ public ControllerServiceLookup getControllerServiceLookup() {
+ return new MockControllerServiceLookup();
+ }
+
+}
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockControllerServiceLookup.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockControllerServiceLookup.java
new file mode 100644
index 0000000000..88d091155e
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockControllerServiceLookup.java
@@ -0,0 +1,55 @@
+/*
+ * 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.mock;
+
+import java.util.Collections;
+import java.util.Set;
+
+import org.apache.nifi.controller.ControllerService;
+import org.apache.nifi.controller.ControllerServiceLookup;
+
+/**
+ * A Mock ControllerServiceLookup that can be used so that
+ * ConfigurableComponents can be initialized for the purpose of generating
+ * documentation
+ *
+ *
+ */
+public class MockControllerServiceLookup implements ControllerServiceLookup {
+
+ @Override
+ public ControllerService getControllerService(String serviceIdentifier) {
+ return null;
+ }
+
+ @Override
+ public boolean isControllerServiceEnabled(String serviceIdentifier) {
+ return false;
+ }
+
+ @Override
+ public boolean isControllerServiceEnabled(ControllerService service) {
+ return false;
+ }
+
+ @Override
+ public Set getControllerServiceIdentifiers(Class extends ControllerService> serviceType)
+ throws IllegalArgumentException {
+ return Collections.emptySet();
+ }
+
+}
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockProcessorInitializationContext.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockProcessorInitializationContext.java
new file mode 100644
index 0000000000..48ffecb1ce
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockProcessorInitializationContext.java
@@ -0,0 +1,45 @@
+/*
+ * 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.mock;
+
+import org.apache.nifi.controller.ControllerServiceLookup;
+import org.apache.nifi.logging.ProcessorLog;
+import org.apache.nifi.processor.ProcessorInitializationContext;
+
+/**
+ * A Mock ProcessorInitializationContext that can be used so that Processors can
+ * be initialized for the purpose of generating documentation.
+ *
+ *
+ */
+public class MockProcessorInitializationContext implements ProcessorInitializationContext {
+
+ @Override
+ public String getIdentifier() {
+ return "";
+ }
+
+ @Override
+ public ProcessorLog getLogger() {
+ return null;
+ }
+
+ @Override
+ public ControllerServiceLookup getControllerServiceLookup() {
+ return new MockControllerServiceLookup();
+ }
+}
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockReportingInitializationContext.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockReportingInitializationContext.java
new file mode 100644
index 0000000000..9782077bc9
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockReportingInitializationContext.java
@@ -0,0 +1,63 @@
+/*
+ * 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.mock;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.nifi.controller.ControllerServiceLookup;
+import org.apache.nifi.reporting.ReportingInitializationContext;
+import org.apache.nifi.scheduling.SchedulingStrategy;
+
+/**
+ * A Mock ReportingInitializationContext that can be used to initialize a
+ * ReportingTask for the purposes of documentation generation.
+ *
+ * @author Alligator
+ *
+ */
+public class MockReportingInitializationContext implements ReportingInitializationContext {
+
+ @Override
+ public String getIdentifier() {
+ return "";
+ }
+
+ @Override
+ public String getName() {
+ return "";
+ }
+
+ @Override
+ public long getSchedulingPeriod(TimeUnit timeUnit) {
+ return 0;
+ }
+
+ @Override
+ public ControllerServiceLookup getControllerServiceLookup() {
+ return new MockControllerServiceLookup();
+ }
+
+ @Override
+ public String getSchedulingPeriod() {
+ return "";
+ }
+
+ @Override
+ public SchedulingStrategy getSchedulingStrategy() {
+ return SchedulingStrategy.TIMER_DRIVEN;
+ }
+}
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/example/FullyDocumentedControllerService.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/example/FullyDocumentedControllerService.java
new file mode 100644
index 0000000000..c27f3baa51
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/example/FullyDocumentedControllerService.java
@@ -0,0 +1,57 @@
+/*
+ * 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.example;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.controller.AbstractControllerService;
+import org.apache.nifi.processor.util.StandardValidators;
+
+@CapabilityDescription("A documented controller service that can help you do things")
+@Tags({ "one", "two", "three" })
+public class FullyDocumentedControllerService extends AbstractControllerService {
+ public static final PropertyDescriptor KEYSTORE = new PropertyDescriptor.Builder().name("Keystore Filename")
+ .description("The fully-qualified filename of the Keystore").defaultValue(null)
+ .addValidator(StandardValidators.FILE_EXISTS_VALIDATOR).sensitive(false).build();
+ public static final PropertyDescriptor KEYSTORE_TYPE = new PropertyDescriptor.Builder().name("Keystore Type")
+ .description("The Type of the Keystore").allowableValues("JKS", "PKCS12")
+ .addValidator(StandardValidators.NON_EMPTY_VALIDATOR).defaultValue("JKS").sensitive(false).build();
+ public static final PropertyDescriptor KEYSTORE_PASSWORD = new PropertyDescriptor.Builder()
+ .name("Keystore Password").defaultValue(null).description("The password for the Keystore")
+ .addValidator(StandardValidators.NON_EMPTY_VALIDATOR).sensitive(true).build();
+
+ private static final List properties;
+
+ static {
+ List props = new ArrayList<>();
+ props.add(KEYSTORE);
+ props.add(KEYSTORE_PASSWORD);
+ props.add(KEYSTORE_TYPE);
+ properties = Collections.unmodifiableList(props);
+ }
+
+ @Override
+ protected List getSupportedPropertyDescriptors() {
+ return properties;
+ }
+
+}
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/example/FullyDocumentedProcessor.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/example/FullyDocumentedProcessor.java
new file mode 100644
index 0000000000..c2c765784f
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/example/FullyDocumentedProcessor.java
@@ -0,0 +1,110 @@
+/*
+ * 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.example;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.nifi.components.AllowableValue;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.processor.AbstractProcessor;
+import org.apache.nifi.processor.ProcessContext;
+import org.apache.nifi.processor.ProcessSession;
+import org.apache.nifi.processor.ProcessorInitializationContext;
+import org.apache.nifi.processor.Relationship;
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.processor.util.StandardValidators;
+
+@Tags({ "one", "two", "three" })
+@CapabilityDescription("This is a processor that is used to test documentation.")
+public class FullyDocumentedProcessor extends AbstractProcessor {
+
+ public static final PropertyDescriptor DIRECTORY = new PropertyDescriptor.Builder().name("Input Directory")
+ .description("The input directory from which to pull files").required(true)
+ .addValidator(StandardValidators.createDirectoryExistsValidator(true, false))
+ .expressionLanguageSupported(true).build();
+
+ public static final PropertyDescriptor RECURSE = new PropertyDescriptor.Builder().name("Recurse Subdirectories")
+ .description("Indicates whether or not to pull files from subdirectories").required(true)
+ .allowableValues(new AllowableValue("true", "true", "Should pull from sub directories"), new AllowableValue("false", "false", "Should not pull from sub directories")).defaultValue("true").build();
+
+ public static final PropertyDescriptor POLLING_INTERVAL = new PropertyDescriptor.Builder().name("Polling Interval")
+ .description("Indicates how long to wait before performing a directory listing").required(true)
+ .addValidator(StandardValidators.TIME_PERIOD_VALIDATOR).defaultValue("0 sec").build();
+
+ public static final PropertyDescriptor OPTIONAL_PROPERTY = new PropertyDescriptor.Builder()
+ .name("Optional Property").description("This is a property you can use or not").required(false).build();
+
+ public static final PropertyDescriptor TYPE_PROPERTY = new PropertyDescriptor.Builder()
+ .name("Type").description("This is the type of something that you can choose. It has several possible values").allowableValues("yes", "no", "maybe", "possibly", "not likely", "longer option name").required(true).build();
+
+ public static final PropertyDescriptor SERVICE_PROPERTY = new PropertyDescriptor.Builder()
+ .name("Controller Service").description("This is the controller service to use to do things")
+ .identifiesControllerService(SampleService.class).required(true).build();
+
+ public static final Relationship REL_SUCCESS = new Relationship.Builder().name("success")
+ .description("Successful files").build();
+ public static final Relationship REL_FAILURE = new Relationship.Builder().name("failure")
+ .description("Failing files").build();
+
+ private List properties;
+ private Set relationships;
+
+ @Override
+ protected void init(ProcessorInitializationContext context) {
+ final List properties = new ArrayList<>();
+ properties.add(DIRECTORY);
+ properties.add(RECURSE);
+ properties.add(POLLING_INTERVAL);
+ properties.add(OPTIONAL_PROPERTY);
+ properties.add(TYPE_PROPERTY);
+ properties.add(SERVICE_PROPERTY);
+ this.properties = Collections.unmodifiableList(properties);
+
+ final Set relationships = new HashSet<>();
+ relationships.add(REL_SUCCESS);
+ relationships.add(REL_FAILURE);
+ this.relationships = Collections.unmodifiableSet(relationships);
+ }
+
+ @Override
+ protected List getSupportedPropertyDescriptors() {
+ return properties;
+ }
+
+ @Override
+ public Set getRelationships() {
+ return relationships;
+ }
+
+ @Override
+ public void onTrigger(ProcessContext context, ProcessSession session) throws ProcessException {
+
+ }
+
+ @Override
+ protected PropertyDescriptor getSupportedDynamicPropertyDescriptor(String propertyDescriptorName) {
+ return new PropertyDescriptor.Builder().name(propertyDescriptorName)
+ .description("This is a property you can use or not").dynamic(true).build();
+ }
+
+}
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/example/FullyDocumentedReportingTask.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/example/FullyDocumentedReportingTask.java
new file mode 100644
index 0000000000..275905b1a4
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/example/FullyDocumentedReportingTask.java
@@ -0,0 +1,50 @@
+/*
+ * 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.example;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.reporting.AbstractReportingTask;
+import org.apache.nifi.reporting.ReportingContext;
+
+@CapabilityDescription("A helper reporting task to do...")
+@Tags({ "first", "second", "third" })
+public class FullyDocumentedReportingTask extends AbstractReportingTask {
+
+ public static final PropertyDescriptor SHOW_DELTAS = new PropertyDescriptor.Builder()
+ .name("Show Deltas")
+ .description(
+ "Specifies whether or not to show the difference in values between the current status and the previous status")
+ .required(true).allowableValues("true", "false").defaultValue("true").build();
+
+ @Override
+ public final List getSupportedPropertyDescriptors() {
+ final List descriptors = new ArrayList<>();
+ descriptors.add(SHOW_DELTAS);
+ return descriptors;
+ }
+
+ @Override
+ public void onTrigger(ReportingContext context) {
+ // TODO Auto-generated method stub
+
+ }
+}
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/example/NakedProcessor.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/example/NakedProcessor.java
new file mode 100644
index 0000000000..6fce1e1386
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/example/NakedProcessor.java
@@ -0,0 +1,16 @@
+package org.apache.nifi.documentation.example;
+
+import org.apache.nifi.processor.AbstractProcessor;
+import org.apache.nifi.processor.ProcessContext;
+import org.apache.nifi.processor.ProcessSession;
+import org.apache.nifi.processor.exception.ProcessException;
+
+public class NakedProcessor extends AbstractProcessor {
+
+ @Override
+ public void onTrigger(ProcessContext arg0, ProcessSession arg1) throws ProcessException {
+ // TODO Auto-generated method stub
+
+ }
+
+}
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/example/SampleService.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/example/SampleService.java
new file mode 100644
index 0000000000..6224364f7b
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/example/SampleService.java
@@ -0,0 +1,25 @@
+/*
+ * 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.example;
+
+import org.apache.nifi.controller.ControllerService;
+
+public interface SampleService extends ControllerService {
+
+ public void doSomething();
+
+}
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/html/HtmlDocumentationWriterTest.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/html/HtmlDocumentationWriterTest.java
new file mode 100644
index 0000000000..9d7926e858
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/html/HtmlDocumentationWriterTest.java
@@ -0,0 +1,93 @@
+/*
+ * 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 java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.apache.nifi.controller.ControllerService;
+import org.apache.nifi.documentation.DocumentationWriter;
+import org.apache.nifi.documentation.example.FullyDocumentedControllerService;
+import org.apache.nifi.documentation.example.FullyDocumentedReportingTask;
+import org.apache.nifi.documentation.mock.MockControllerServiceInitializationContext;
+import org.apache.nifi.documentation.mock.MockReportingInitializationContext;
+import org.apache.nifi.reporting.InitializationException;
+import org.apache.nifi.reporting.ReportingTask;
+import org.junit.Test;
+
+import static org.apache.nifi.documentation.html.XmlValidator.assertContains;
+
+public class HtmlDocumentationWriterTest {
+
+ @Test
+ public void testDocumentControllerService() throws InitializationException, IOException {
+
+ ControllerService controllerService = new FullyDocumentedControllerService();
+ controllerService.initialize(new MockControllerServiceInitializationContext());
+
+ DocumentationWriter writer = new HtmlDocumentationWriter();
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ writer.write(controllerService, baos, false);
+
+ String results = new String(baos.toByteArray());
+ XmlValidator.assertXmlValid(results);
+
+ // description
+ assertContains(results, "A documented controller service that can help you do things");
+
+ // tags
+ assertContains(results, "one, two, three");
+
+ // properties
+ assertContains(results, "Keystore Filename");
+ assertContains(results, "The fully-qualified filename of the Keystore");
+ assertContains(results, "Keystore Type");
+ assertContains(results, "JKS");
+ assertContains(results, "PKCS12");
+ assertContains(results, "Sensitive Property: true");
+ }
+
+ @Test
+ public void testDocumentReportingTask() throws InitializationException, IOException {
+
+ ReportingTask reportingTask = new FullyDocumentedReportingTask();
+ reportingTask.initialize(new MockReportingInitializationContext());
+
+ DocumentationWriter writer = new HtmlDocumentationWriter();
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ writer.write(reportingTask, baos, false);
+
+ String results = new String(baos.toByteArray());
+ XmlValidator.assertXmlValid(results);
+
+ // description
+ assertContains(results, "A helper reporting task to do...");
+
+ // tags
+ assertContains(results, "first, second, third");
+
+ // properties
+ assertContains(results, "Show Deltas");
+ assertContains(results, "Specifies whether or not to show the difference in values between the current status and the previous status");
+ assertContains(results, "true");
+ assertContains(results, "false");
+ }
+}
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/html/ProcessorDocumentationWriterTest.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/html/ProcessorDocumentationWriterTest.java
new file mode 100644
index 0000000000..5306ddf2ee
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/html/ProcessorDocumentationWriterTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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 static org.apache.nifi.documentation.html.XmlValidator.assertContains;
+import static org.apache.nifi.documentation.html.XmlValidator.assertNotContains;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.documentation.DocumentationWriter;
+import org.apache.nifi.documentation.example.FullyDocumentedProcessor;
+import org.apache.nifi.documentation.example.NakedProcessor;
+import org.apache.nifi.documentation.mock.MockProcessorInitializationContext;
+import org.junit.Test;
+
+public class ProcessorDocumentationWriterTest {
+
+ @Test
+ public void testFullyDocumentedProcessor() throws IOException {
+ FullyDocumentedProcessor processor = new FullyDocumentedProcessor();
+ processor.initialize(new MockProcessorInitializationContext());
+
+ DocumentationWriter writer = new HtmlProcessorDocumentationWriter();
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ writer.write(processor, baos, false);
+
+ String results = new String(baos.toByteArray());
+ XmlValidator.assertXmlValid(results);
+
+ assertContains(results, FullyDocumentedProcessor.DIRECTORY.getDisplayName());
+ assertContains(results, FullyDocumentedProcessor.DIRECTORY.getDescription());
+ assertContains(results, FullyDocumentedProcessor.OPTIONAL_PROPERTY.getDisplayName());
+ assertContains(results, FullyDocumentedProcessor.OPTIONAL_PROPERTY.getDescription());
+ assertContains(results, FullyDocumentedProcessor.POLLING_INTERVAL.getDisplayName());
+ assertContains(results, FullyDocumentedProcessor.POLLING_INTERVAL.getDescription());
+ assertContains(results, FullyDocumentedProcessor.POLLING_INTERVAL.getDefaultValue());
+ assertContains(results, FullyDocumentedProcessor.RECURSE.getDisplayName());
+ assertContains(results, FullyDocumentedProcessor.RECURSE.getDescription());
+
+ assertContains(results, FullyDocumentedProcessor.REL_SUCCESS.getName());
+ assertContains(results, FullyDocumentedProcessor.REL_SUCCESS.getDescription());
+ assertContains(results, FullyDocumentedProcessor.REL_FAILURE.getName());
+ assertContains(results, FullyDocumentedProcessor.REL_FAILURE.getDescription());
+ assertContains(results, "Controller Service: ");
+ assertContains(results, "SampleService");
+
+ 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 Tags provided.");
+ assertNotContains(results, "Additional Details...");
+ }
+
+ @Test
+ public void testNakedProcessor() throws IOException {
+ NakedProcessor processor = new NakedProcessor();
+ processor.initialize(new MockProcessorInitializationContext());
+
+ DocumentationWriter writer = new HtmlProcessorDocumentationWriter();
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ writer.write(processor, baos, false);
+
+ String results = new String(baos.toByteArray());
+ XmlValidator.assertXmlValid(results);
+
+ // no description
+ assertContains(results, "No description provided.");
+
+ // no tags
+ assertContains(results, "None.");
+
+ // properties
+ assertContains(results, "This component has no required or optional properties.");
+
+ // relationships
+ assertContains(results, "This processor has no relationships.");
+
+
+ }
+
+}
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/html/XmlValidator.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/html/XmlValidator.java
new file mode 100644
index 0000000000..6cb7499628
--- /dev/null
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/html/XmlValidator.java
@@ -0,0 +1,50 @@
+/*
+ * 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 java.io.IOException;
+import java.io.StringReader;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.junit.Assert;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+/**
+ * A helper class to validate xml documents.
+ *
+ *
+ */
+public class XmlValidator {
+ public static void assertXmlValid(String xml) {
+ try {
+ DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(new StringReader(xml)));
+ } catch (SAXException | IOException | ParserConfigurationException e) {
+ Assert.fail(e.getMessage());
+ }
+ }
+
+ public static void assertContains(String original, String subword) {
+ Assert.assertTrue(original + " did not contain: " + subword, original.contains(subword));
+ }
+
+ public static void assertNotContains(String original, String subword) {
+ Assert.assertFalse(original + " did contain: " + subword, original.contains(subword));
+ }
+}
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-runtime/pom.xml b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-runtime/pom.xml
index 4dc9331b4e..46a1aca719 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-runtime/pom.xml
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-runtime/pom.xml
@@ -31,6 +31,11 @@
org.apache.nifinifi-propertiescompile
+
+
+ org.apache.nifi
+ nifi-documentation
+ compileorg.slf4j
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-runtime/src/main/java/org/apache/nifi/NiFi.java b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-runtime/src/main/java/org/apache/nifi/NiFi.java
index ed7c3298dd..e166f8e6de 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-runtime/src/main/java/org/apache/nifi/NiFi.java
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-runtime/src/main/java/org/apache/nifi/NiFi.java
@@ -31,6 +31,7 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
+import org.apache.nifi.documentation.DocGenerator;
import org.apache.nifi.nar.ExtensionManager;
import org.apache.nifi.nar.ExtensionMapping;
import org.apache.nifi.nar.NarClassLoaders;
@@ -117,6 +118,8 @@ public class NiFi {
// discover the extensions
ExtensionManager.discoverExtensions();
ExtensionManager.logClassLoaderMapping();
+
+ DocGenerator.generate(properties);
// load the server from the framework classloader
Thread.currentThread().setContextClassLoader(frameworkClassLoader);
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-docs/src/main/webapp/css/component-usage.css b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-docs/src/main/webapp/css/component-usage.css
index 816403427e..45c1e267ac 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-docs/src/main/webapp/css/component-usage.css
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-docs/src/main/webapp/css/component-usage.css
@@ -42,30 +42,75 @@ body {
/* tables */
table {
- background-color: #fefefe;
- border: 1px solid #ccc;
- border-left: 6px solid #ccc;
- color: #555;
- display: block;
- margin-bottom: 12px;
- padding: 5px 8px;
+ color:#666;
+ font-size:14px;
+ background:#eaebec;
+ border:#ccc 1px solid;
+ -webkit-border-radius:3px;
+ border-radius:3px;
+ width: 100%;
}
-tr td {
- font-size: 14px;
- vertical-align:top;
- text-align:left;
- padding: 4px;
- border-width: 0;
+table th {
+ padding:11px 15px 12px 15px;
+ border-top:1px solid #fafafa;
+ border-bottom:1px solid #e0e0e0;
+
+ background: #ededed;
}
-tr th {
- font-size: 16px;
- vertical-align:top;
- text-align:left;
- padding: 4px;
- border-width: 0;
- white-space: nowrap;
+table th:first-child {
+ text-align: left;
+ padding-left:10px;
+}
+
+table th:last-child {
+ text-align: left;
+ padding-left:10px;
+}
+
+table tr:first-child th:first-child {
+ border-top-left-radius:3px;
+}
+
+table tr:first-child th:last-child {
+ border-top-right-radius:3px;
+}
+
+table tr {
+ text-align: center;
+ padding-left:10px;
+}
+
+table td:first-child {
+ text-align: left;
+ padding-left:10px;
+ border-left: 0;
+}
+
+table td:last-child {
+ text-align: left;
+ padding-left:10px;
+ border-left: 0;
+ vertical-align: top;
+
+}
+
+table td {
+ padding:12px;
+ background: #fafafa;
+}
+
+table tr:last-child td {
+ border-bottom:0;
+}
+
+table tr:last-child td:first-child {
+ border-bottom-left-radius:3px;
+}
+
+table tr:last-child td:last-child {
+ border-bottom-right-radius:3px;
}
/* links */
@@ -99,10 +144,13 @@ p strong {
}
/* ul li */
-
+td ul {
+ margin: 0px 0px 0px 0px;
+ padding-left: 20px;
+}
ul li {
- font-family: 'Noto Serif', 'DejaVu Serif', serif;
- font-size: 16px;
+ text-align: left;
+ display: list-item;
}
ul li strong {
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/pom.xml b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/pom.xml
index d5d449964c..b0ed5c1969 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/pom.xml
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/pom.xml
@@ -39,6 +39,7 @@
nifi-administrationnifi-webnifi-resources
+ nifi-documentation
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/pom.xml b/nifi/nifi-nar-bundles/nifi-framework-bundle/pom.xml
index 18f889c5ad..4c07d7562c 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/pom.xml
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/pom.xml
@@ -108,6 +108,11 @@
nifi-web-security0.1.0-incubating-SNAPSHOT
+
+ org.apache.nifi
+ nifi-documentation
+ 0.0.2-incubating-SNAPSHOT
+
diff --git a/nifi/nifi-nar-bundles/nifi-hadoop-bundle/nifi-hdfs-processors/src/main/resources/docs/org.apache.nifi.processors.hadoop.CreateHadoopSequenceFile/index.html b/nifi/nifi-nar-bundles/nifi-hadoop-bundle/nifi-hdfs-processors/src/main/resources/docs/org.apache.nifi.processors.hadoop.CreateHadoopSequenceFile/additionalDetails.html
similarity index 68%
rename from nifi/nifi-nar-bundles/nifi-hadoop-bundle/nifi-hdfs-processors/src/main/resources/docs/org.apache.nifi.processors.hadoop.CreateHadoopSequenceFile/index.html
rename to nifi/nifi-nar-bundles/nifi-hadoop-bundle/nifi-hdfs-processors/src/main/resources/docs/org.apache.nifi.processors.hadoop.CreateHadoopSequenceFile/additionalDetails.html
index ff1709ebdd..d9403822b8 100644
--- a/nifi/nifi-nar-bundles/nifi-hadoop-bundle/nifi-hdfs-processors/src/main/resources/docs/org.apache.nifi.processors.hadoop.CreateHadoopSequenceFile/index.html
+++ b/nifi/nifi-nar-bundles/nifi-hadoop-bundle/nifi-hdfs-processors/src/main/resources/docs/org.apache.nifi.processors.hadoop.CreateHadoopSequenceFile/additionalDetails.html
@@ -43,41 +43,6 @@
NOTE: The value portion of a key/value pair is loaded into memory. While there is a max size limit of 2GB, this could cause memory
issues if there are too many concurrent tasks and the flow file sizes are large.
-
Properties:
-
In the list below, the names of required properties appear in bold. Any other properties (not in bold) are
- considered optional. If a property has a default value, it is indicated. If a property supports the use of the
- NiFi Expression Language (or simply, "expression language"), that is also indicated.
-
-
Hadoop Configuration Resources
-
-
A file or comma separated list of files which contains the Hadoop file system configuration.
- Without this, Hadoop will search the classpath for a 'core-site.xml' and 'hdfs-site.xml' file or will
- revert to a default configuration.
-
Default value: none
-
-
-
compression type
-
-
Type of compression to use when creating Sequence File.
-
Default value: none
-
-
-
-
-
Relationships:
-
-
success
-
-
Generated Sequence Files are sent to this relationship.
-
-
-
fail
-
-
Incoming files that failed to generate a Sequence File are sent to this relationship.
The path is set to the relative path of the file's directory on HDFS. For example, if the Directory
+ property is set to /tmp, then files picked up from /tmp will have the path attribute set to
+ "./". If the Recurse Subdirectories property is set to true
+ and a file is picked up from /tmp/abc/1/2/3, then the path attribute will be set to "abc/1/2/3".
+
This processor reads files from an HDFS cluster into NiFi FlowFiles.
-
-
- Modifies Attributes:
-
-
-
-
-
Attribute Name
-
Description
-
-
-
-
-
filename
-
The name of the file that was read from HDFS.
-
-
-
path
-
The path is set to the relative path of the file's directory on HDFS. For example, if the Directory
- property is set to /tmp, then files picked up from /tmp will have the path attribute set to
- "./". If the Recurse Subdirectories property is set to true
- and a file is picked up from /tmp/abc/1/2/3, then the path attribute will be set to "abc/1/2/3".
-
-
-
-
-
-
Properties:
-
In the list below, the names of required properties appear in bold. Any other properties (not in bold) are
- considered optional. If a property has a default value, it is indicated. If a property supports the use of the
- NiFi Expression Language (or simply, "expression language"), that is also indicated.
-
-
Hadoop Configuration Resources
-
-
A file or comma separated list of files which contains the Hadoop file system configuration.
- Without this, Hadoop will search the classpath for a 'core-site.xml' and 'hdfs-site.xml' file or will
- revert to a default configuration.
-
Default value: none
-
-
-
Directory
-
-
The HDFS directory from which FlowFile content should be read.
-
Default value: none
-
-
Recurse Subdirectories
-
-
A Boolean value (true/false), when true will pull files from subdirectories of the HDFS Directory.
-
-
Default value: true
-
-
Keep Source File
-
-
A Boolean value (true/false), indicates whether to keep (true) or delete (false) the file from HDFS
- after it has been successfully transferred.
-
Default value: false
-
-
File Filter Regex
-
-
A Java Regular Expression for filtering Filenames; if a filter is supplied then only files whose
- names match that Regular Expression will be fetched, otherwise all files will be fetched.
-
Default value: none
-
-
-
Filter Match Name Only
-
-
A Boolean value (true/false), when true File Filter Regex will match on just the filename,
- otherwise subdirectory names will be included with filename in the regex comparison.
-
-
Default value: true
-
-
Ignore Dotted Files
-
-
A Boolean value (true/false), when true files whose names begin with a dot (".") will not
- be fetched.
-
Default value: true
-
-
Minimum File Age
-
-
The minimum age that a file must be in order to be fetched; any file that is younger than this
- amount of time (based on last modification time) will be ignored. The value must be a non-negative
- integer and be followed by a time unit, such as nanos, millis, secs, mins, hrs, days.
-
Default value: 0 sec
-
-
Maximum File Age
-
-
The maximum age that a file must be in order to be fetched; any file that is older than this amount
- of time (based on last modification time) will be ignored. The value must be a non-negative integer,
- followed by a time unit, such as nanos, millis, secs, mins, hrs, days. Cannot be less than 100 millis.
-
Default value: none
-
-
-
Polling Interval
-
-
Indicates how long to wait between performing directory listings. The value must be a non-negative
- integer and be followed by a time unit, such as nanos, millis, secs, mins, hrs, days.
-
Default value: 0 sec
-
-
Batch Size
-
-
The maximum number of files to pull in each iteration, based on configured run schedule.
-
Default value: 100
-
-
IO Buffer Size
-
-
Amount of memory to use to buffer file contents during IO. This is a data size integer that must
- include units of B, KB, MB, GB, or TB. This overrides the Hadoop Configuration.
-
Default value: none
-
-
-
-
-
Relationships:
-
-
success
-
-
All files retrieved from HDFS are transferred to this relationship.
-
-
-
passthrough
-
-
If this processor has an input queue for some reason, then FlowFiles arriving on that input are
- transferred to this relationship.
This processor is used to pull files from HDFS. The files being pulled in MUST be SequenceFile
- formatted files. The processor creates a flow file for each key/value entry in the ingested SequenceFile.
- The created flow file's content depends on the value of the optional configuration property FlowFile Content. Currently,
- there are two choices: VALUE ONLY and KEY VALUE PAIR. With the prior, only the SequenceFile value element is
- written to the flow file contents. With the latter, the SequenceFile key and value are written to the flow file
- contents as serialized objects; the format is key length (int), key(String), value length(int), value(bytes). The default is
- VALUE ONLY.
- NOTE: This processor loads the entire value entry into memory. While the size limit for a value entry is 2GB, this will cause
- memory problems if there are too many concurrent tasks and the data being ingested is large.
-
-
Properties:
-
In the list below, the names of required properties appear in bold. Any other properties (not in bold) are
- considered optional. If a property has a default value, it is indicated. If a property supports the use of the
- NiFi Expression Language (or simply, "expression language"), that is also indicated.
-
-
Hadoop Configuration Resources
-
-
A file or comma separated list of files which contains the Hadoop file system configuration.
- Without this, Hadoop will search the classpath for a 'core-site.xml' and 'hdfs-site.xml' file or will
- revert to a default configuration.
-
Default value: none
-
-
-
FlowFile Content
-
-
Indicate if the content is to be both the key and value of the Sequence File, or just the value.
-
Default value: VALUE ONLY
-
-
-
Directory
-
-
The HDFS directory from which FlowFile content should be read.
-
Default value: none
-
-
Recurse Subdirectories
-
-
A Boolean value (true/false), when true will pull files from subdirectories of the HDFS Directory.
-
-
Default value: true
-
-
Keep Source File
-
-
A Boolean value (true/false), indicates whether to keep (true) or delete (false) the file from HDFS
- after it has been successfully transferred.
-
Default value: false
-
-
File Filter Regex
-
-
A Java Regular Expression for filtering Filenames; if a filter is supplied then only files whose
- names match that Regular Expression will be fetched, otherwise all files will be fetched.
-
Default value: none
-
-
-
Filter Match Name Only
-
-
A Boolean value (true/false), when true File Filter Regex will match on just the filename,
- otherwise subdirectory names will be included with filename in the regex comparison.
-
-
Default value: true
-
-
Ignore Dotted Files
-
-
A Boolean value (true/false), when true files whose names begin with a dot (".") will not
- be fetched.
-
Default value: true
-
-
Minimum File Age
-
-
The minimum age that a file must be in order to be fetched; any file that is younger than this
- amount of time (based on last modification time) will be ignored. The value must be a non-negative
- integer and be followed by a time unit, such as nanos, millis, secs, mins, hrs, days.
-
Default value: 0 sec
-
-
Maximum File Age
-
-
The maximum age that a file must be in order to be fetched; any file that is older than this amount
- of time (based on last modification time) will be ignored. The value must be a non-negative integer,
- followed by a time unit, such as nanos, millis, secs, mins, hrs, days. Cannot be less than 100 millis.
-
Default value: none
-
-
-
Polling Interval
-
-
Indicates how long to wait between performing directory listings. The value must be a non-negative
- integer and be followed by a time unit, such as nanos, millis, secs, mins, hrs, days.
-
Default value: 0 sec
-
-
Batch Size
-
-
The maximum number of files to pull in each iteration, based on configured run schedule.
-
Default value: 100
-
-
IO Buffer Size
-
-
Amount of memory to use to buffer file contents during IO. This is a data size integer that must
- include units of B, KB, MB, GB, or TB. This overrides the Hadoop Configuration.
-
Default value: none
-
-
-
-
-
Relationships:
-
-
success
-
-
All files retrieved from HDFS are transferred to this relationship.
-
-
-
passthrough
-
-
If this processor has an input queue for some reason, then FlowFiles arriving on that input are
- transferred to this relationship.
- This processor writes FlowFiles to an HDFS cluster. It will create directories in which to store files as
- needed based on the Directory property.
-
-
-
- When files are written to HDFS, the file's owner is the user identity of the NiFi process, the file's group is the
- group of the parent directory, and the read/write/execute permissions use the default umask. The owner can be
- overridden using the Remote Owner property, the group can be overridden using the Remote Group
- property, and the read/write/execute permissions can be overridden using the Permissions umask property.
-
-
NOTE: This processor can change owner or group only if the user identity of the NiFi process has super user
- privilege in HDFS to do so.
-
- NOTE: The Permissions umask property cannot add execute permissions to regular files.
-
-
-
- Uses Attributes:
-
-
-
-
-
Attribute Name
-
Description
-
-
-
-
-
filename
-
The name of the file written to HDFS comes from the value of this attribute.
-
-
-
-
-
Properties:
-
In the list below, the names of required properties appear in bold. Any other properties (not in bold) are
- considered optional. If a property has a default value, it is indicated. If a property supports the use of the
- NiFi Expression Language (or simply, "expression language"), that is also indicated.
-
-
Hadoop Configuration Resources
-
-
A file or comma separated list of files which contains the Hadoop file system configuration.
- Without this, Hadoop will search the classpath for a 'core-site.xml' and 'hdfs-site.xml' file or will
- revert to a default configuration.
-
Default value: none
-
-
-
Directory
-
-
The HDFS directory to which FlowFile content should be written. This property supports the
- expression language so you can keep the FlowFile's directory structure by using the ${path} attribute
- reference, e.g. /in/data/${path}.
-
Default value: none
-
Supports expression language: true
-
-
Conflict Resolution Strategy
-
-
Indicates what should happen when a file with the same name already exists in the output directory.
- Valid options are:
-
-
replace - existing file is overwritten by new file
-
ignore - existing file is untouched, FlowFile routed to success
-
fail - existing file is untouched, FlowFile routed to failure
-
-
Default value: fail
-
-
Block Size
-
-
Size of each block as written to HDFS. This is a data size integer that must include units of B,
- KB, MB, GB, or TB. This overrides the Hadoop Configuration.
-
Default value: none
-
-
-
IO Buffer Size
-
-
Amount of memory to use to buffer file contents during IO. This is a data size integer that must
- include units of B, KB, MB, GB, or TB. This overrides the Hadoop Configuration.
-
Default value: none
-
-
-
Replication
-
-
Number of times that HDFS will replicate each file. This must be an integer greater than 0. This
- overrides the Hadoop Configuration.
-
Default value: none
-
-
-
Permissions umask
-
-
A umask represented as an octal number which determines the permissions of files written to HDFS.
- This overrides the Hadoop Configuration dfs.umaskmode.
-
Default value: none
-
-
-
Remote Owner
-
-
Changes the owner of the HDFS file to this value after it is written. This only works if NiFi is
- running as a user that has HDFS super user privilege to change owner.
-
Default value: none
-
-
-
Remote Group
-
-
Changes the group of the HDFS file to this value after it is written. This only works if NiFi is
- running as a user that has HDFS super user privilege to change group.
-
Default value: none
-
-
-
-
-
Relationships:
-
-
success
-
-
Files that have been successfully written to HDFS are transferred to this relationship.
-
-
-
failure
-
-
Files that could not be written to HDFS for some reason are transferred to this relationship.
+ This Processors polls Apache Kafka
+ for data. When a message is received from Kafka, this Processor emits a FlowFile
+ where the content of the FlowFile is the value of the Kafka message. If the
+ message has a key associated with it, an attribute named kafka.key
+ will be added to the FlowFile, with the value being the UTF-8 Encoded value
+ of the Message's Key.
+
+
+ Kafka supports the notion of a Consumer Group when pulling messages in order to
+ provide scalability while still offering a publish-subscribe interface. Each
+ Consumer Group must have a unique identifier. The Consumer Group identifier that
+ is used by NiFi is the UUID of the Processor. This means that all of the nodes
+ within a cluster will use the same Consumer Group Identifier so that they do
+ not receive duplicate data but multiple GetKafka Processors can be used to pull
+ from multiple Topics, as each Processor will receive a different Processor UUID
+ and therefore a different Consumer Group Identifier.
+
+
+
+ Modifies Attributes:
+
+
+
+
+
Attribute Name
+
Description
+
+
+
+
+
kafka.topic
+
The name of the Kafka Topic from which the message was received
+
+
+
kafka.key
+
The key of the Kafka message, if it exists and batch size is 1. If the message does not have a key,
+ or if the batch size is greater than 1, this attribute will not be added.
+
+
+
kafka.partition
+
The partition of the Kafka Topic from which the message was received. This attribute is added only
+ if the batch size is 1.
+
+
+
kafka.offset
+
The offset of the message within the Kafka partition. This attribute is added only
+ if the batch size is 1.
- This Processors polls Apache Kafka
- for data. When a message is received from Kafka, this Processor emits a FlowFile
- where the content of the FlowFile is the value of the Kafka message. If the
- message has a key associated with it, an attribute named kafka.key
- will be added to the FlowFile, with the value being the UTF-8 Encoded value
- of the Message's Key.
-
-
- Kafka supports the notion of a Consumer Group when pulling messages in order to
- provide scalability while still offering a publish-subscribe interface. Each
- Consumer Group must have a unique identifier. The Consumer Group identifier that
- is used by NiFi is the UUID of the Processor. This means that all of the nodes
- within a cluster will use the same Consumer Group Identifier so that they do
- not receive duplicate data but multiple GetKafka Processors can be used to pull
- from multiple Topics, as each Processor will receive a different Processor UUID
- and therefore a different Consumer Group Identifier.
-
-
-
- Modifies Attributes:
-
-
-
-
-
Attribute Name
-
Description
-
-
-
-
-
kafka.topic
-
The name of the Kafka Topic from which the message was received
-
-
-
kafka.key
-
The key of the Kafka message, if it exists and batch size is 1. If the message does not have a key,
- or if the batch size is greater than 1, this attribute will not be added.
-
-
-
kafka.partition
-
The partition of the Kafka Topic from which the message was received. This attribute is added only
- if the batch size is 1.
-
-
-
kafka.offset
-
The offset of the message within the Kafka partition. This attribute is added only
- if the batch size is 1.
-
-
-
-
-
-
- Properties:
-
-
In the list below, the names of required properties appear
- in bold. Any other properties (not in bold) are considered optional.
- If a property has a default value, it is indicated. If a property
- supports the use of the NiFi Expression Language (or simply,
- "expression language"), that is also indicated.
-
-
ZooKeeper Connection String
-
-
The Connection String to use in order to connect to ZooKeeper. This is often a
- comma-separated list of <host>:<port> combinations. For example,
- host1:2181,host2:2181,host3:2188
-
Default value: no default
-
Supports expression language: false
-
-
-
Topic Name
-
-
The Kafka Topic to pull messages from
-
Default value: no default
-
Supports expression language: false
-
-
-
Zookeeper Commit Frequency
-
-
Specifies how often to communicate with ZooKeeper to indicate which messages have been pulled.
- A longer time period will result in better overall performance but can result in more data
- duplication if a NiFi node is lost
-
-
Default value: 60 secs
-
Supports expression language: false
-
-
-
ZooKeeper Communications Timeout
-
-
The amount of time to wait for a response from ZooKeeper before determining that there is a communications error
-
Default value: 30 secs
-
Supports expression language: false
-
-
-
Kafka Communications Timeout
-
-
The amount of time to wait for a response from Kafka before determining that there is a communications error
-
Default value: 30 secs
-
Supports expression language: false
-
-
-
-
Batch Size
-
-
Specifies the maximum number of messages to combine into a single FlowFile.
- These messages will be concatenated together with the <Message Demarcator>
- string placed between the content of each message. If the messages from Kafka
- should not be concatenated together, leave this value at 1.
-
Default value: 1
-
Supports expression language: false
-
-
-
-
Message Demarcator
-
-
Specifies the characters to use in order to demarcate multiple messages from Kafka.
- If the <Batch Size> property is set to 1, this value is ignored. Otherwise, for each two
- subsequent messages in the batch, this value will be placed in between them. This property will
- treat "\n" as a new-line, "\r" as a carriage return and "\t" as a tab character. All other
- characters are treated as literal characters.
-
-
Default value: \n
-
Supports expression language: false
-
-
-
Client Name
-
-
Client Name to use when communicating with Kafka
-
Default value: "NiFi-" followed by the UUID of the Processor
-
Supports expression language: false
-
-
-
-
-
- Relationships:
-
-
-
success
-
-
All messages that are received from Kafka are routed to the 'success' relationship
-
-
-
-
-
-
diff --git a/nifi/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/resources/docs/org.apache.nifi.processors.standard.ValidateXml/index.html b/nifi/nifi-nar-bundles/nifi-kafka-bundle/nifi-kafka-processors/src/main/resources/docs/org.apache.nifi.processors.kafka.PutKafka/additionalDetails.html
similarity index 50%
rename from nifi/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/resources/docs/org.apache.nifi.processors.standard.ValidateXml/index.html
rename to nifi/nifi-nar-bundles/nifi-kafka-bundle/nifi-kafka-processors/src/main/resources/docs/org.apache.nifi.processors.kafka.PutKafka/additionalDetails.html
index d4db3cb4aa..04d9463ed6 100644
--- a/nifi/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/resources/docs/org.apache.nifi.processors.standard.ValidateXml/index.html
+++ b/nifi/nifi-nar-bundles/nifi-kafka-bundle/nifi-kafka-processors/src/main/resources/docs/org.apache.nifi.processors.kafka.PutKafka/additionalDetails.html
@@ -16,8 +16,7 @@
-->
- ValidateXml
-
+ PutKafka
@@ -25,32 +24,22 @@
Description:
- This processor validates the contents of FlowFiles against a user-specified XML schema file.
-
-
- Properties:
+ This Processors puts the contents of a FlowFile to a Topic in
+ Apache Kafka. The full contents of
+ a FlowFile becomes the contents of a single message in Kafka.
+ This message is optionally assigned a key by using the
+ <Kafka Key> Property.
-
- In the list below, the names of required properties appear in bold. Any other properties (not in bold) are considered optional. If a property has a default value, it is indicated. If a property supports the use of the NiFi Expression Language (or simply, "expression language"), that is also indicated.
-
-
-
Schema File
-
-
The local file path to the schema file to be used for validation.
-
Default value: no default
-
Supports expression language: false
-
-
-
-
- Relationships:
-
-
-
valid
-
-
If FlowFiles are successfully validated against the schema, then they follow this relationship.
-
-
+
+ The Processor allows the user to configure an optional Message Delimiter that
+ can be used to send many messages per FlowFile. For example, a \n could be used
+ to indicate that the contents of the FlowFile should be used to send one message
+ per line of text. If the property is not set, the entire contents of the FlowFile
+ will be sent as a single message. When using the delimiter, if some messages are
+ successfully sent but other messages fail to send, the FlowFile will be FORKed into
+ two child FlowFiles, with the successfully sent messages being routed to 'success'
+ and the messages that could not be sent going to 'failure'.
+