From cfa37fbf55531303730a350b64f1d74d3e87252b Mon Sep 17 00:00:00 2001 From: danbress Date: Mon, 17 Aug 2015 21:23:07 -0400 Subject: [PATCH 1/4] NIFI-725 building site assembly --- nifi-assembly/pom.xml | 57 +++++++++++++++++++ .../src/main/assembly/dependencies.xml | 1 + nifi-assembly/src/main/assembly/site.xml | 57 +++++++++++++++++++ 3 files changed, 115 insertions(+) create mode 100644 nifi-assembly/src/main/assembly/site.xml diff --git a/nifi-assembly/pom.xml b/nifi-assembly/pom.xml index 1f1522787dc0..b89548395462 100644 --- a/nifi-assembly/pom.xml +++ b/nifi-assembly/pom.xml @@ -46,8 +46,58 @@ language governing permissions and limitations under the License. --> posix + + make site + + single + + package + + + 0775 + 0775 + 0664 + + + src/main/assembly/site.xml + + posix + + + + org.apache.maven.plugins + maven-dependency-plugin + + + unpack + generate-resources + + unpack-dependencies + + + nifi-web-api + **/*.* + ${project.build.directory}/nifi-web-api + + + + + + org.apache.nifi + nifi-documentation-plugin + 1.0.0-SNAPSHOT + + + extract + + extract + + process-classes + + + @@ -217,6 +267,13 @@ language governing permissions and limitations under the License. --> nifi-ambari-nar nar + + org.apache.nifi + nifi-web-api + 0.3.0-SNAPSHOT + war + compile + diff --git a/nifi-assembly/src/main/assembly/dependencies.xml b/nifi-assembly/src/main/assembly/dependencies.xml index 0e5362c23de6..7acd0bcdbb20 100644 --- a/nifi-assembly/src/main/assembly/dependencies.xml +++ b/nifi-assembly/src/main/assembly/dependencies.xml @@ -36,6 +36,7 @@ nifi-bootstrap nifi-resources nifi-docs + nifi-web-api diff --git a/nifi-assembly/src/main/assembly/site.xml b/nifi-assembly/src/main/assembly/site.xml new file mode 100644 index 000000000000..9b87ed6a6a26 --- /dev/null +++ b/nifi-assembly/src/main/assembly/site.xml @@ -0,0 +1,57 @@ + + + + site + + dir + + true + nifi-docs + + + + + runtime + false + ./ + true + + nifi-docs + + true + + false + + + LICENSE + NOTICE + + + + + + + ./target/docs/ + components + + **/*.* + + + + ./target/nifi-web-api/docs/ + ./ + + **/*.* + + + + From 13c670a903bb19aabeb818095c26b1b51e29748b Mon Sep 17 00:00:00 2001 From: danbress Date: Tue, 18 Aug 2015 19:42:32 -0400 Subject: [PATCH 2/4] NIFI-725 - kicking off documentation plugin --- .../nifi-ambari-reporting-task/pom.xml | 8 +++++ .../nifi-aws-processors/pom.xml | 4 +++ .../nifi-flume-processors/pom.xml | 4 +++ .../nifi-geo-processors/pom.xml | 8 +++++ .../nifi-hdfs-processors/pom.xml | 8 +++++ .../nifi-hl7-processors/pom.xml | 4 +++ .../nifi-kafka-processors/pom.xml | 8 +++++ .../nifi-kite-processors/pom.xml | 8 +++++ .../nifi-yandex-processors/pom.xml | 8 +++++ .../nifi-mongodb-processors/pom.xml | 8 +++++ .../nifi-twitter-processors/pom.xml | 8 +++++ .../nifi-solr-processors/pom.xml | 4 +++ .../nifi-standard-processors/pom.xml | 4 +++ .../nifi-standard-reporting-tasks/pom.xml | 8 +++++ .../nifi-dbcp-service/pom.xml | 8 +++++ .../pom.xml | 8 +++++ .../nifi-distributed-cache-server/pom.xml | 8 +++++ .../nifi-http-context-map/pom.xml | 8 +++++ .../nifi-ssl-context-service/pom.xml | 8 +++++ .../nifi-update-attribute-processor/pom.xml | 8 +++++ nifi-nar-bundles/pom.xml | 31 +++++++++++++++++++ 21 files changed, 171 insertions(+) diff --git a/nifi-nar-bundles/nifi-ambari-bundle/nifi-ambari-reporting-task/pom.xml b/nifi-nar-bundles/nifi-ambari-bundle/nifi-ambari-reporting-task/pom.xml index ac5a9370d15f..73d55fc6e88c 100644 --- a/nifi-nar-bundles/nifi-ambari-bundle/nifi-ambari-reporting-task/pom.xml +++ b/nifi-nar-bundles/nifi-ambari-bundle/nifi-ambari-reporting-task/pom.xml @@ -68,4 +68,12 @@ test + + + + org.apache.nifi + nifi-documentation-plugin + + + diff --git a/nifi-nar-bundles/nifi-aws-bundle/nifi-aws-processors/pom.xml b/nifi-nar-bundles/nifi-aws-bundle/nifi-aws-processors/pom.xml index b7845249d494..e3cb8b25a2ad 100644 --- a/nifi-nar-bundles/nifi-aws-bundle/nifi-aws-processors/pom.xml +++ b/nifi-nar-bundles/nifi-aws-bundle/nifi-aws-processors/pom.xml @@ -62,6 +62,10 @@ + + org.apache.nifi + nifi-documentation-plugin + diff --git a/nifi-nar-bundles/nifi-flume-bundle/nifi-flume-processors/pom.xml b/nifi-nar-bundles/nifi-flume-bundle/nifi-flume-processors/pom.xml index e7932559e68b..81c6d782101c 100644 --- a/nifi-nar-bundles/nifi-flume-bundle/nifi-flume-processors/pom.xml +++ b/nifi-nar-bundles/nifi-flume-bundle/nifi-flume-processors/pom.xml @@ -173,6 +173,10 @@ + + org.apache.nifi + nifi-documentation-plugin + diff --git a/nifi-nar-bundles/nifi-geo-bundle/nifi-geo-processors/pom.xml b/nifi-nar-bundles/nifi-geo-bundle/nifi-geo-processors/pom.xml index 471f49962c40..9c9792490cec 100644 --- a/nifi-nar-bundles/nifi-geo-bundle/nifi-geo-processors/pom.xml +++ b/nifi-nar-bundles/nifi-geo-bundle/nifi-geo-processors/pom.xml @@ -40,4 +40,12 @@ 2.1.0 + + + + org.apache.nifi + nifi-documentation-plugin + + + diff --git a/nifi-nar-bundles/nifi-hadoop-bundle/nifi-hdfs-processors/pom.xml b/nifi-nar-bundles/nifi-hadoop-bundle/nifi-hdfs-processors/pom.xml index 6bc110f9d09e..2924d2b1f698 100644 --- a/nifi-nar-bundles/nifi-hadoop-bundle/nifi-hdfs-processors/pom.xml +++ b/nifi-nar-bundles/nifi-hadoop-bundle/nifi-hdfs-processors/pom.xml @@ -62,4 +62,12 @@ commons-io + + + + org.apache.nifi + nifi-documentation-plugin + + + diff --git a/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/pom.xml b/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/pom.xml index b8268d138447..1c155164c3f3 100644 --- a/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/pom.xml +++ b/nifi-nar-bundles/nifi-hl7-bundle/nifi-hl7-processors/pom.xml @@ -36,6 +36,10 @@ + + org.apache.nifi + nifi-documentation-plugin + diff --git a/nifi-nar-bundles/nifi-kafka-bundle/nifi-kafka-processors/pom.xml b/nifi-nar-bundles/nifi-kafka-bundle/nifi-kafka-processors/pom.xml index 8cee490886b3..779dd37cb259 100644 --- a/nifi-nar-bundles/nifi-kafka-bundle/nifi-kafka-processors/pom.xml +++ b/nifi-nar-bundles/nifi-kafka-bundle/nifi-kafka-processors/pom.xml @@ -66,4 +66,12 @@ test + + + + org.apache.nifi + nifi-documentation-plugin + + + diff --git a/nifi-nar-bundles/nifi-kite-bundle/nifi-kite-processors/pom.xml b/nifi-nar-bundles/nifi-kite-bundle/nifi-kite-processors/pom.xml index 118731a2d9b2..d6611d66b284 100644 --- a/nifi-nar-bundles/nifi-kite-bundle/nifi-kite-processors/pom.xml +++ b/nifi-nar-bundles/nifi-kite-bundle/nifi-kite-processors/pom.xml @@ -130,4 +130,12 @@ + + + + org.apache.nifi + nifi-documentation-plugin + + + diff --git a/nifi-nar-bundles/nifi-language-translation-bundle/nifi-yandex-processors/pom.xml b/nifi-nar-bundles/nifi-language-translation-bundle/nifi-yandex-processors/pom.xml index 746126a8be04..d106dadff515 100644 --- a/nifi-nar-bundles/nifi-language-translation-bundle/nifi-yandex-processors/pom.xml +++ b/nifi-nar-bundles/nifi-language-translation-bundle/nifi-yandex-processors/pom.xml @@ -60,4 +60,12 @@ test + + + + org.apache.nifi + nifi-documentation-plugin + + + diff --git a/nifi-nar-bundles/nifi-mongodb-bundle/nifi-mongodb-processors/pom.xml b/nifi-nar-bundles/nifi-mongodb-bundle/nifi-mongodb-processors/pom.xml index c1f6f9ded59a..1dc8fff932d3 100644 --- a/nifi-nar-bundles/nifi-mongodb-bundle/nifi-mongodb-processors/pom.xml +++ b/nifi-nar-bundles/nifi-mongodb-bundle/nifi-mongodb-processors/pom.xml @@ -63,4 +63,12 @@ test + + + + org.apache.nifi + nifi-documentation-plugin + + + diff --git a/nifi-nar-bundles/nifi-social-media-bundle/nifi-twitter-processors/pom.xml b/nifi-nar-bundles/nifi-social-media-bundle/nifi-twitter-processors/pom.xml index 41bc45f56d4d..8d9f29ae409f 100644 --- a/nifi-nar-bundles/nifi-social-media-bundle/nifi-twitter-processors/pom.xml +++ b/nifi-nar-bundles/nifi-social-media-bundle/nifi-twitter-processors/pom.xml @@ -55,4 +55,12 @@ test + + + + org.apache.nifi + nifi-documentation-plugin + + + diff --git a/nifi-nar-bundles/nifi-solr-bundle/nifi-solr-processors/pom.xml b/nifi-nar-bundles/nifi-solr-bundle/nifi-solr-processors/pom.xml index 1ba510645268..b3f64b9b3195 100644 --- a/nifi-nar-bundles/nifi-solr-bundle/nifi-solr-processors/pom.xml +++ b/nifi-nar-bundles/nifi-solr-bundle/nifi-solr-processors/pom.xml @@ -112,6 +112,10 @@ + + org.apache.nifi + nifi-documentation-plugin + diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/pom.xml b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/pom.xml index 994612255c7e..7d258c6bc014 100644 --- a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/pom.xml +++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/pom.xml @@ -279,6 +279,10 @@ language governing permissions and limitations under the License. --> + + org.apache.nifi + nifi-documentation-plugin + diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-reporting-tasks/pom.xml b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-reporting-tasks/pom.xml index 6977495f2b2f..0d463f4c6704 100644 --- a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-reporting-tasks/pom.xml +++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-reporting-tasks/pom.xml @@ -57,4 +57,12 @@ test + + + + org.apache.nifi + nifi-documentation-plugin + + + diff --git a/nifi-nar-bundles/nifi-standard-services/nifi-dbcp-service-bundle/nifi-dbcp-service/pom.xml b/nifi-nar-bundles/nifi-standard-services/nifi-dbcp-service-bundle/nifi-dbcp-service/pom.xml index 6bcdc84230c5..a904a13d211b 100644 --- a/nifi-nar-bundles/nifi-standard-services/nifi-dbcp-service-bundle/nifi-dbcp-service/pom.xml +++ b/nifi-nar-bundles/nifi-standard-services/nifi-dbcp-service-bundle/nifi-dbcp-service/pom.xml @@ -58,4 +58,12 @@ 1.3 + + + + org.apache.nifi + nifi-documentation-plugin + + + \ No newline at end of file diff --git a/nifi-nar-bundles/nifi-standard-services/nifi-distributed-cache-services-bundle/nifi-distributed-cache-client-service/pom.xml b/nifi-nar-bundles/nifi-standard-services/nifi-distributed-cache-services-bundle/nifi-distributed-cache-client-service/pom.xml index 27859aa9362b..a3a001c240b0 100644 --- a/nifi-nar-bundles/nifi-standard-services/nifi-distributed-cache-services-bundle/nifi-distributed-cache-client-service/pom.xml +++ b/nifi-nar-bundles/nifi-standard-services/nifi-distributed-cache-services-bundle/nifi-distributed-cache-client-service/pom.xml @@ -48,4 +48,12 @@ nifi-ssl-context-service-api + + + + org.apache.nifi + nifi-documentation-plugin + + + diff --git a/nifi-nar-bundles/nifi-standard-services/nifi-distributed-cache-services-bundle/nifi-distributed-cache-server/pom.xml b/nifi-nar-bundles/nifi-standard-services/nifi-distributed-cache-services-bundle/nifi-distributed-cache-server/pom.xml index 335c4f0f1c82..67e97e54e9ee 100644 --- a/nifi-nar-bundles/nifi-standard-services/nifi-distributed-cache-services-bundle/nifi-distributed-cache-server/pom.xml +++ b/nifi-nar-bundles/nifi-standard-services/nifi-distributed-cache-services-bundle/nifi-distributed-cache-server/pom.xml @@ -67,4 +67,12 @@ test + + + + org.apache.nifi + nifi-documentation-plugin + + + diff --git a/nifi-nar-bundles/nifi-standard-services/nifi-http-context-map-bundle/nifi-http-context-map/pom.xml b/nifi-nar-bundles/nifi-standard-services/nifi-http-context-map-bundle/nifi-http-context-map/pom.xml index fc638991cddf..a25e3ee95f94 100644 --- a/nifi-nar-bundles/nifi-standard-services/nifi-http-context-map-bundle/nifi-http-context-map/pom.xml +++ b/nifi-nar-bundles/nifi-standard-services/nifi-http-context-map-bundle/nifi-http-context-map/pom.xml @@ -41,4 +41,12 @@ javax.servlet-api + + + + org.apache.nifi + nifi-documentation-plugin + + + diff --git a/nifi-nar-bundles/nifi-standard-services/nifi-ssl-context-bundle/nifi-ssl-context-service/pom.xml b/nifi-nar-bundles/nifi-standard-services/nifi-ssl-context-bundle/nifi-ssl-context-service/pom.xml index 6e110bc79679..70516c10e517 100644 --- a/nifi-nar-bundles/nifi-standard-services/nifi-ssl-context-bundle/nifi-ssl-context-service/pom.xml +++ b/nifi-nar-bundles/nifi-standard-services/nifi-ssl-context-bundle/nifi-ssl-context-service/pom.xml @@ -44,4 +44,12 @@ test + + + + org.apache.nifi + nifi-documentation-plugin + + + diff --git a/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-processor/pom.xml b/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-processor/pom.xml index 786ff8ebfcae..2185abb75ced 100644 --- a/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-processor/pom.xml +++ b/nifi-nar-bundles/nifi-update-attribute-bundle/nifi-update-attribute-processor/pom.xml @@ -50,4 +50,12 @@ test + + + + org.apache.nifi + nifi-documentation-plugin + + + diff --git a/nifi-nar-bundles/pom.xml b/nifi-nar-bundles/pom.xml index fdcececcaada..2f7219e7fd65 100644 --- a/nifi-nar-bundles/pom.xml +++ b/nifi-nar-bundles/pom.xml @@ -128,4 +128,35 @@ + + + + + org.apache.nifi + nifi-documentation-plugin + 1.0.0-SNAPSHOT + + + generate-docs + + document + + + + + + org.apache.nifi + nifi-api + 0.2.1 + + + org.slf4j + slf4j-api + 1.7.12 + + + + + + From 1d19a34ecb2587fd3c14bc57892ad65097a45fec Mon Sep 17 00:00:00 2001 From: danbress Date: Tue, 18 Aug 2015 21:45:05 -0400 Subject: [PATCH 3/4] NIFI-725 - dont generate documentation --- .../nifi-runtime/src/main/java/org/apache/nifi/NiFi.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-runtime/src/main/java/org/apache/nifi/NiFi.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-runtime/src/main/java/org/apache/nifi/NiFi.java index ef2377f25b75..dd4913834b04 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-runtime/src/main/java/org/apache/nifi/NiFi.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-runtime/src/main/java/org/apache/nifi/NiFi.java @@ -120,8 +120,6 @@ public void run() { ExtensionManager.discoverExtensions(); ExtensionManager.logClassLoaderMapping(); - DocGenerator.generate(properties); - // load the server from the framework classloader Thread.currentThread().setContextClassLoader(frameworkClassLoader); Class jettyServer = Class.forName("org.apache.nifi.web.server.JettyServer", true, frameworkClassLoader); From af87b52b8657582d6e0dec162d760a85d35eccfa Mon Sep 17 00:00:00 2001 From: danbress Date: Tue, 18 Aug 2015 21:46:18 -0400 Subject: [PATCH 4/4] NIFI-725 - adding nifi-documentation-plugin --- 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 + .../java/org/apache/nifi/DocumentMojo.java | 177 +++++ .../org/apache/nifi/ExtractDocumentation.java | 111 ++++ .../org/apache/nifi/NoCloseInputStream.java | 32 + .../ConfigurableComponentInitializer.java | 45 ++ .../documentation/DocumentationWriter.java | 33 + .../html/HtmlDocumentationWriter.java | 615 ++++++++++++++++++ .../HtmlProcessorDocumentationWriter.java | 256 ++++++++ .../init/ControllerServiceInitializer.java | 53 ++ .../init/ProcessorInitializer.java | 53 ++ .../init/ReportingTaskingInitializer.java | 52 ++ .../mock/MockConfigurationContext.java | 48 ++ ...ontrollerServiceInitializationContext.java | 46 ++ .../mock/MockControllerServiceLookup.java | 65 ++ .../mock/MockProcessContext.java | 85 +++ .../MockProcessorInitializationContext.java | 45 ++ .../mock/MockProcessorLogger.java | 169 +++++ .../MockReportingInitializationContext.java | 67 ++ .../documentation/util/ReflectionUtils.java | 139 ++++ 22 files changed, 2296 insertions(+) create mode 100644 nifi-documentation-plugin/pom.xml create mode 100644 nifi-documentation-plugin/src/it/settings.xml create mode 100644 nifi-documentation-plugin/src/it/simple-it/pom.xml create mode 100644 nifi-documentation-plugin/src/it/simple-it/verify.groovy create mode 100644 nifi-documentation-plugin/src/main/java/org/apache/nifi/DocumentMojo.java create mode 100644 nifi-documentation-plugin/src/main/java/org/apache/nifi/ExtractDocumentation.java create mode 100644 nifi-documentation-plugin/src/main/java/org/apache/nifi/NoCloseInputStream.java create mode 100644 nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/ConfigurableComponentInitializer.java create mode 100644 nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/DocumentationWriter.java create mode 100644 nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/html/HtmlDocumentationWriter.java create mode 100644 nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/html/HtmlProcessorDocumentationWriter.java create mode 100644 nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/init/ControllerServiceInitializer.java create mode 100644 nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/init/ProcessorInitializer.java create mode 100644 nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/init/ReportingTaskingInitializer.java create mode 100644 nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/mock/MockConfigurationContext.java create mode 100644 nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/mock/MockControllerServiceInitializationContext.java create mode 100644 nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/mock/MockControllerServiceLookup.java create mode 100644 nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/mock/MockProcessContext.java create mode 100644 nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/mock/MockProcessorInitializationContext.java create mode 100644 nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/mock/MockProcessorLogger.java create mode 100644 nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/mock/MockReportingInitializationContext.java create mode 100644 nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/util/ReflectionUtils.java diff --git a/nifi-documentation-plugin/pom.xml b/nifi-documentation-plugin/pom.xml new file mode 100644 index 000000000000..cc5e160bc0c4 --- /dev/null +++ b/nifi-documentation-plugin/pom.xml @@ -0,0 +1,113 @@ + + + 4.0.0 + + org.apache + apache + 17 + + + org.apache.nifi + nifi-documentation-plugin + 1.0.0-SNAPSHOT + maven-plugin + + nifi-documentation-plugin Maven Plugin + + + http://maven.apache.org + + + 1.7 + 1.7 + 3.0.5 + UTF-8 + UTF-8 + 2014 + + + + + org.apache.maven + maven-plugin-api + 2.2.1 + + + org.apache.maven + maven-project + 2.2.1 + + + org.apache.maven.plugin-tools + maven-plugin-annotations + 3.3 + provided + + + org.codehaus.plexus + plexus-utils + 3.0.8 + + + junit + junit + 4.8.2 + test + + + org.apache.nifi + nifi-api + 0.2.1 + provided + + + org.slf4j + slf4j-api + 1.7.12 + provided + + + com.google.guava + guava + 17.0 + + + + + + org.apache.maven.plugins + maven-plugin-plugin + + + default-descriptor + + descriptor + + process-classes + + + help-descriptor + + helpmojo + + process-classes + + + + + + diff --git a/nifi-documentation-plugin/src/it/settings.xml b/nifi-documentation-plugin/src/it/settings.xml new file mode 100644 index 000000000000..dc0212096d2e --- /dev/null +++ b/nifi-documentation-plugin/src/it/settings.xml @@ -0,0 +1,55 @@ + + + + + + + + it-repo + + true + + + + local.central + @localRepositoryUrl@ + + true + + + true + + + + + + local.central + @localRepositoryUrl@ + + true + + + true + + + + + + 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 000000000000..c8b4d7ac5142 --- /dev/null +++ b/nifi-documentation-plugin/src/it/simple-it/pom.xml @@ -0,0 +1,34 @@ + + + 4.0.0 + + org.apache.nifi.it + simple-it + 1.0-SNAPSHOT + + A simple IT verifying the basic use case. + + + UTF-8 + + + + + + @project.groupId@ + @project.artifactId@ + @project.version@ + + + touch + validate + + touch + + + + + + + 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 000000000000..ff7981320540 --- /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() 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 000000000000..bd9714901b51 --- /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 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 void loadServices(final ClassLoader projectClassLoader, final List components, final Class service) { + final ServiceLoader 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); + } +} 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 000000000000..957b757794b9 --- /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 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); + } + } + } + } + } + } + +} 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 000000000000..80b259e82409 --- /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 { + } +} 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 000000000000..ad21f2124a3d --- /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); +} 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 000000000000..d17863651a98 --- /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; +} 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 000000000000..394897e0ca44 --- /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(""); + 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 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 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 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 getDynamicProperties(ConfigurableComponent configurableComponent) { + final List 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 controllerServiceClass = property.getControllerServiceDefinition(); + + writeSimpleElement(xmlStreamWriter, "strong", "Controller Service API: "); + xmlStreamWriter.writeEmptyElement("br"); + xmlStreamWriter.writeCharacters(controllerServiceClass.getSimpleName()); + + final List> 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: <p>text</p> + * + * @param writer the stream writer to use + * @param elementName the name of the element + * @param characters the characters to insert into the element + * @param strong whether the characters should be strong or not. + * @throws XMLStreamException thrown if there was a problem writing to the + * stream. + */ + protected final static void writeSimpleElement(final XMLStreamWriter writer, final String elementName, + final String characters, boolean strong) throws XMLStreamException { + 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: <p + * id="p-id">text</p> + * + * @param writer the stream writer to use + * @param elementName the name of the element + * @param characters the text of the element + * @param 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: <p>text</p> + * + * @param writer the stream writer to use + * @param elementName the name of the element + * @param characters the characters to insert into the element + * @throws XMLStreamException thrown if there was a problem writing to the + * stream + */ + protected final static void writeSimpleElement(final XMLStreamWriter writer, final String elementName, + final String characters) throws XMLStreamException { + writeSimpleElement(writer, elementName, characters, false); + } + + /** + * A helper method to write a link + * + * @param xmlStreamWriter the stream to write to + * @param text the text of the link + * @param location the location of the link + * @throws XMLStreamException thrown if there was a problem writing to the + * stream + */ + protected void writeLink(final XMLStreamWriter xmlStreamWriter, final String text, final String location) + throws XMLStreamException { + xmlStreamWriter.writeStartElement("a"); + xmlStreamWriter.writeAttribute("href", location); + xmlStreamWriter.writeCharacters(text); + xmlStreamWriter.writeEndElement(); + } + + /** + * 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> lookupControllerServiceImpls( + final Class parent) { + + final List> implementations = new ArrayList<>(); + + // first get all ControllerService implementations + final Set 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 controllerServiceClass : controllerServices) { + if (parent.isAssignableFrom(controllerServiceClass)) { + implementations.add(controllerServiceClass); + } + } + + return implementations; + } +} 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 000000000000..4a15b50426c6 --- /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 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 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 getReadsAttributes(Processor processor) { + List 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 getWritesAttributes(Processor processor) { + List 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 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 getDynamicRelationships(Processor processor) { + List results = new ArrayList<>(); + + DynamicRelationship dynamicRelationships = processor.getClass().getAnnotation(DynamicRelationship.class); + if (dynamicRelationships != null) { + results.add(dynamicRelationships); + } + + return results; + } +} 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 000000000000..dc9f82fb5a9a --- /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); + + } +} 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 000000000000..f4ca36dcd10a --- /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); + + } +} 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 000000000000..0a43f0b87456 --- /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); + + } +} 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 000000000000..6c9ec9dde114 --- /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 getProperties() { + return Collections.emptyMap(); + } + + @Override + public String getSchedulingPeriod() { + return "0 secs"; + } + + @Override + public Long getSchedulingPeriod(final TimeUnit timeUnit) { + return 0L; + } +} 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 000000000000..14076a3dee05 --- /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(); + } + +} 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 000000000000..5c60881ceef1 --- /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 getControllerServiceIdentifiers(Class serviceType) + throws IllegalArgumentException { + return Collections.emptySet(); + } + + @Override + public boolean isControllerServiceEnabling(String serviceIdentifier) { + return false; + } + + @Override + public String getControllerServiceName(String serviceIdentifier) { + return serviceIdentifier; + } + +} diff --git a/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/mock/MockProcessContext.java b/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/mock/MockProcessContext.java new file mode 100644 index 000000000000..76d7d3d447e5 --- /dev/null +++ b/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/mock/MockProcessContext.java @@ -0,0 +1,85 @@ +/* + * 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.Set; + +import org.apache.nifi.components.PropertyDescriptor; +import org.apache.nifi.components.PropertyValue; +import org.apache.nifi.controller.ControllerServiceLookup; +import org.apache.nifi.processor.ProcessContext; +import org.apache.nifi.processor.Relationship; + +public class MockProcessContext implements ProcessContext { + + @Override + public PropertyValue getProperty(PropertyDescriptor descriptor) { + return null; + } + + @Override + public PropertyValue getProperty(String propertyName) { + return null; + } + + @Override + public PropertyValue newPropertyValue(String rawValue) { + return null; + } + + @Override + public void yield() { + + } + + @Override + public int getMaxConcurrentTasks() { + return 0; + } + + @Override + public String getAnnotationData() { + return ""; + } + + @Override + public Map getProperties() { + return Collections.emptyMap(); + } + + @Override + public String encrypt(String unencrypted) { + return unencrypted; + } + + @Override + public String decrypt(String encrypted) { + return encrypted; + } + + @Override + public ControllerServiceLookup getControllerServiceLookup() { + return new MockControllerServiceLookup(); + } + + @Override + public Set getAvailableRelationships() { + return Collections.emptySet(); + } +} diff --git a/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/mock/MockProcessorInitializationContext.java b/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/mock/MockProcessorInitializationContext.java new file mode 100644 index 000000000000..2b6ccc2e7368 --- /dev/null +++ b/nifi-documentation-plugin/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 "mock-processor"; + } + + @Override + public ProcessorLog getLogger() { + return new MockProcessorLogger(); + } + + @Override + public ControllerServiceLookup getControllerServiceLookup() { + return new MockControllerServiceLookup(); + } +} diff --git a/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/mock/MockProcessorLogger.java b/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/mock/MockProcessorLogger.java new file mode 100644 index 000000000000..82a5f8b07d8e --- /dev/null +++ b/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/mock/MockProcessorLogger.java @@ -0,0 +1,169 @@ +/* + * 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.logging.ProcessorLog; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Stubs out the functionality of a ProcessorLog/ComponentLog so that it can + * be used during initialization of a component. + * + */ +public class MockProcessorLogger implements ProcessorLog { + + private static final Logger logger = LoggerFactory + .getLogger(MockProcessorLogger.class); + + @Override + public void warn(String msg, Throwable t) { + logger.warn(msg, t); + } + + @Override + public void warn(String msg, Object[] os) { + logger.warn(msg, os); + } + + @Override + public void warn(String msg, Object[] os, Throwable t) { + logger.warn(msg, os); + logger.warn("", t); + } + + @Override + public void warn(String msg) { + logger.warn(msg); + } + + @Override + public void trace(String msg, Throwable t) { + logger.trace(msg, t); + } + + @Override + public void trace(String msg, Object[] os) { + logger.trace(msg, os); + } + + @Override + public void trace(String msg) { + logger.trace(msg); + } + + @Override + public void trace(String msg, Object[] os, Throwable t) { + logger.trace(msg, os); + logger.trace("", t); + } + + @Override + public boolean isWarnEnabled() { + return logger.isWarnEnabled(); + } + + @Override + public boolean isTraceEnabled() { + return logger.isTraceEnabled(); + } + + @Override + public boolean isInfoEnabled() { + return logger.isInfoEnabled(); + } + + @Override + public boolean isErrorEnabled() { + return logger.isErrorEnabled(); + } + + @Override + public boolean isDebugEnabled() { + return logger.isDebugEnabled(); + } + + @Override + public void info(String msg, Throwable t) { + logger.info(msg, t); + } + + @Override + public void info(String msg, Object[] os) { + logger.info(msg, os); + } + + @Override + public void info(String msg) { + logger.info(msg); + + } + + @Override + public void info(String msg, Object[] os, Throwable t) { + logger.trace(msg, os); + logger.trace("", t); + + } + + @Override + public String getName() { + return logger.getName(); + } + + @Override + public void error(String msg, Throwable t) { + logger.error(msg, t); + } + + @Override + public void error(String msg, Object[] os) { + logger.error(msg, os); + } + + @Override + public void error(String msg) { + logger.error(msg); + } + + @Override + public void error(String msg, Object[] os, Throwable t) { + logger.error(msg, os); + logger.error("", t); + } + + @Override + public void debug(String msg, Throwable t) { + logger.debug(msg, t); + } + + @Override + public void debug(String msg, Object[] os) { + logger.debug(msg, os); + } + + @Override + public void debug(String msg, Object[] os, Throwable t) { + logger.debug(msg, os); + logger.debug("", t); + } + + @Override + public void debug(String msg) { + logger.debug(msg); + } +} diff --git a/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/mock/MockReportingInitializationContext.java b/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/mock/MockReportingInitializationContext.java new file mode 100644 index 000000000000..9d0db6429506 --- /dev/null +++ b/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/mock/MockReportingInitializationContext.java @@ -0,0 +1,67 @@ +/* + * 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.logging.ComponentLog; +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. + * + */ +public class MockReportingInitializationContext implements ReportingInitializationContext { + + @Override + public String getIdentifier() { + return "mock-reporting-task"; + } + + @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; + } + + @Override + public ComponentLog getLogger() { + return new MockProcessorLogger(); + } +} diff --git a/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/util/ReflectionUtils.java b/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/util/ReflectionUtils.java new file mode 100644 index 000000000000..449bd075f775 --- /dev/null +++ b/nifi-documentation-plugin/src/main/java/org/apache/nifi/documentation/util/ReflectionUtils.java @@ -0,0 +1,139 @@ +/* + * 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.util; + +import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +import org.apache.nifi.logging.ProcessorLog; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class is a copy of org.apache.nifi.util.ReflectionUtils. Ultimately the documentation generation + * component should be moved to a place where it can depend on this directly instead of copying it in. + * + * + */ +public class ReflectionUtils { + + private final static Logger LOG = LoggerFactory.getLogger(ReflectionUtils.class); + /** + * Invokes all methods on the given instance that have been annotated with the given preferredAnnotation and if no such method exists will invoke all methods on the given instance that have been + * annotated with the given alternateAnnotation, if any exists. If the signature of the method that is defined in instance uses 1 or more parameters, those parameters must be + * specified by the args parameter. However, if more arguments are supplied by the args parameter than needed, the extra arguments will be ignored. + * + * @param preferredAnnotation preferred + * @param alternateAnnotation alternate + * @param instance instance + * @param logger the ProcessorLog to use for logging any errors. If null, will use own logger, but that will not generate bulletins or easily tie to the Processor's log messages. + * @param args args + * @return true if all appropriate methods were invoked and returned without throwing an Exception, false if one of the methods threw an Exception or could not be + * invoked; if false is returned, an error will have been logged. + */ + public static boolean quietlyInvokeMethodsWithAnnotations( + final Class preferredAnnotation, final Class alternateAnnotation, final Object instance, final ProcessorLog logger, final Object... args) { + final List> annotationClasses = new ArrayList<>(alternateAnnotation == null ? 1 : 2); + annotationClasses.add(preferredAnnotation); + if (alternateAnnotation != null) { + annotationClasses.add(alternateAnnotation); + } + + boolean annotationFound = false; + for (final Class annotationClass : annotationClasses) { + if (annotationFound) { + break; + } + + for (final Method method : instance.getClass().getMethods()) { + if (method.isAnnotationPresent(annotationClass)) { + annotationFound = true; + + final boolean isAccessible = method.isAccessible(); + method.setAccessible(true); + + try { + final Class[] argumentTypes = method.getParameterTypes(); + if (argumentTypes.length > args.length) { + if (logger == null) { + LOG.error("Unable to invoke method {} on {} because method expects {} parameters but only {} were given", + new Object[]{method.getName(), instance, argumentTypes.length, args.length}); + } else { + logger.error("Unable to invoke method {} on {} because method expects {} parameters but only {} were given", + new Object[]{method.getName(), instance, argumentTypes.length, args.length}); + } + + return false; + } + + for (int i = 0; i < argumentTypes.length; i++) { + final Class argType = argumentTypes[i]; + if (!argType.isAssignableFrom(args[i].getClass())) { + if (logger == null) { + LOG.error("Unable to invoke method {} on {} because method parameter {} is expected to be of type {} but argument passed was of type {}", + new Object[]{method.getName(), instance, i, argType, args[i].getClass()}); + } else { + logger.error("Unable to invoke method {} on {} because method parameter {} is expected to be of type {} but argument passed was of type {}", + new Object[]{method.getName(), instance, i, argType, args[i].getClass()}); + } + + return false; + } + } + + try { + if (argumentTypes.length == args.length) { + method.invoke(instance, args); + } else { + final Object[] argsToPass = new Object[argumentTypes.length]; + for (int i = 0; i < argsToPass.length; i++) { + argsToPass[i] = args[i]; + } + + method.invoke(instance, argsToPass); + } + } catch (final InvocationTargetException ite) { + if (logger == null) { + LOG.error("Unable to invoke method {} on {} due to {}", new Object[]{method.getName(), instance, ite.getCause()}); + LOG.error("", ite.getCause()); + } else { + logger.error("Unable to invoke method {} on {} due to {}", new Object[]{method.getName(), instance, ite.getCause()}); + } + } catch (final IllegalAccessException | IllegalArgumentException t) { + if (logger == null) { + LOG.error("Unable to invoke method {} on {} due to {}", new Object[]{method.getName(), instance, t}); + LOG.error("", t); + } else { + logger.error("Unable to invoke method {} on {} due to {}", new Object[]{method.getName(), instance, t}); + } + + return false; + } + } finally { + if (!isAccessible) { + method.setAccessible(false); + } + } + } + } + } + return true; + } +}