nifi-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From danbr...@apache.org
Subject [5/5] nifi git commit: NIFI-725 - adding nifi-documentation-plugin
Date Wed, 19 Aug 2015 01:46:47 GMT
NIFI-725 - adding nifi-documentation-plugin


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

Branch: refs/heads/NIFI-725-master
Commit: af87b52b8657582d6e0dec162d760a85d35eccfa
Parents: 1d19a34
Author: danbress <dbress@onyxconsults.com>
Authored: Tue Aug 18 21:46:18 2015 -0400
Committer: danbress <dbress@onyxconsults.com>
Committed: Tue Aug 18 21:46:18 2015 -0400

----------------------------------------------------------------------
 nifi-documentation-plugin/pom.xml               | 113 ++++
 nifi-documentation-plugin/src/it/settings.xml   |  55 ++
 .../src/it/simple-it/pom.xml                    |  34 +
 .../src/it/simple-it/verify.groovy              |   3 +
 .../main/java/org/apache/nifi/DocumentMojo.java | 177 ++++++
 .../org/apache/nifi/ExtractDocumentation.java   | 111 ++++
 .../org/apache/nifi/NoCloseInputStream.java     |  32 +
 .../ConfigurableComponentInitializer.java       |  45 ++
 .../nifi/documentation/DocumentationWriter.java |  33 +
 .../html/HtmlDocumentationWriter.java           | 615 +++++++++++++++++++
 .../html/HtmlProcessorDocumentationWriter.java  | 256 ++++++++
 .../init/ControllerServiceInitializer.java      |  53 ++
 .../init/ProcessorInitializer.java              |  53 ++
 .../init/ReportingTaskingInitializer.java       |  52 ++
 .../mock/MockConfigurationContext.java          |  48 ++
 ...kControllerServiceInitializationContext.java |  46 ++
 .../mock/MockControllerServiceLookup.java       |  65 ++
 .../documentation/mock/MockProcessContext.java  |  85 +++
 .../MockProcessorInitializationContext.java     |  45 ++
 .../documentation/mock/MockProcessorLogger.java | 169 +++++
 .../MockReportingInitializationContext.java     |  67 ++
 .../documentation/util/ReflectionUtils.java     | 139 +++++
 22 files changed, 2296 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/af87b52b/nifi-documentation-plugin/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-documentation-plugin/pom.xml b/nifi-documentation-plugin/pom.xml
new file mode 100644
index 0000000..cc5e160
--- /dev/null
+++ b/nifi-documentation-plugin/pom.xml
@@ -0,0 +1,113 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	 <!--
+      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</groupId>
+		<artifactId>apache</artifactId>
+		<version>17</version>
+		<relativePath />
+	</parent>
+	<groupId>org.apache.nifi</groupId>
+	<artifactId>nifi-documentation-plugin</artifactId>
+	<version>1.0.0-SNAPSHOT</version>
+	<packaging>maven-plugin</packaging>
+
+	<name>nifi-documentation-plugin Maven Plugin</name>
+
+	<!-- FIXME change it to the project's website -->
+	<url>http://maven.apache.org</url>
+
+	<properties>
+		<maven.compiler.source>1.7</maven.compiler.source>
+		<maven.compiler.target>1.7</maven.compiler.target>
+		<maven.min-version>3.0.5</maven.min-version>
+		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+		<inceptionYear>2014</inceptionYear>
+	</properties>
+
+	<dependencies>
+		<dependency>
+			<groupId>org.apache.maven</groupId>
+			<artifactId>maven-plugin-api</artifactId>
+			<version>2.2.1</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.maven</groupId>
+			<artifactId>maven-project</artifactId>
+			<version>2.2.1</version>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.maven.plugin-tools</groupId>
+			<artifactId>maven-plugin-annotations</artifactId>
+			<version>3.3</version>
+			<scope>provided</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.codehaus.plexus</groupId>
+			<artifactId>plexus-utils</artifactId>
+			<version>3.0.8</version>
+		</dependency>
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<version>4.8.2</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.nifi</groupId>
+			<artifactId>nifi-api</artifactId>
+			<version>0.2.1</version>
+			<scope>provided</scope>
+		</dependency>
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>slf4j-api</artifactId>
+			<version>1.7.12</version>
+			<scope>provided</scope>
+		</dependency>
+		<dependency>
+			<groupId>com.google.guava</groupId>
+			<artifactId>guava</artifactId>
+			<version>17.0</version>
+		</dependency>
+	</dependencies>
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-plugin-plugin</artifactId>
+				<executions>
+					<execution>
+						<id>default-descriptor</id>
+						<goals>
+							<goal>descriptor</goal>
+						</goals>
+						<phase>process-classes</phase>
+					</execution>
+					<execution>
+						<id>help-descriptor</id>
+						<goals>
+							<goal>helpmojo</goal>
+						</goals>
+						<phase>process-classes</phase>
+					</execution>
+				</executions>
+			</plugin>
+		</plugins>
+	</build>
+</project>

http://git-wip-us.apache.org/repos/asf/nifi/blob/af87b52b/nifi-documentation-plugin/src/it/settings.xml
----------------------------------------------------------------------
diff --git a/nifi-documentation-plugin/src/it/settings.xml b/nifi-documentation-plugin/src/it/settings.xml
new file mode 100644
index 0000000..dc02120
--- /dev/null
+++ b/nifi-documentation-plugin/src/it/settings.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+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.
+-->
+
+<settings>
+  <profiles>
+    <profile>
+      <id>it-repo</id>
+      <activation>
+        <activeByDefault>true</activeByDefault>
+      </activation>
+      <repositories>
+        <repository>
+          <id>local.central</id>
+          <url>@localRepositoryUrl@</url>
+          <releases>
+            <enabled>true</enabled>
+          </releases>
+          <snapshots>
+            <enabled>true</enabled>
+          </snapshots>
+        </repository>
+      </repositories>
+      <pluginRepositories>
+        <pluginRepository>
+          <id>local.central</id>
+          <url>@localRepositoryUrl@</url>
+          <releases>
+            <enabled>true</enabled>
+          </releases>
+          <snapshots>
+            <enabled>true</enabled>
+          </snapshots>
+        </pluginRepository>
+      </pluginRepositories>
+    </profile>
+  </profiles>
+</settings>

http://git-wip-us.apache.org/repos/asf/nifi/blob/af87b52b/nifi-documentation-plugin/src/it/simple-it/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-documentation-plugin/src/it/simple-it/pom.xml b/nifi-documentation-plugin/src/it/simple-it/pom.xml
new file mode 100644
index 0000000..c8b4d7a
--- /dev/null
+++ b/nifi-documentation-plugin/src/it/simple-it/pom.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>org.apache.nifi.it</groupId>
+  <artifactId>simple-it</artifactId>
+  <version>1.0-SNAPSHOT</version>
+
+  <description>A simple IT verifying the basic use case.</description>
+
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+  </properties>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>@project.groupId@</groupId>
+        <artifactId>@project.artifactId@</artifactId>
+        <version>@project.version@</version>
+        <executions>
+          <execution>
+            <id>touch</id>
+            <phase>validate</phase>
+            <goals>
+              <goal>touch</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/nifi/blob/af87b52b/nifi-documentation-plugin/src/it/simple-it/verify.groovy
----------------------------------------------------------------------
diff --git a/nifi-documentation-plugin/src/it/simple-it/verify.groovy b/nifi-documentation-plugin/src/it/simple-it/verify.groovy
new file mode 100644
index 0000000..ff79813
--- /dev/null
+++ b/nifi-documentation-plugin/src/it/simple-it/verify.groovy
@@ -0,0 +1,3 @@
+File touchFile = new File( basedir, "target/touch.txt" );
+
+assert touchFile.isFile()

http://git-wip-us.apache.org/repos/asf/nifi/blob/af87b52b/nifi-documentation-plugin/src/main/java/org/apache/nifi/DocumentMojo.java
----------------------------------------------------------------------
diff --git a/nifi-documentation-plugin/src/main/java/org/apache/nifi/DocumentMojo.java b/nifi-documentation-plugin/src/main/java/org/apache/nifi/DocumentMojo.java
new file mode 100644
index 0000000..bd97149
--- /dev/null
+++ b/nifi-documentation-plugin/src/main/java/org/apache/nifi/DocumentMojo.java
@@ -0,0 +1,177 @@
+/*
+ * 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;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ServiceConfigurationError;
+import java.util.ServiceLoader;
+
+import org.apache.maven.artifact.DependencyResolutionRequiredException;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugins.annotations.LifecyclePhase;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.plugins.annotations.ResolutionScope;
+import org.apache.maven.project.MavenProject;
+import org.apache.nifi.components.ConfigurableComponent;
+import org.apache.nifi.controller.ControllerService;
+import org.apache.nifi.documentation.ConfigurableComponentInitializer;
+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.processor.Processor;
+import org.apache.nifi.reporting.InitializationException;
+import org.apache.nifi.reporting.ReportingTask;
+
+@Mojo(name = "document", defaultPhase = LifecyclePhase.PROCESS_CLASSES, threadSafe = false, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME, requiresProject = true)
+public class DocumentMojo extends AbstractMojo {
+
+    /**
+     * Location of the file.
+     */
+    @Parameter(defaultValue = "${project.build.outputDirectory}", property = "outputDir", required = true)
+    private File outputDirectory;
+
+    @Parameter(defaultValue = "${project}", required = true)
+    private MavenProject mavenProject;
+
+    public void execute() throws MojoExecutionException {
+        final File docsDir = new File(outputDirectory, "docs");
+        if (!docsDir.exists()) {
+            docsDir.mkdirs();
+        }
+
+        try {
+            final List<?> compileClassPathElements = mavenProject.getCompileClasspathElements();
+            // final List<?> compileClassPathElements = mavenProject.getRuntimeClasspathElements();
+            final URL[] urls = new URL[compileClassPathElements.size()];
+
+            for (int i = 0; i < compileClassPathElements.size(); i++) {
+                final String path = compileClassPathElements.get(i).toString();
+                final File filePath = new File(path);
+                final URL url = filePath.toURI().toURL();
+                urls[i] = url;
+                getLog().info("Adding url: " + url);
+            }
+
+            final ClassLoader myClassLoader = Thread.currentThread().getContextClassLoader();
+            final ClassLoader projectClassLoader = new URLClassLoader(urls, myClassLoader);
+
+            final List<ConfigurableComponent> components = new ArrayList<>();
+            loadServices(projectClassLoader, components, Processor.class);
+            loadServices(projectClassLoader, components, ControllerService.class);
+            loadServices(projectClassLoader, components, ReportingTask.class);
+
+            for (ConfigurableComponent component : components) {
+                initializeComponent(component);
+                generateDocumentation(component, projectClassLoader, docsDir);
+                tearDownComponent(component);
+            }
+
+        } catch (DependencyResolutionRequiredException e) {
+            throw new MojoExecutionException("Unable to resolve classpath", e);
+        } catch (MalformedURLException e) {
+            throw new MojoExecutionException("Unable to resolve classpath", e);
+        } catch (InitializationException e) {
+            throw new MojoExecutionException("Unable to initializer component", e);
+        } catch (FileNotFoundException e) {
+            throw new MojoExecutionException("Unable to generate documentation", e);
+        } catch (IOException e) {
+            throw new MojoExecutionException("Unable to generate documentation", e);
+        } catch (ServiceConfigurationError e) {
+            throw new MojoExecutionException("Unable to generate documentation", e);
+        }
+    }
+
+    private static <T extends ConfigurableComponent> void loadServices(final ClassLoader projectClassLoader, final List<ConfigurableComponent> components, final Class<T> service) {
+        final ServiceLoader<T> nifiServiceLoader = ServiceLoader.load(service, projectClassLoader);
+
+        for (T processor : nifiServiceLoader) {
+            components.add(processor);
+        }
+    }
+
+    private void tearDownComponent(ConfigurableComponent component) {
+        final ConfigurableComponentInitializer initializer;
+        if (component instanceof Processor) {
+            initializer = new ProcessorInitializer();
+        } else if (component instanceof ReportingTask) {
+            initializer = new ReportingTaskingInitializer();
+        } else if (component instanceof ControllerService) {
+            initializer = new ControllerServiceInitializer();
+        } else {
+            throw new NullPointerException("Unknown type: " + component.getClass());
+        }
+
+        initializer.teardown(component);
+
+    }
+
+    private void generateDocumentation(ConfigurableComponent component, ClassLoader classLoader, File docsDirectory) throws FileNotFoundException, IOException {
+        final HtmlDocumentationWriter writer;
+        if (component instanceof Processor) {
+            writer = new HtmlProcessorDocumentationWriter();
+        } else if (component instanceof ReportingTask) {
+            writer = new HtmlDocumentationWriter();
+        } else if (component instanceof ControllerService) {
+            writer = new HtmlDocumentationWriter();
+        } else {
+            throw new NullPointerException("Unknown type: " + component.getClass());
+        }
+
+        final String className = component.getClass().getName();
+
+        boolean hasAdditionalDetails = classLoader.getResource("docs" + File.separator + className + File.separator + "additionalDetails.html") != null;
+        final File componentDocumentationDir = new File(docsDirectory, className);
+        if (!componentDocumentationDir.exists()) {
+            componentDocumentationDir.mkdirs();
+        }
+
+        final File generatedFile = new File(componentDocumentationDir, "index.html");
+
+        try (FileOutputStream fos = new FileOutputStream(generatedFile); BufferedOutputStream bos = new BufferedOutputStream(fos)) {
+            writer.write(component, bos, hasAdditionalDetails);
+        }
+    }
+
+    private void initializeComponent(ConfigurableComponent component) throws InitializationException {
+        final ConfigurableComponentInitializer initializer;
+        if (component instanceof Processor) {
+            initializer = new ProcessorInitializer();
+        } else if (component instanceof ReportingTask) {
+            initializer = new ReportingTaskingInitializer();
+        } else if (component instanceof ControllerService) {
+            initializer = new ControllerServiceInitializer();
+        } else {
+            throw new NullPointerException("Unknown type: " + component.getClass());
+        }
+
+        initializer.initialize(component);
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/af87b52b/nifi-documentation-plugin/src/main/java/org/apache/nifi/ExtractDocumentation.java
----------------------------------------------------------------------
diff --git a/nifi-documentation-plugin/src/main/java/org/apache/nifi/ExtractDocumentation.java b/nifi-documentation-plugin/src/main/java/org/apache/nifi/ExtractDocumentation.java
new file mode 100644
index 0000000..957b757
--- /dev/null
+++ b/nifi-documentation-plugin/src/main/java/org/apache/nifi/ExtractDocumentation.java
@@ -0,0 +1,111 @@
+/*
+ * 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;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.model.Dependency;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugins.annotations.LifecyclePhase;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.plugins.annotations.ResolutionScope;
+import org.apache.maven.project.MavenProject;
+
+import com.google.common.io.ByteStreams;
+
+@Mojo(name = "extract", defaultPhase = LifecyclePhase.GENERATE_RESOURCES, threadSafe = false, requiresProject = false, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME)
+public class ExtractDocumentation extends AbstractMojo {
+    /**
+     * Location of the file.
+     */
+    @Parameter(defaultValue = "${project.build.directory}", property = "outputDir", required = true)
+    private File outputDirectory;
+
+    @Parameter(defaultValue = "${project}", required = true)
+    private MavenProject mavenProject;
+
+    @Override
+    public void execute() throws MojoExecutionException, MojoFailureException {
+
+        @SuppressWarnings("unchecked")
+        final List<Dependency> runtimeDependencies = mavenProject.getDependencies();
+
+        for (Dependency dependency : runtimeDependencies) {
+            getLog().debug("Looking at: " + dependency);
+            // TODO dedup
+            if (dependency.getType().equals("nar")) {
+
+                final Artifact artifact = (Artifact) mavenProject.getArtifactMap().get(dependency.getGroupId() + ":" + dependency.getArtifactId());
+                final File narFile = artifact.getFile();
+                try (FileInputStream fos = new FileInputStream(narFile); BufferedInputStream bis = new BufferedInputStream(fos); JarInputStream jis = new JarInputStream(bis)) {
+
+                    JarEntry jarEntry = null;
+
+                    while ((jarEntry = jis.getNextJarEntry()) != null) {
+                        if (jarEntry.getName().endsWith(".jar")) {
+                            getLog().debug("Found a jar inside of a nar " + jarEntry + " -> " + narFile);
+                            processJar(new NoCloseInputStream(jis));
+                        }
+                    }
+
+                } catch (IOException e) {
+                    throw new MojoExecutionException("Unable to process: " + narFile, e);
+                }
+
+            }
+        }
+
+    }
+
+    private void processJar(InputStream inputStream) throws IOException {
+        try (JarInputStream jis = new JarInputStream(inputStream)) {
+            JarEntry jarEntry = null;
+
+            while ((jarEntry = jis.getNextJarEntry()) != null) {
+                
+                if (!jarEntry.isDirectory()) {
+                    final String jarName = jarEntry.getName();
+                    if (jarName.startsWith("docs")) {
+                        getLog().debug("Looking inside a jar at: " + jarEntry);
+                        File outputFile = new File(outputDirectory, jarName);
+                        File parentFile = outputFile.getParentFile();
+                        if (!parentFile.exists()) {
+                            parentFile.mkdirs();
+                        }
+
+                        try (FileOutputStream fos = new FileOutputStream(outputFile)) {
+                            ByteStreams.copy(jis, fos);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/af87b52b/nifi-documentation-plugin/src/main/java/org/apache/nifi/NoCloseInputStream.java
----------------------------------------------------------------------
diff --git a/nifi-documentation-plugin/src/main/java/org/apache/nifi/NoCloseInputStream.java b/nifi-documentation-plugin/src/main/java/org/apache/nifi/NoCloseInputStream.java
new file mode 100644
index 0000000..80b259e
--- /dev/null
+++ b/nifi-documentation-plugin/src/main/java/org/apache/nifi/NoCloseInputStream.java
@@ -0,0 +1,32 @@
+/*
+ * 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;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class NoCloseInputStream extends FilterInputStream {
+
+    protected NoCloseInputStream(InputStream arg0) {
+        super(arg0);
+    }
+
+    @Override
+    public void close() throws IOException {
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/af87b52b/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/ConfigurableComponentInitializer.java
----------------------------------------------------------------------
diff --git a/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/ConfigurableComponentInitializer.java b/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/ConfigurableComponentInitializer.java
new file mode 100644
index 0000000..ad21f21
--- /dev/null
+++ b/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/ConfigurableComponentInitializer.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;
+
+import org.apache.nifi.components.ConfigurableComponent;
+import org.apache.nifi.reporting.InitializationException;
+
+/**
+ * An interface for initializing and tearing down 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;
+
+    /**
+     * Calls the lifecycle methods that should be called when a flow is shutdown.
+     *
+     * @param component the component to initialize
+     */
+    void teardown(ConfigurableComponent component);
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/af87b52b/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/DocumentationWriter.java
----------------------------------------------------------------------
diff --git a/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/DocumentationWriter.java b/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/DocumentationWriter.java
new file mode 100644
index 0000000..d178636
--- /dev/null
+++ b/nifi-documentation-plugin/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/nifi/blob/af87b52b/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/html/HtmlDocumentationWriter.java
----------------------------------------------------------------------
diff --git a/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/html/HtmlDocumentationWriter.java b/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/html/HtmlDocumentationWriter.java
new file mode 100644
index 0000000..394897e
--- /dev/null
+++ b/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/html/HtmlDocumentationWriter.java
@@ -0,0 +1,615 @@
+/*
+ * 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.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import javax.xml.stream.FactoryConfigurationError;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+
+import org.apache.nifi.annotation.behavior.DynamicProperties;
+import org.apache.nifi.annotation.behavior.DynamicProperty;
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.SeeAlso;
+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;
+
+import edu.emory.mathcs.backport.java.util.Collections;
+
+/**
+ * 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("<!DOCTYPE html>");
+            xmlStreamWriter.writeStartElement("html");
+            xmlStreamWriter.writeAttribute("lang", "en");
+            writeHead(configurableComponent, xmlStreamWriter);
+            writeBody(configurableComponent, xmlStreamWriter, includesAdditionalDocumentation);
+            xmlStreamWriter.writeEndElement();
+            xmlStreamWriter.close();
+        } catch (XMLStreamException | FactoryConfigurationError e) {
+            throw new IOException("Unable to create XMLOutputStream", e);
+        }
+    }
+
+    /**
+     * Writes the head portion of the HTML documentation.
+     *
+     * @param configurableComponent the component to describe
+     * @param xmlStreamWriter the stream to write to
+     * @throws XMLStreamException thrown if there was a problem writing to the
+     * stream
+     */
+    protected void writeHead(final ConfigurableComponent configurableComponent,
+            final XMLStreamWriter xmlStreamWriter) throws XMLStreamException {
+        xmlStreamWriter.writeStartElement("head");
+        xmlStreamWriter.writeStartElement("meta");
+        xmlStreamWriter.writeAttribute("charset", "utf-8");
+        xmlStreamWriter.writeEndElement();
+        writeSimpleElement(xmlStreamWriter, "title", getTitle(configurableComponent));
+
+        xmlStreamWriter.writeStartElement("link");
+        xmlStreamWriter.writeAttribute("rel", "stylesheet");
+        xmlStreamWriter.writeAttribute("href", "../../css/component-usage.css");
+        xmlStreamWriter.writeAttribute("type", "text/css");
+        xmlStreamWriter.writeEndElement();
+
+        xmlStreamWriter.writeEndElement();
+    }
+
+    /**
+     * Gets the class name of the component.
+     *
+     * @param configurableComponent the component to describe
+     * @return the class name of the component
+     */
+    protected String getTitle(final ConfigurableComponent configurableComponent) {
+        return configurableComponent.getClass().getSimpleName();
+    }
+
+    /**
+     * Writes the body section of the documentation, this consists of the
+     * component description, the tags, and the PropertyDescriptors.
+     *
+     * @param configurableComponent the component to describe
+     * @param xmlStreamWriter the stream writer
+     * @param hasAdditionalDetails whether there are additional details present
+     * or not
+     * @throws XMLStreamException thrown if there was a problem writing to the
+     * XML stream
+     */
+    private void writeBody(final ConfigurableComponent configurableComponent,
+            final XMLStreamWriter xmlStreamWriter, final boolean hasAdditionalDetails)
+            throws XMLStreamException {
+        xmlStreamWriter.writeStartElement("body");
+        writeDescription(configurableComponent, xmlStreamWriter, hasAdditionalDetails);
+        writeTags(configurableComponent, xmlStreamWriter);
+        writeProperties(configurableComponent, xmlStreamWriter);
+        writeDynamicProperties(configurableComponent, xmlStreamWriter);
+        writeAdditionalBodyInfo(configurableComponent, xmlStreamWriter);
+        writeSeeAlso(configurableComponent, xmlStreamWriter);
+        xmlStreamWriter.writeEndElement();
+    }
+
+    /**
+     * Writes the list of components that may be linked from this component.
+     *
+     * @param configurableComponent the component to describe
+     * @param xmlStreamWriter the stream writer to use
+     * @throws XMLStreamException thrown if there was a problem writing the XML
+     */
+    private void writeSeeAlso(ConfigurableComponent configurableComponent, XMLStreamWriter xmlStreamWriter)
+            throws XMLStreamException {
+        final SeeAlso seeAlso = configurableComponent.getClass().getAnnotation(SeeAlso.class);
+        if (seeAlso != null) {
+            writeSimpleElement(xmlStreamWriter, "h3", "See Also:");
+            xmlStreamWriter.writeStartElement("p");
+            int index = 0;
+            for (final Class<? extends ConfigurableComponent> linkedComponent : seeAlso.value()) {
+                if (index != 0) {
+                    xmlStreamWriter.writeCharacters(", ");
+                }
+
+                writeLinkForComponent(xmlStreamWriter, linkedComponent);
+
+                ++index;
+            }
+
+            for (final String linkedComponent : seeAlso.classNames()) {
+                if (index != 0) {
+                    xmlStreamWriter.writeCharacters(", ");
+                }
+
+                final String link = "../" + linkedComponent + "/index.html";
+
+                final int indexOfLastPeriod = linkedComponent.lastIndexOf(".") + 1;
+
+                writeLink(xmlStreamWriter, linkedComponent.substring(indexOfLastPeriod), link);
+
+                ++index;
+            }
+            xmlStreamWriter.writeEndElement();
+        }
+    }
+
+    /**
+     * This method may be overridden by sub classes to write additional
+     * information to the body of the documentation.
+     *
+     * @param configurableComponent the component to describe
+     * @param xmlStreamWriter the stream writer
+     * @throws XMLStreamException thrown if there was a problem writing to the
+     * XML stream
+     */
+    protected void writeAdditionalBodyInfo(final ConfigurableComponent configurableComponent,
+            final XMLStreamWriter xmlStreamWriter) throws XMLStreamException {
+
+    }
+
+    private void writeTags(final ConfigurableComponent configurableComponent,
+            final XMLStreamWriter xmlStreamWriter) throws XMLStreamException {
+        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 = join(tags.value(), ", ");
+            xmlStreamWriter.writeCharacters(tagString);
+        } else {
+            xmlStreamWriter.writeCharacters("None.");
+        }
+        xmlStreamWriter.writeEndElement();
+    }
+
+    static String join(final String[] toJoin, final String delimiter) {
+        final StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < toJoin.length; i++) {
+            sb.append(toJoin[i]);
+            if (i < toJoin.length - 1) {
+                sb.append(delimiter);
+            }
+        }
+        return sb.toString();
+    }
+
+    /**
+     * 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 {
+
+        final List<PropertyDescriptor> properties = configurableComponent.getPropertyDescriptors();
+        writeSimpleElement(xmlStreamWriter, "h3", "Properties: ");
+
+        if (properties.size() > 0) {
+            final boolean containsExpressionLanguage = containsExpressionLanguage(configurableComponent);
+            final boolean containsSensitiveProperties = containsSensitiveProperties(configurableComponent);
+            xmlStreamWriter.writeStartElement("p");
+            xmlStreamWriter.writeCharacters("In the list below, the names of required properties appear in ");
+            writeSimpleElement(xmlStreamWriter, "strong", "bold");
+            xmlStreamWriter.writeCharacters(". Any other properties (not in bold) are considered optional. " +
+                    "The table also indicates any default values");
+            if (containsExpressionLanguage) {
+                if (!containsSensitiveProperties) {
+                    xmlStreamWriter.writeCharacters(", and ");
+                } else {
+                    xmlStreamWriter.writeCharacters(", ");
+                }
+                xmlStreamWriter.writeCharacters("whether a property supports the ");
+                writeLink(xmlStreamWriter, "NiFi Expression Language", "../../html/expression-language-guide.html");
+            }
+            if (containsSensitiveProperties) {
+                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();
+
+            xmlStreamWriter.writeStartElement("table");
+            xmlStreamWriter.writeAttribute("id", "properties");
+
+            // write the header row
+            xmlStreamWriter.writeStartElement("tr");
+            writeSimpleElement(xmlStreamWriter, "th", "Name");
+            writeSimpleElement(xmlStreamWriter, "th", "Default Value");
+            writeSimpleElement(xmlStreamWriter, "th", "Allowable Values");
+            writeSimpleElement(xmlStreamWriter, "th", "Description");
+            xmlStreamWriter.writeEndElement();
+
+            // write the individual properties
+            for (PropertyDescriptor property : properties) {
+                xmlStreamWriter.writeStartElement("tr");
+                xmlStreamWriter.writeStartElement("td");
+                xmlStreamWriter.writeAttribute("id", "name");
+                if (property.isRequired()) {
+                    writeSimpleElement(xmlStreamWriter, "strong", property.getDisplayName());
+                } else {
+                    xmlStreamWriter.writeCharacters(property.getDisplayName());
+                }
+
+                xmlStreamWriter.writeEndElement();
+                writeSimpleElement(xmlStreamWriter, "td", property.getDefaultValue(), false, "default-value");
+                xmlStreamWriter.writeStartElement("td");
+                xmlStreamWriter.writeAttribute("id", "allowable-values");
+                writeValidValues(xmlStreamWriter, property);
+                xmlStreamWriter.writeEndElement();
+                xmlStreamWriter.writeStartElement("td");
+                xmlStreamWriter.writeAttribute("id", "description");
+                if (property.getDescription() != null && property.getDescription().trim().length() > 0) {
+                    xmlStreamWriter.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.");
+        }
+    }
+
+    /**
+     * Indicates whether or not the component contains at least one sensitive property.
+     *
+     * @param component the component to interogate
+     * @return whether or not the component contains at least one sensitive property.
+     */
+    private boolean containsSensitiveProperties(final ConfigurableComponent component) {
+        for (PropertyDescriptor descriptor : component.getPropertyDescriptors()) {
+            if (descriptor.isSensitive()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Indicates whether or not the component contains at least one property that supports Expression Language.
+     *
+     * @param component the component to interogate
+     * @return whether or not the component contains at least one sensitive property.
+     */
+    private boolean containsExpressionLanguage(final ConfigurableComponent component) {
+        for (PropertyDescriptor descriptor : component.getPropertyDescriptors()) {
+            if (descriptor.isExpressionLanguageSupported()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void writeDynamicProperties(final ConfigurableComponent configurableComponent,
+            final XMLStreamWriter xmlStreamWriter) throws XMLStreamException {
+
+        final List<DynamicProperty> dynamicProperties = getDynamicProperties(configurableComponent);
+
+        if (dynamicProperties != null && dynamicProperties.size() > 0) {
+            writeSimpleElement(xmlStreamWriter, "h3", "Dynamic Properties: ");
+            xmlStreamWriter.writeStartElement("p");
+            xmlStreamWriter
+                    .writeCharacters("Dynamic Properties allow the user to specify both the name and value of a property.");
+            xmlStreamWriter.writeStartElement("table");
+            xmlStreamWriter.writeAttribute("id", "dynamic-properties");
+            xmlStreamWriter.writeStartElement("tr");
+            writeSimpleElement(xmlStreamWriter, "th", "Name");
+            writeSimpleElement(xmlStreamWriter, "th", "Value");
+            writeSimpleElement(xmlStreamWriter, "th", "Description");
+            xmlStreamWriter.writeEndElement();
+            for (final DynamicProperty dynamicProperty : dynamicProperties) {
+                xmlStreamWriter.writeStartElement("tr");
+                writeSimpleElement(xmlStreamWriter, "td", dynamicProperty.name(), false, "name");
+                writeSimpleElement(xmlStreamWriter, "td", dynamicProperty.value(), false, "value");
+                xmlStreamWriter.writeStartElement("td");
+                xmlStreamWriter.writeCharacters(dynamicProperty.description());
+                if (dynamicProperty.supportsExpressionLanguage()) {
+                    xmlStreamWriter.writeEmptyElement("br");
+                    writeSimpleElement(xmlStreamWriter, "strong", "Supports Expression Language: true");
+                }
+                xmlStreamWriter.writeEndElement();
+                xmlStreamWriter.writeEndElement();
+            }
+
+            xmlStreamWriter.writeEndElement();
+            xmlStreamWriter.writeEndElement();
+        }
+    }
+
+    private List<DynamicProperty> getDynamicProperties(ConfigurableComponent configurableComponent) {
+        final List<DynamicProperty> dynamicProperties = new ArrayList<>();
+        final DynamicProperties dynProps = configurableComponent.getClass().getAnnotation(DynamicProperties.class);
+        if (dynProps != null) {
+            for (final DynamicProperty dynProp : dynProps.value()) {
+                dynamicProperties.add(dynProp);
+            }
+        }
+
+        final DynamicProperty dynProp = configurableComponent.getClass().getAnnotation(DynamicProperty.class);
+        if (dynProp != null) {
+            dynamicProperties.add(dynProp);
+        }
+
+        return dynamicProperties;
+    }
+
+    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 API: ");
+            xmlStreamWriter.writeEmptyElement("br");
+            xmlStreamWriter.writeCharacters(controllerServiceClass.getSimpleName());
+
+            final List<Class<? extends ControllerService>> implementations = lookupControllerServiceImpls(controllerServiceClass);
+            xmlStreamWriter.writeEmptyElement("br");
+            if (implementations.size() > 0) {
+                final String title = implementations.size() > 1 ? "Implementations: " : "Implementation:";
+                writeSimpleElement(xmlStreamWriter, "strong", title);
+                for (int i = 0; i < implementations.size(); i++) {
+                    xmlStreamWriter.writeEmptyElement("br");
+                    writeLinkForComponent(xmlStreamWriter, implementations.get(i));
+                }
+            } else {
+                xmlStreamWriter.writeCharacters("No implementations found.");
+            }
+        }
+    }
+
+    /**
+     * 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 {
+        writeSimpleElement(writer, elementName, characters, strong, null);
+    }
+
+    /**
+     * Writes a begin element, an id attribute(if specified), then text, then
+     * end element for element of the users choosing. Example: &lt;p
+     * id="p-id"&gt;text&lt;/p&gt;
+     *
+     * @param writer the stream writer to use
+     * @param elementName the name of the element
+     * @param characters the text of the element
+     * @param strong whether to bold the text of the element or not
+     * @param id the id of the element. specifying null will cause no element to
+     * be written.
+     * @throws XMLStreamException xse
+     */
+    protected final static void writeSimpleElement(final XMLStreamWriter writer, final String elementName,
+            final String characters, boolean strong, String id) throws XMLStreamException {
+        writer.writeStartElement(elementName);
+        if (id != null) {
+            writer.writeAttribute("id", id);
+        }
+        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();
+    }
+
+    /**
+     * Writes a link to another configurable component
+     *
+     * @param xmlStreamWriter the xml stream writer
+     * @param clazz the configurable component to link to
+     * @throws XMLStreamException thrown if there is a problem writing the XML
+     */
+    protected void writeLinkForComponent(final XMLStreamWriter xmlStreamWriter, final Class<?> clazz) throws XMLStreamException {
+        writeLink(xmlStreamWriter, clazz.getSimpleName(), "../" + clazz.getCanonicalName() + "/index.html");
+    }
+
+    /**
+     * Uses the {@link ExtensionManager} to discover any {@link ControllerService} implementations that implement a specific
+     * ControllerService API.
+     *
+     * @param parent the controller service API
+     * @return a list of controller services that implement the controller service API
+     */
+    private List<Class<? extends ControllerService>> lookupControllerServiceImpls(
+            final Class<? extends ControllerService> parent) {
+
+        final List<Class<? extends ControllerService>> implementations = new ArrayList<>();
+
+        // first get all ControllerService implementations
+        final Set<Class> controllerServices = Collections.emptySet();
+
+        // then iterate over all controller services looking for any that is a child of the parent
+        // ControllerService API that was passed in as a parameter
+        for (final Class<? extends ControllerService> controllerServiceClass : controllerServices) {
+            if (parent.isAssignableFrom(controllerServiceClass)) {
+                implementations.add(controllerServiceClass);
+            }
+        }
+
+        return implementations;
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/af87b52b/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/html/HtmlProcessorDocumentationWriter.java
----------------------------------------------------------------------
diff --git a/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/html/HtmlProcessorDocumentationWriter.java b/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/html/HtmlProcessorDocumentationWriter.java
new file mode 100644
index 0000000..4a15b50
--- /dev/null
+++ b/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/html/HtmlProcessorDocumentationWriter.java
@@ -0,0 +1,256 @@
+/*
+ * 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.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+
+import org.apache.nifi.annotation.behavior.DynamicRelationship;
+import org.apache.nifi.annotation.behavior.ReadsAttribute;
+import org.apache.nifi.annotation.behavior.ReadsAttributes;
+import org.apache.nifi.annotation.behavior.WritesAttribute;
+import org.apache.nifi.annotation.behavior.WritesAttributes;
+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;
+        writeRelationships(processor, xmlStreamWriter);
+        writeDynamicRelationships(processor, xmlStreamWriter);
+        writeAttributeInfo(processor, xmlStreamWriter);
+    }
+
+    /**
+     * Writes all the attributes that a processor says it reads and writes
+     *
+     * @param processor the processor to describe
+     * @param xmlStreamWriter the xml stream writer to use
+     * @throws XMLStreamException thrown if there was a problem writing the XML
+     */
+    private void writeAttributeInfo(Processor processor, XMLStreamWriter xmlStreamWriter)
+            throws XMLStreamException {
+
+        handleReadsAttributes(xmlStreamWriter, processor);
+        handleWritesAttributes(xmlStreamWriter, processor);
+    }
+
+    private String defaultIfBlank(final String test, final String defaultValue) {
+        if (test == null || test.trim().isEmpty()) {
+            return defaultValue;
+        }
+        return test;
+    }
+
+    /**
+     * Writes out just the attributes that are being read in a table form.
+     *
+     * @param xmlStreamWriter the xml stream writer to use
+     * @param processor the processor to describe
+     * @throws XMLStreamException xse
+     */
+    private void handleReadsAttributes(XMLStreamWriter xmlStreamWriter, final Processor processor)
+            throws XMLStreamException {
+        List<ReadsAttribute> attributesRead = getReadsAttributes(processor);
+
+        writeSimpleElement(xmlStreamWriter, "h3", "Reads Attributes: ");
+        if (attributesRead.size() > 0) {
+            xmlStreamWriter.writeStartElement("table");
+            xmlStreamWriter.writeAttribute("id", "reads-attributes");
+            xmlStreamWriter.writeStartElement("tr");
+            writeSimpleElement(xmlStreamWriter, "th", "Name");
+            writeSimpleElement(xmlStreamWriter, "th", "Description");
+            xmlStreamWriter.writeEndElement();
+            for (ReadsAttribute attribute : attributesRead) {
+                xmlStreamWriter.writeStartElement("tr");
+                writeSimpleElement(xmlStreamWriter, "td",
+                        defaultIfBlank(attribute.attribute(), "Not Specified"));
+                // TODO allow for HTML characters here.
+                writeSimpleElement(xmlStreamWriter, "td",
+                        defaultIfBlank(attribute.description(), "Not Specified"));
+                xmlStreamWriter.writeEndElement();
+
+            }
+            xmlStreamWriter.writeEndElement();
+
+        } else {
+            xmlStreamWriter.writeCharacters("None specified.");
+        }
+    }
+
+    /**
+     * Writes out just the attributes that are being written to in a table form.
+     *
+     * @param xmlStreamWriter the xml stream writer to use
+     * @param processor the processor to describe
+     * @throws XMLStreamException xse
+     */
+    private void handleWritesAttributes(XMLStreamWriter xmlStreamWriter, final Processor processor)
+            throws XMLStreamException {
+        List<WritesAttribute> attributesRead = getWritesAttributes(processor);
+
+        writeSimpleElement(xmlStreamWriter, "h3", "Writes Attributes: ");
+        if (attributesRead.size() > 0) {
+            xmlStreamWriter.writeStartElement("table");
+            xmlStreamWriter.writeAttribute("id", "writes-attributes");
+            xmlStreamWriter.writeStartElement("tr");
+            writeSimpleElement(xmlStreamWriter, "th", "Name");
+            writeSimpleElement(xmlStreamWriter, "th", "Description");
+            xmlStreamWriter.writeEndElement();
+            for (WritesAttribute attribute : attributesRead) {
+                xmlStreamWriter.writeStartElement("tr");
+                writeSimpleElement(xmlStreamWriter, "td",
+                        defaultIfBlank(attribute.attribute(), "Not Specified"));
+                // TODO allow for HTML characters here.
+                writeSimpleElement(xmlStreamWriter, "td",
+                        defaultIfBlank(attribute.description(), "Not Specified"));
+                xmlStreamWriter.writeEndElement();
+            }
+            xmlStreamWriter.writeEndElement();
+
+        } else {
+            xmlStreamWriter.writeCharacters("None specified.");
+        }
+    }
+
+    /**
+     * Collects the attributes that a processor is reading from.
+     *
+     * @param processor the processor to describe
+     * @return the list of attributes that processor is reading
+     */
+    private List<ReadsAttribute> getReadsAttributes(Processor processor) {
+        List<ReadsAttribute> attributes = new ArrayList<>();
+
+        ReadsAttributes readsAttributes = processor.getClass().getAnnotation(ReadsAttributes.class);
+        if (readsAttributes != null) {
+            attributes.addAll(Arrays.asList(readsAttributes.value()));
+        }
+
+        ReadsAttribute readsAttribute = processor.getClass().getAnnotation(ReadsAttribute.class);
+        if (readsAttribute != null) {
+            attributes.add(readsAttribute);
+        }
+
+        return attributes;
+    }
+
+    /**
+     * Collects the attributes that a processor is writing to.
+     *
+     * @param processor the processor to describe
+     * @return the list of attributes the processor is writing
+     */
+    private List<WritesAttribute> getWritesAttributes(Processor processor) {
+        List<WritesAttribute> attributes = new ArrayList<>();
+
+        WritesAttributes writesAttributes = processor.getClass().getAnnotation(WritesAttributes.class);
+        if (writesAttributes != null) {
+            attributes.addAll(Arrays.asList(writesAttributes.value()));
+        }
+
+        WritesAttribute writeAttribute = processor.getClass().getAnnotation(WritesAttribute.class);
+        if (writeAttribute != null) {
+            attributes.add(writeAttribute);
+        }
+
+        return attributes;
+    }
+
+    /**
+     * Writes a table describing the relations a processor has.
+     *
+     * @param processor the processor to describe
+     * @param xmlStreamWriter the stream writer to use
+     * @throws XMLStreamException thrown if there was a problem writing the xml
+     */
+    private void writeRelationships(final Processor processor, final XMLStreamWriter xmlStreamWriter)
+            throws XMLStreamException {
+
+        writeSimpleElement(xmlStreamWriter, "h3", "Relationships: ");
+
+        if (processor.getRelationships().size() > 0) {
+            xmlStreamWriter.writeStartElement("table");
+            xmlStreamWriter.writeAttribute("id", "relationships");
+            xmlStreamWriter.writeStartElement("tr");
+            writeSimpleElement(xmlStreamWriter, "th", "Name");
+            writeSimpleElement(xmlStreamWriter, "th", "Description");
+            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.");
+        }
+    }
+
+    private void writeDynamicRelationships(final Processor processor, final XMLStreamWriter xmlStreamWriter) throws XMLStreamException {
+
+        List<DynamicRelationship> dynamicRelationships = getDynamicRelationships(processor);
+
+        if (dynamicRelationships.size() > 0) {
+            writeSimpleElement(xmlStreamWriter, "h3", "Dynamic Relationships: ");
+            xmlStreamWriter.writeStartElement("p");
+            xmlStreamWriter.writeCharacters("A Dynamic Relationship may be created based on how the user configures the Processor.");
+            xmlStreamWriter.writeStartElement("table");
+            xmlStreamWriter.writeAttribute("id", "dynamic-relationships");
+            xmlStreamWriter.writeStartElement("tr");
+            writeSimpleElement(xmlStreamWriter, "th", "Name");
+            writeSimpleElement(xmlStreamWriter, "th", "Description");
+            xmlStreamWriter.writeEndElement();
+
+            for (DynamicRelationship dynamicRelationship : dynamicRelationships) {
+                xmlStreamWriter.writeStartElement("tr");
+                writeSimpleElement(xmlStreamWriter, "td", dynamicRelationship.name());
+                writeSimpleElement(xmlStreamWriter, "td", dynamicRelationship.description());
+                xmlStreamWriter.writeEndElement();
+            }
+            xmlStreamWriter.writeEndElement();
+            xmlStreamWriter.writeEndElement();
+        }
+    }
+
+    private List<DynamicRelationship> getDynamicRelationships(Processor processor) {
+        List<DynamicRelationship> results = new ArrayList<>();
+
+        DynamicRelationship dynamicRelationships = processor.getClass().getAnnotation(DynamicRelationship.class);
+        if (dynamicRelationships != null) {
+            results.add(dynamicRelationships);
+        }
+
+        return results;
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/af87b52b/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/init/ControllerServiceInitializer.java
----------------------------------------------------------------------
diff --git a/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/init/ControllerServiceInitializer.java b/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/init/ControllerServiceInitializer.java
new file mode 100644
index 0000000..dc9f82f
--- /dev/null
+++ b/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/init/ControllerServiceInitializer.java
@@ -0,0 +1,53 @@
+/*
+ * 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.annotation.lifecycle.OnShutdown;
+import org.apache.nifi.components.ConfigurableComponent;
+import org.apache.nifi.controller.ControllerService;
+import org.apache.nifi.documentation.ConfigurableComponentInitializer;
+import org.apache.nifi.documentation.mock.MockConfigurationContext;
+import org.apache.nifi.documentation.mock.MockControllerServiceInitializationContext;
+import org.apache.nifi.documentation.mock.MockProcessorLogger;
+import org.apache.nifi.documentation.util.ReflectionUtils;
+import org.apache.nifi.logging.ProcessorLog;
+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());
+
+    }
+
+    @Override
+    public void teardown(ConfigurableComponent component) {
+
+        final ProcessorLog logger = new MockProcessorLogger();
+        final MockConfigurationContext context = new MockConfigurationContext();
+        ReflectionUtils.quietlyInvokeMethodsWithAnnotations(OnShutdown.class, org.apache.nifi.processor.annotation.OnShutdown.class, component, logger, context);
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/af87b52b/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/init/ProcessorInitializer.java
----------------------------------------------------------------------
diff --git a/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/init/ProcessorInitializer.java b/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/init/ProcessorInitializer.java
new file mode 100644
index 0000000..f4ca36d
--- /dev/null
+++ b/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/init/ProcessorInitializer.java
@@ -0,0 +1,53 @@
+/*
+ * 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.annotation.lifecycle.OnShutdown;
+import org.apache.nifi.components.ConfigurableComponent;
+import org.apache.nifi.documentation.ConfigurableComponentInitializer;
+import org.apache.nifi.documentation.mock.MockProcessContext;
+import org.apache.nifi.documentation.mock.MockProcessorInitializationContext;
+import org.apache.nifi.documentation.mock.MockProcessorLogger;
+import org.apache.nifi.documentation.util.ReflectionUtils;
+import org.apache.nifi.logging.ProcessorLog;
+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());
+
+    }
+
+    @Override
+    public void teardown(ConfigurableComponent component) {
+        Processor processor = (Processor) component;
+
+        final ProcessorLog logger = new MockProcessorLogger();
+        final MockProcessContext context = new MockProcessContext();
+        ReflectionUtils.quietlyInvokeMethodsWithAnnotations(OnShutdown.class, org.apache.nifi.processor.annotation.OnShutdown.class, processor, logger, context);
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/af87b52b/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/init/ReportingTaskingInitializer.java
----------------------------------------------------------------------
diff --git a/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/init/ReportingTaskingInitializer.java b/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/init/ReportingTaskingInitializer.java
new file mode 100644
index 0000000..0a43f0b
--- /dev/null
+++ b/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/init/ReportingTaskingInitializer.java
@@ -0,0 +1,52 @@
+/*
+ * 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.annotation.lifecycle.OnShutdown;
+import org.apache.nifi.components.ConfigurableComponent;
+import org.apache.nifi.documentation.ConfigurableComponentInitializer;
+import org.apache.nifi.documentation.mock.MockConfigurationContext;
+import org.apache.nifi.documentation.mock.MockProcessorLogger;
+import org.apache.nifi.documentation.mock.MockReportingInitializationContext;
+import org.apache.nifi.documentation.util.ReflectionUtils;
+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());
+
+    }
+
+    @Override
+    public void teardown(ConfigurableComponent component) {
+        ReportingTask reportingTask = (ReportingTask) component;
+
+        final MockConfigurationContext context = new MockConfigurationContext();
+        ReflectionUtils.quietlyInvokeMethodsWithAnnotations(OnShutdown.class, org.apache.nifi.processor.annotation.OnShutdown.class, reportingTask, new MockProcessorLogger(), context);
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/af87b52b/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/mock/MockConfigurationContext.java
----------------------------------------------------------------------
diff --git a/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/mock/MockConfigurationContext.java b/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/mock/MockConfigurationContext.java
new file mode 100644
index 0000000..6c9ec9d
--- /dev/null
+++ b/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/mock/MockConfigurationContext.java
@@ -0,0 +1,48 @@
+/*
+ * 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.Map;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.components.PropertyValue;
+import org.apache.nifi.controller.ConfigurationContext;
+
+public class MockConfigurationContext implements ConfigurationContext {
+
+    @Override
+    public PropertyValue getProperty(PropertyDescriptor property) {
+        return null;
+    }
+
+    @Override
+    public Map<PropertyDescriptor, String> getProperties() {
+        return Collections.emptyMap();
+    }
+
+    @Override
+    public String getSchedulingPeriod() {
+        return "0 secs";
+    }
+
+    @Override
+    public Long getSchedulingPeriod(final TimeUnit timeUnit) {
+        return 0L;
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/af87b52b/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/mock/MockControllerServiceInitializationContext.java
----------------------------------------------------------------------
diff --git a/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/mock/MockControllerServiceInitializationContext.java b/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/mock/MockControllerServiceInitializationContext.java
new file mode 100644
index 0000000..14076a3
--- /dev/null
+++ b/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/mock/MockControllerServiceInitializationContext.java
@@ -0,0 +1,46 @@
+/*
+ * 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;
+import org.apache.nifi.logging.ComponentLog;
+
+/**
+ * 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 "mock-controller-service";
+    }
+
+    @Override
+    public ControllerServiceLookup getControllerServiceLookup() {
+        return new MockControllerServiceLookup();
+    }
+
+    @Override
+    public ComponentLog getLogger() {
+        return new MockProcessorLogger();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/af87b52b/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/mock/MockControllerServiceLookup.java
----------------------------------------------------------------------
diff --git a/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/mock/MockControllerServiceLookup.java b/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/mock/MockControllerServiceLookup.java
new file mode 100644
index 0000000..5c60881
--- /dev/null
+++ b/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/mock/MockControllerServiceLookup.java
@@ -0,0 +1,65 @@
+/*
+ * 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();
+    }
+
+    @Override
+    public boolean isControllerServiceEnabling(String serviceIdentifier) {
+        return false;
+    }
+
+    @Override
+    public String getControllerServiceName(String serviceIdentifier) {
+        return serviceIdentifier;
+    }
+
+}


Mime
View raw message