NIFI-280 - Adding new documentation framework component and incorporating
in framework


Project: http://git-wip-us.apache.org/repos/asf/incubator-nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-nifi/commit/d7cf25be
Tree: http://git-wip-us.apache.org/repos/asf/incubator-nifi/tree/d7cf25be
Diff: http://git-wip-us.apache.org/repos/asf/incubator-nifi/diff/d7cf25be

Branch: refs/heads/NIFI-353
Commit: d7cf25be9a157f6934593a325f906a0fc85e4007
Parents: 4977673
Author: danbress <[email protected]>
Authored: Sat Jan 31 09:02:23 2015 -0500
Committer: danbress <[email protected]>
Committed: Sat Feb 7 10:47:06 2015 -0500

----------------------------------------------------------------------
 .../nifi-framework/nifi-documentation/pom.xml   |  49 +++
 .../ConfigurableComponentInitializer.java       |  38 ++
 .../apache/nifi/documentation/DocGenerator.java | 182 ++++++++
 .../nifi/documentation/DocumentationWriter.java |  33 ++
 .../html/HtmlDocumentationWriter.java           | 415 +++++++++++++++++++
 .../html/HtmlProcessorDocumentationWriter.java  |  60 +++
 .../init/ControllerServiceInitializer.java      |  38 ++
 .../init/ProcessorInitializer.java              |  37 ++
 .../init/ReportingTaskingInitializer.java       |  37 ++
 ...kControllerServiceInitializationContext.java |  40 ++
 .../mock/MockControllerServiceLookup.java       |  55 +++
 .../MockProcessorInitializationContext.java     |  45 ++
 .../MockReportingInitializationContext.java     |  63 +++
 .../src/main/resources/apache.license           |  12 +
 .../FullyDocumentedControllerService.java       |  57 +++
 .../example/FullyDocumentedProcessor.java       | 100 +++++
 .../example/FullyDocumentedReportingTask.java   |  50 +++
 .../html/HtmlDocumentationWriterTest.java       |  56 +++
 .../html/ProcessorDocumentationWriterTest.java  |  69 +++
 .../nifi/documentation/html/XmlValidator.java   |  50 +++
 .../nifi-framework/nifi-runtime/pom.xml         |   5 +
 .../src/main/java/org/apache/nifi/NiFi.java     |   3 +
 .../nifi-framework/pom.xml                      |   1 +
 .../nifi-framework-bundle/pom.xml               |   5 +
 24 files changed, 1500 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/d7cf25be/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/pom.xml
----------------------------------------------------------------------
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 0000000..f35c8cd
--- /dev/null
+++ 
b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/pom.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0"?>
+<project
+       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";
+       xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";>
+       <!-- 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. -->
+       <modelVersion>4.0.0</modelVersion>
+       <parent>
+               <groupId>org.apache.nifi</groupId>
+               <artifactId>nifi-framework</artifactId>
+               <version>0.0.2-incubating-SNAPSHOT</version>
+       </parent>
+       <artifactId>nifi-documentation</artifactId>
+       <dependencies>
+               <dependency>
+                       <groupId>org.apache.nifi</groupId>
+                       <artifactId>nifi-nar-utils</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.apache.nifi</groupId>
+                       <artifactId>nifi-api</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.apache.nifi</groupId>
+                       <artifactId>nifi-properties</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>commons-io</groupId>
+                       <artifactId>commons-io</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.apache.commons</groupId>
+                       <artifactId>commons-lang3</artifactId>
+               </dependency>
+               <dependency>
+                       <groupId>org.apache.nifi</groupId>
+                       <artifactId>nifi-processor-utils</artifactId>
+                       <scope>test</scope>
+               </dependency>
+       </dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/d7cf25be/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/ConfigurableComponentInitializer.java
----------------------------------------------------------------------
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 0000000..164d212
--- /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;
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/d7cf25be/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/DocGenerator.java
----------------------------------------------------------------------
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 0000000..9a37356
--- /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<Class> 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;
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/d7cf25be/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/DocumentationWriter.java
----------------------------------------------------------------------
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 0000000..5a3c5d8
--- /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;
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/d7cf25be/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/html/HtmlDocumentationWriter.java
----------------------------------------------------------------------
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 0000000..bf9bd13
--- /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,415 @@
+/*
+ * 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.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.components.AllowableValue;
+import org.apache.nifi.components.ConfigurableComponent;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.documentation.DocumentationWriter;
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.Tags;
+
+/**
+ * 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";
+
+       /**
+        * The apache licence to apply to the top of the generated html
+        */
+       private static final String apacheLicense;
+
+       static {
+               String value = null;
+               try {
+                       value = 
IOUtils.toString(ClassLoader.getSystemResourceAsStream("apache.license"));
+               } catch (IOException e) {
+                       e.printStackTrace();
+               }
+
+               apacheLicense = value;
+       }
+
+       @Override
+       public void write(final ConfigurableComponent configurableComponent, 
final OutputStream streamToWriteTo,
+                       final boolean includesAdditionalDocumentation) throws 
IOException {
+
+               try {
+                       XMLStreamWriter xmlStreamWriter = 
XMLOutputFactory.newInstance().createXMLStreamWriter(streamToWriteTo,
+                                       "UTF-8");
+                       xmlStreamWriter.writeDTD("<!DOCTYPE html>");
+                       xmlStreamWriter.writeStartElement("html");
+                       xmlStreamWriter.writeAttribute("lang", "en");
+                       writeHead(configurableComponent, xmlStreamWriter);
+                       writeBody(configurableComponent, xmlStreamWriter, 
includesAdditionalDocumentation);
+                       xmlStreamWriter.writeEndElement();
+                       xmlStreamWriter.close();
+               } catch (XMLStreamException | FactoryConfigurationError e) {
+                       throw new IOException("Unable to create 
XMLOutputStream", e);
+               }
+       }
+
+       /**
+        * Writes the head portion of the HTML documentation.
+        * 
+        * @param configurableComponent
+        *            the component to describe
+        * @param xmlStreamWriter
+        *            the stream to write to
+        * @throws XMLStreamException
+        *             thrown if there was a problem writing to the stream
+        */
+       protected void writeHead(final ConfigurableComponent 
configurableComponent, final XMLStreamWriter xmlStreamWriter)
+                       throws XMLStreamException {
+
+               // write the apache license
+               xmlStreamWriter.writeComment(apacheLicense);
+               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("p");
+               if (tags != null) {
+                       final String tagString = StringUtils.join(tags.value(), 
", ");
+                       xmlStreamWriter.writeCharacters("Tags: ");
+                       xmlStreamWriter.writeCharacters(tagString);
+               } else {
+                       xmlStreamWriter.writeCharacters("No Tags provided.");
+               }
+               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 {
+               xmlStreamWriter.writeStartElement("p");
+               writeSimpleElement(xmlStreamWriter, "strong", "Properties: ");
+               xmlStreamWriter.writeEndElement();
+               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 (or simply 
EL)",
+                               "../../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();
+
+               boolean containsSensitiveElement = false;
+
+               List<PropertyDescriptor> properties = 
configurableComponent.getPropertyDescriptors();
+               if (properties.size() > 0) {
+                       xmlStreamWriter.writeStartElement("table");
+
+                       // write the header row
+                       xmlStreamWriter.writeStartElement("tr");
+                       writeSimpleElement(xmlStreamWriter, "th", "Name");
+                       writeSimpleElement(xmlStreamWriter, "th", 
"Description");
+                       writeSimpleElement(xmlStreamWriter, "th", "Default 
Value");
+                       writeSimpleElement(xmlStreamWriter, "th", "Valid 
Values");
+                       xmlStreamWriter.writeStartElement("th");
+                       writeLink(xmlStreamWriter, "EL", 
"../../html/expression-language-guide.html");
+                       xmlStreamWriter.writeEndElement();
+                       xmlStreamWriter.writeEndElement();
+
+                       // write the individual properties
+                       for (PropertyDescriptor property : properties) {
+                               containsSensitiveElement |= 
property.isSensitive();
+                               xmlStreamWriter.writeStartElement("tr");
+                               xmlStreamWriter.writeStartElement("td");
+                               if (property.isRequired()) {
+                                       writeSimpleElement(xmlStreamWriter, 
"strong", property.getDisplayName());
+                               } else {
+                                       
xmlStreamWriter.writeCharacters(property.getDisplayName());
+                               }
+                               if (property.isSensitive()) {
+                                       writeSensitiveImg(xmlStreamWriter);
+                               }
+                               xmlStreamWriter.writeEndElement();
+                               writeSimpleElement(xmlStreamWriter, "td", 
property.getDescription());
+                               writeSimpleElement(xmlStreamWriter, "td", 
property.getDefaultValue());
+                               writeValidValues(xmlStreamWriter, property);
+                               writeSimpleElement(xmlStreamWriter, "td", 
property.isExpressionLanguageSupported() ? "Yes" : "No");
+                               xmlStreamWriter.writeEndElement();
+                       }
+
+                       // TODO support dynamic properties...
+                       xmlStreamWriter.writeEndElement();
+                       
+                       if (containsSensitiveElement) {
+                               writeSensitiveImg(xmlStreamWriter);
+                               xmlStreamWriter.writeCharacters(" indicates 
that a property is a sensitive property");
+                       }
+
+               } else {
+                       writeSimpleElement(xmlStreamWriter, "p", "This 
component has no required or optional properties.");
+               }
+       }
+
+       private void writeSensitiveImg(final XMLStreamWriter xmlStreamWriter) 
throws XMLStreamException {
+               xmlStreamWriter.writeCharacters(" ");
+               xmlStreamWriter.writeStartElement("img");
+               xmlStreamWriter.writeAttribute("src", 
"../../html/images/iconSecure.png");
+               xmlStreamWriter.writeAttribute("alt", "Sensitive Property");
+               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 {
+               xmlStreamWriter.writeStartElement("td");
+               if (property.getAllowableValues() != null && 
property.getAllowableValues().size() > 0) {
+                       xmlStreamWriter.writeStartElement("ul");
+                       for (AllowableValue value : 
property.getAllowableValues()) {
+                               writeSimpleElement(xmlStreamWriter, "li", 
value.getDisplayName());
+                       }
+                       xmlStreamWriter.writeEndElement();
+               }
+               xmlStreamWriter.writeEndElement();
+       }
+
+       /**
+        * Writes a begin element, then text, then end element for the element 
of a
+        * users choosing. Example: &lt;p&gt;text&lt;/p&gt;
+        * 
+        * @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: &lt;p&gt;text&lt;/p&gt;
+        * 
+        * @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();
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/d7cf25be/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/html/HtmlProcessorDocumentationWriter.java
----------------------------------------------------------------------
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 0000000..2637235
--- /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,60 @@
+/*
+ * 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;
+               xmlStreamWriter.writeStartElement("p");
+               writeSimpleElement(xmlStreamWriter, "strong", "Relationships: 
");
+               xmlStreamWriter.writeEndElement();
+
+               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.");
+               }
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/d7cf25be/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/init/ControllerServiceInitializer.java
----------------------------------------------------------------------
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 0000000..123a39c
--- /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());
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/d7cf25be/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/init/ProcessorInitializer.java
----------------------------------------------------------------------
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 0000000..a33f7b9
--- /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());
+       }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/d7cf25be/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/init/ReportingTaskingInitializer.java
----------------------------------------------------------------------
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 0000000..ff915cf
--- /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());
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/d7cf25be/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockControllerServiceInitializationContext.java
----------------------------------------------------------------------
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 0000000..6153a8e
--- /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();
+       }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/d7cf25be/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockControllerServiceLookup.java
----------------------------------------------------------------------
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 0000000..88d0911
--- /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<String> getControllerServiceIdentifiers(Class<? extends 
ControllerService> serviceType)
+                       throws IllegalArgumentException {
+               return Collections.emptySet();
+       }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/d7cf25be/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockProcessorInitializationContext.java
----------------------------------------------------------------------
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 0000000..48ffecb
--- /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();
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/d7cf25be/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/java/org/apache/nifi/documentation/mock/MockReportingInitializationContext.java
----------------------------------------------------------------------
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 0000000..9782077
--- /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;
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/d7cf25be/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/resources/apache.license
----------------------------------------------------------------------
diff --git 
a/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/resources/apache.license
 
b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/resources/apache.license
new file mode 100644
index 0000000..d7b32e8
--- /dev/null
+++ 
b/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/main/resources/apache.license
@@ -0,0 +1,12 @@
+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.
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/d7cf25be/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/example/FullyDocumentedControllerService.java
----------------------------------------------------------------------
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 0000000..c27f3ba
--- /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<PropertyDescriptor> properties;
+
+       static {
+               List<PropertyDescriptor> props = new ArrayList<>();
+               props.add(KEYSTORE);
+               props.add(KEYSTORE_PASSWORD);
+               props.add(KEYSTORE_TYPE);
+               properties = Collections.unmodifiableList(props);
+       }
+
+       @Override
+       protected List<PropertyDescriptor> getSupportedPropertyDescriptors() {
+               return properties;
+       }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/d7cf25be/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/example/FullyDocumentedProcessor.java
----------------------------------------------------------------------
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 0000000..2a00177
--- /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,100 @@
+/*
+ * 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.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("true", 
"false").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 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<PropertyDescriptor> properties;
+       private Set<Relationship> relationships;
+
+       @Override
+       protected void init(ProcessorInitializationContext context) {
+               final List<PropertyDescriptor> properties = new ArrayList<>();
+               properties.add(DIRECTORY);
+               properties.add(RECURSE);
+               properties.add(POLLING_INTERVAL);
+               properties.add(OPTIONAL_PROPERTY);
+               this.properties = Collections.unmodifiableList(properties);
+
+               final Set<Relationship> relationships = new HashSet<>();
+               relationships.add(REL_SUCCESS);
+               relationships.add(REL_FAILURE);
+               this.relationships = Collections.unmodifiableSet(relationships);
+       }
+
+       @Override
+       protected List<PropertyDescriptor> getSupportedPropertyDescriptors() {
+               return properties;
+       }
+
+       @Override
+       public Set<Relationship> 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();
+       }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/d7cf25be/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/example/FullyDocumentedReportingTask.java
----------------------------------------------------------------------
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 0000000..275905b
--- /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<PropertyDescriptor> getSupportedPropertyDescriptors() 
{
+               final List<PropertyDescriptor> descriptors = new ArrayList<>();
+               descriptors.add(SHOW_DELTAS);
+               return descriptors;
+       }
+
+       @Override
+       public void onTrigger(ReportingContext context) {
+               // TODO Auto-generated method stub
+
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/d7cf25be/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/html/HtmlDocumentationWriterTest.java
----------------------------------------------------------------------
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 0000000..f685f39
--- /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,56 @@
+/*
+ * 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 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;
+
+public class HtmlDocumentationWriterTest {
+
+       @Test
+       public void testDocumentControllerService() throws 
InitializationException, IOException {
+
+               ControllerService controllerService = new 
FullyDocumentedControllerService();
+               controllerService.initialize(new 
MockControllerServiceInitializationContext());
+
+               DocumentationWriter writer = new HtmlDocumentationWriter();
+
+               writer.write(controllerService, System.out, false);
+
+       }
+
+       @Test
+       public void testDocumentReportingTask() throws InitializationException, 
IOException {
+
+               ReportingTask reportingTask = new 
FullyDocumentedReportingTask();
+               reportingTask.initialize(new 
MockReportingInitializationContext());
+
+               DocumentationWriter writer = new HtmlDocumentationWriter();
+
+               writer.write(reportingTask, System.out, false);
+       }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/d7cf25be/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/html/ProcessorDocumentationWriterTest.java
----------------------------------------------------------------------
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 0000000..faf66b5
--- /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,69 @@
+/*
+ * 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.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());
+
+               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());
+
+               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...");
+       }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/d7cf25be/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/src/test/java/org/apache/nifi/documentation/html/XmlValidator.java
----------------------------------------------------------------------
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 0000000..6cb7499
--- /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));
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/d7cf25be/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-runtime/pom.xml
----------------------------------------------------------------------
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 b75c35e..78f80df 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
@@ -32,6 +32,11 @@
             <artifactId>nifi-properties</artifactId>
             <scope>compile</scope>
         </dependency>
+               <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-documentation</artifactId>
+            <scope>compile</scope>
+        </dependency>
         <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>jul-to-slf4j</artifactId>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/d7cf25be/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-runtime/src/main/java/org/apache/nifi/NiFi.java
----------------------------------------------------------------------
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 ed7c329..e166f8e 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);

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/d7cf25be/nifi/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/pom.xml
----------------------------------------------------------------------
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 c386128..b5a1d54 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
@@ -40,6 +40,7 @@
         <module>nifi-administration</module>
         <module>nifi-web</module>
         <module>nifi-resources</module>
+               <module>nifi-documentation</module>
     </modules>
     <dependencies>
         <dependency>

http://git-wip-us.apache.org/repos/asf/incubator-nifi/blob/d7cf25be/nifi/nifi-nar-bundles/nifi-framework-bundle/pom.xml
----------------------------------------------------------------------
diff --git a/nifi/nifi-nar-bundles/nifi-framework-bundle/pom.xml 
b/nifi/nifi-nar-bundles/nifi-framework-bundle/pom.xml
index 413bfca..fcb1243 100644
--- a/nifi/nifi-nar-bundles/nifi-framework-bundle/pom.xml
+++ b/nifi/nifi-nar-bundles/nifi-framework-bundle/pom.xml
@@ -109,6 +109,11 @@
                 <artifactId>nifi-web-security</artifactId>
                 <version>0.0.2-incubating-SNAPSHOT</version>
             </dependency>
+            <dependency>
+                <groupId>org.apache.nifi</groupId>
+                <artifactId>nifi-documentation</artifactId>
+                <version>0.0.2-incubating-SNAPSHOT</version>
+            </dependency>
         </dependencies>
     </dependencyManagement>
 </project>

Reply via email to