You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by ma...@apache.org on 2017/11/13 19:45:06 UTC
[3/3] nifi git commit: groovyx initial version
groovyx initial version
NIFI-3688 license update to ASF
NIFI-3688 add groovyx dependency
NIFI-3688 comments from @alopresto
- reformat code https://cwiki.apache.org/confluence/display/NIFI/Contributor+Guide#ContributorGuide-EclipseUsers
- enabled java-doc
NIFI-3688
- try fix mess with licenses
- remove commented import
- remove unused var `sql`
NIFI-3688 the properties/skip javadoc/src
NIFI-3688 remove not used sql var
NIFI-3688
- first test case
NIFI-3688
+ 2 more tests based on current groovy tests
NIFI-3688 comment from @mattyb149
- processor renamed to ExecuteGroovyScript
NIFI-3688 fix script path validation
NIFI-3688
- refactor to compile on validation
- prepare 4 test cases with database
NIFI-3688 new test cases with groovy and sql
NIFI-3688 documentation
NIFI-3688
- refactor groovy extended methods
- add more test cases
NIFI-3688
- codestyle
- javadoc
- refactor flowfile voids to self-reference
- fix test cases
NIFI-3688 minor comments changes
NIFI-3688 rename additional documentation according to processor classname
NIFI-3688 exclude json test file from rat check
NIFI-3688 codestyle
NIFI-3688 add nar bundle to root pom.xml and to nifi-assembly/pom.xml
NIFI-3688
- fix & extend additional documentation
- fix pom.xml to bundle groovy into nar
NIFI-3688 add examples into additional processor documentation
NIFI-3688 fix pom.xml to exclude unnecessary libs from nar bundle
NIFI-3688 add restricted annotation
NIFI-3688 change version from 1.2.0-SNAPSHOT to 1.3.0-SNAPSHOT after rebase
NIFI-3688 new method in ProcessSession: public OutputStream write(FlowFile source)
NIFI-3688 change version from 1.3.0-SNAPSHOT to 1.4.0-SNAPSHOT after rebase
NIFI-3688 fix for @mattyb149 comment: The bundles referred to here and below are not included with this NAR and should be removed.
NIFI-3688 fix for @mattyb149 comment: This unused line can be removed
NIFI-3688
- removed `require flowfile` property
- fixed test cases according to deprecated property
change version to 1.5.0-SNAPSHOT
[NIFI-3688] Commented by mistake
[NIFI-3688] remove unused class
[NIFI-3688] fix javadoc comments
[NIFI-3688] refactor CTL & SQL properties
b3eecec9013435bc5faef6d25966fa2962620144
2916ce1ec80714b886cbed9797bf7874aacb32dd
8e15392e2fcddc56ef75b333177b0299bbded159
NIFI-3688: Checkstyle and typo fixes
Signed-off-by: Matthew Burgess <ma...@apache.org>
This closes #1662
Project: http://git-wip-us.apache.org/repos/asf/nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/nifi/commit/c7a5a09b
Tree: http://git-wip-us.apache.org/repos/asf/nifi/tree/c7a5a09b
Diff: http://git-wip-us.apache.org/repos/asf/nifi/diff/c7a5a09b
Branch: refs/heads/master
Commit: c7a5a09b8aeb54931e9f59a1437d0ae7a8299cfd
Parents: 2d3e5ab
Author: dlukyanov <dl...@ukr.net>
Authored: Sun Apr 2 10:55:22 2017 +0300
Committer: Matthew Burgess <ma...@apache.org>
Committed: Mon Nov 13 14:32:58 2017 -0500
----------------------------------------------------------------------
nifi-assembly/pom.xml | 5 +
.../nifi-groovyx-nar/pom.xml | 44 +
.../src/main/resources/META-INF/LICENSE | 211 ++++
.../src/main/resources/META-INF/NOTICE | 14 +
.../nifi-groovyx-processors/pom.xml | 75 ++
.../processors/groovyx/ExecuteGroovyScript.java | 508 ++++++++++
.../nifi/processors/groovyx/GroovyMethods.java | 85 ++
.../groovyx/flow/GroovyProcessSessionWrap.java | 72 ++
.../groovyx/flow/GroovySessionFile.java | 284 ++++++
.../groovyx/flow/ProcessSessionWrap.java | 969 +++++++++++++++++++
.../processors/groovyx/flow/SessionFile.java | 243 +++++
.../nifi/processors/groovyx/sql/OSql.java | 69 ++
.../nifi/processors/groovyx/util/Files.java | 65 ++
.../processors/groovyx/util/Throwables.java | 176 ++++
.../processors/groovyx/util/Validators.java | 57 ++
.../org.apache.nifi.processor.Processor | 15 +
.../SQL.gif | Bin 0 -> 30954 bytes
.../SQL2.gif | Bin 0 -> 28716 bytes
.../additionalDetails.html | 203 ++++
.../groovyx/ExecuteGroovyScriptTest.java | 392 ++++++++
.../resources/groovy/test_ctl_01_access.groovy | 24 +
.../test/resources/groovy/test_no_input.groovy | 31 +
.../test/resources/groovy/test_onTrigger.groovy | 24 +
.../resources/groovy/test_onTriggerX.groovy | 20 +
.../groovy/test_onTrigger_changeContent.groovy | 41 +
.../groovy/test_onTrigger_changeContentX.groovy | 38 +
.../resources/groovy/test_sql_01_select.groovy | 34 +
.../groovy/test_sql_02_blob_write.groovy | 32 +
.../groovy/test_sql_03_blob_read.groovy | 29 +
.../groovy/test_sql_04_insert_and_json.groovy | 54 ++
.../groovy/test_sql_04_insert_and_json.json | 7 +
nifi-nar-bundles/nifi-groovyx-bundle/pom.xml | 48 +
nifi-nar-bundles/pom.xml | 1 +
pom.xml | 6 +
34 files changed, 3876 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/nifi/blob/c7a5a09b/nifi-assembly/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-assembly/pom.xml b/nifi-assembly/pom.xml
index b9cd707..3f5dba4 100755
--- a/nifi-assembly/pom.xml
+++ b/nifi-assembly/pom.xml
@@ -353,6 +353,11 @@
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
+ <artifactId>nifi-groovyx-nar</artifactId>
+ <type>nar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.nifi</groupId>
<artifactId>nifi-elasticsearch-nar</artifactId>
<type>nar</type>
</dependency>
http://git-wip-us.apache.org/repos/asf/nifi/blob/c7a5a09b/nifi-nar-bundles/nifi-groovyx-bundle/nifi-groovyx-nar/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-groovyx-bundle/nifi-groovyx-nar/pom.xml b/nifi-nar-bundles/nifi-groovyx-bundle/nifi-groovyx-nar/pom.xml
new file mode 100644
index 0000000..4afe2c1
--- /dev/null
+++ b/nifi-nar-bundles/nifi-groovyx-bundle/nifi-groovyx-nar/pom.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.nifi</groupId>
+ <artifactId>nifi-groovyx-bundle</artifactId>
+ <version>1.5.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>nifi-groovyx-nar</artifactId>
+ <packaging>nar</packaging>
+ <properties>
+ <maven.javadoc.skip>true</maven.javadoc.skip>
+ <source.skip>true</source.skip>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.nifi</groupId>
+ <artifactId>nifi-standard-services-api-nar</artifactId>
+ <type>nar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.nifi</groupId>
+ <artifactId>nifi-groovyx-processors</artifactId>
+ </dependency>
+ </dependencies>
+</project>
+
http://git-wip-us.apache.org/repos/asf/nifi/blob/c7a5a09b/nifi-nar-bundles/nifi-groovyx-bundle/nifi-groovyx-nar/src/main/resources/META-INF/LICENSE
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-groovyx-bundle/nifi-groovyx-nar/src/main/resources/META-INF/LICENSE b/nifi-nar-bundles/nifi-groovyx-bundle/nifi-groovyx-nar/src/main/resources/META-INF/LICENSE
new file mode 100644
index 0000000..513ea56
--- /dev/null
+++ b/nifi-nar-bundles/nifi-groovyx-bundle/nifi-groovyx-nar/src/main/resources/META-INF/LICENSE
@@ -0,0 +1,211 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed 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.
+
+
+
+ nifi-groovyx-bundle includes subcomponents with separate copyright notices and
+ license terms. Your use of these subcomponents is subject to the terms
+ and conditions of the following licenses:
+
+ The binary distribution of this product bundles 'Apache Groovy Language'
+ under an Apache License Version 2.0, January 2004 http://www.apache.org/licenses/
http://git-wip-us.apache.org/repos/asf/nifi/blob/c7a5a09b/nifi-nar-bundles/nifi-groovyx-bundle/nifi-groovyx-nar/src/main/resources/META-INF/NOTICE
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-groovyx-bundle/nifi-groovyx-nar/src/main/resources/META-INF/NOTICE b/nifi-nar-bundles/nifi-groovyx-bundle/nifi-groovyx-nar/src/main/resources/META-INF/NOTICE
new file mode 100644
index 0000000..90b232e
--- /dev/null
+++ b/nifi-nar-bundles/nifi-groovyx-bundle/nifi-groovyx-nar/src/main/resources/META-INF/NOTICE
@@ -0,0 +1,14 @@
+nifi-groovyx-nar
+Copyright 2014-2017 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+******************
+Apache Software License v2
+******************
+
+The following binary components are provided under the Apache Software License v2
+
+Apache Groovy
+Copyright 2003-2015 The Apache Software Foundation
http://git-wip-us.apache.org/repos/asf/nifi/blob/c7a5a09b/nifi-nar-bundles/nifi-groovyx-bundle/nifi-groovyx-processors/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-groovyx-bundle/nifi-groovyx-processors/pom.xml b/nifi-nar-bundles/nifi-groovyx-bundle/nifi-groovyx-processors/pom.xml
new file mode 100644
index 0000000..1a56eaa
--- /dev/null
+++ b/nifi-nar-bundles/nifi-groovyx-bundle/nifi-groovyx-processors/pom.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.nifi</groupId>
+ <artifactId>nifi-groovyx-bundle</artifactId>
+ <version>1.5.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>nifi-groovyx-processors</artifactId>
+ <packaging>jar</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.nifi</groupId>
+ <artifactId>nifi-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.nifi</groupId>
+ <artifactId>nifi-processor-utils</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.groovy</groupId>
+ <artifactId>groovy-all</artifactId>
+ <version>2.4.12</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.nifi</groupId>
+ <artifactId>nifi-dbcp-service-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.nifi</groupId>
+ <artifactId>nifi-mock</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.derby</groupId>
+ <artifactId>derby</artifactId>
+ <version>10.12.1.1</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.rat</groupId>
+ <artifactId>apache-rat-plugin</artifactId>
+ <configuration>
+ <excludes combine.children="append">
+ <exclude>src/test/resources/groovy/test_sql_04_insert_and_json.json</exclude>
+ </excludes>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
+
http://git-wip-us.apache.org/repos/asf/nifi/blob/c7a5a09b/nifi-nar-bundles/nifi-groovyx-bundle/nifi-groovyx-processors/src/main/java/org/apache/nifi/processors/groovyx/ExecuteGroovyScript.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-groovyx-bundle/nifi-groovyx-processors/src/main/java/org/apache/nifi/processors/groovyx/ExecuteGroovyScript.java b/nifi-nar-bundles/nifi-groovyx-bundle/nifi-groovyx-processors/src/main/java/org/apache/nifi/processors/groovyx/ExecuteGroovyScript.java
new file mode 100644
index 0000000..3979f6c
--- /dev/null
+++ b/nifi-nar-bundles/nifi-groovyx-bundle/nifi-groovyx-processors/src/main/java/org/apache/nifi/processors/groovyx/ExecuteGroovyScript.java
@@ -0,0 +1,508 @@
+/*
+ * 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.processors.groovyx;
+
+import java.io.File;
+import java.lang.reflect.Method;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.nifi.annotation.behavior.Restricted;
+import org.apache.nifi.annotation.behavior.DynamicProperty;
+import org.apache.nifi.annotation.behavior.EventDriven;
+import org.apache.nifi.annotation.behavior.InputRequirement;
+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.annotation.lifecycle.OnScheduled;
+import org.apache.nifi.annotation.lifecycle.OnStopped;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.controller.ControllerService;
+import org.apache.nifi.dbcp.DBCPService;
+import org.apache.nifi.processor.AbstractProcessor;
+import org.apache.nifi.processor.ProcessContext;
+import org.apache.nifi.processor.ProcessSession;
+import org.apache.nifi.processor.ProcessorInitializationContext;
+import org.apache.nifi.processor.Relationship;
+import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.processor.util.StandardValidators;
+import org.codehaus.groovy.control.CompilerConfiguration;
+import org.codehaus.groovy.runtime.ResourceGroovyMethods;
+import org.codehaus.groovy.runtime.StackTraceUtils;
+
+import org.apache.nifi.processors.groovyx.sql.OSql;
+import org.apache.nifi.processors.groovyx.util.Files;
+import org.apache.nifi.processors.groovyx.util.Validators;
+import org.apache.nifi.processors.groovyx.flow.GroovyProcessSessionWrap;
+
+import groovy.lang.GroovyShell;
+import groovy.lang.Script;
+
+import org.apache.nifi.components.ValidationResult;
+import org.apache.nifi.components.ValidationContext;
+
+@EventDriven
+@InputRequirement(InputRequirement.Requirement.INPUT_ALLOWED)
+@Tags({"script", "groovy", "groovyx"})
+@CapabilityDescription(
+ "Experimental Extended Groovy script processor. The script is responsible for "
+ + "handling the incoming flow file (transfer to SUCCESS or remove, e.g.) as well as any flow files created by "
+ + "the script. If the handling is incomplete or incorrect, the session will be rolled back.")
+@Restricted("Provides operator the ability to execute arbitrary code assuming all permissions that NiFi has.")
+@SeeAlso(classNames={"org.apache.nifi.processors.script.ExecuteScript"})
+@DynamicProperty(name = "A script engine property to update",
+ value = "The value to set it to",
+ supportsExpressionLanguage = true,
+ description = "Updates a script engine property specified by the Dynamic Property's key with the value "
+ + "specified by the Dynamic Property's value. Use `CTL.` to access any controller services.")
+public class ExecuteGroovyScript extends AbstractProcessor {
+ public static final String GROOVY_CLASSPATH = "${groovy.classes.path}";
+
+ private static final String PRELOADS = "import org.apache.nifi.components.*;" + "import org.apache.nifi.flowfile.FlowFile;" + "import org.apache.nifi.processor.*;"
+ + "import org.apache.nifi.processor.FlowFileFilter.FlowFileFilterResult;" + "import org.apache.nifi.processor.exception.*;" + "import org.apache.nifi.processor.io.*;"
+ + "import org.apache.nifi.processor.util.*;" + "import org.apache.nifi.processors.script.*;" + "import org.apache.nifi.logging.ComponentLog;";
+
+ public static final PropertyDescriptor SCRIPT_FILE = new PropertyDescriptor.Builder()
+ .name("groovyx-script-file")
+ .displayName("Script File")
+ .required(false)
+ .description("Path to script file to execute. Only one of Script File or Script Body may be used")
+ .addValidator(Validators.createFileExistsAndReadableValidator())
+ .expressionLanguageSupported(true)
+ .build();
+
+ public static final PropertyDescriptor SCRIPT_BODY = new PropertyDescriptor.Builder()
+ .name("groovyx-script-body")
+ .displayName("Script Body")
+ .required(false)
+ .description("Body of script to execute. Only one of Script File or Script Body may be used")
+ .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+ .expressionLanguageSupported(false)
+ .build();
+
+ public static String[] VALID_FAIL_STRATEGY = {"rollback", "transfer to failure"};
+ public static final PropertyDescriptor FAIL_STRATEGY = new PropertyDescriptor.Builder()
+ .name("groovyx-failure-strategy")
+ .displayName("Failure strategy")
+ .description("What to do with unhandled exceptions. If you want to manage exception by code then keep the default value `rollback`."
+ +" If `transfer to failure` selected and unhandled exception occurred then all flowFiles received from incoming queues in this session"
+ +" will be transferred to `failure` relationship with additional attributes set: ERROR_MESSAGE and ERROR_STACKTRACE."
+ +" If `rollback` selected and unhandled exception occurred then all flowFiles received from incoming queues will be penalized and returned."
+ +" If the processor has no incoming connections then this parameter has no effect."
+ )
+ .required(true).expressionLanguageSupported(false).allowableValues(VALID_FAIL_STRATEGY).defaultValue(VALID_FAIL_STRATEGY[0]).build();
+
+ public static final PropertyDescriptor ADD_CLASSPATH = new PropertyDescriptor.Builder()
+ .name("groovyx-additional-classpath")
+ .displayName("Additional classpath")
+ .required(false)
+ .description("Classpath list separated by semicolon. You can use masks like `*`, `*.jar` in file name.")
+ .addValidator(StandardValidators.NON_EMPTY_VALIDATOR).expressionLanguageSupported(true).build();
+
+ public static final Relationship REL_SUCCESS = new Relationship.Builder().name("success").description("FlowFiles that were successfully processed").build();
+
+ public static final Relationship REL_FAILURE = new Relationship.Builder().name("failure").description("FlowFiles that failed to be processed").build();
+
+ private List<PropertyDescriptor> descriptors;
+ private Set<Relationship> relationships;
+ //parameters evaluated on Start or on Validate
+ File scriptFile = null; //SCRIPT_FILE
+ String scriptBody = null; //SCRIPT_BODY
+ String addClasspath = null; //ADD_CLASSPATH
+ String groovyClasspath = null; //evaluated from GROOVY_CLASSPATH = ${groovy.classes.path} global property
+ //compiled script
+ volatile GroovyShell shell = null; //new GroovyShell();
+ volatile Class<Script> compiled = null; //compiled script
+ volatile long scriptLastModified = 0; //last scriptFile modification to check if recompile required
+
+ @Override
+ protected void init(final ProcessorInitializationContext context) {
+ List<PropertyDescriptor> descriptors = new ArrayList<>();
+ descriptors.add(SCRIPT_FILE);
+ descriptors.add(SCRIPT_BODY);
+ descriptors.add(FAIL_STRATEGY);
+ descriptors.add(ADD_CLASSPATH);
+ this.descriptors = Collections.unmodifiableList(descriptors);
+
+ HashSet<Relationship> relationshipSet = new HashSet<>();
+ relationshipSet.add(REL_SUCCESS);
+ relationshipSet.add(REL_FAILURE);
+ relationships = Collections.unmodifiableSet(relationshipSet);
+ }
+
+ @Override
+ public Set<Relationship> getRelationships() {
+ return relationships;
+ }
+
+ @Override
+ public final List<PropertyDescriptor> getSupportedPropertyDescriptors() {
+ return descriptors;
+ }
+
+ private File asFile(String f) {
+ if (f == null || f.length() == 0) {
+ return null;
+ }
+ return new File(f);
+ }
+
+ private void callScriptStatic(String method, final ProcessContext context) throws IllegalAccessException, java.lang.reflect.InvocationTargetException {
+ if (compiled != null) {
+ Method m = null;
+ try {
+ m = compiled.getDeclaredMethod(method, ProcessContext.class);
+ } catch (NoSuchMethodException e) {
+ // The method will not be invoked if it does not exist
+ }
+ if (m == null) {
+ try {
+ m = compiled.getDeclaredMethod(method, Object.class);
+ } catch (NoSuchMethodException e) {
+ // The method will not be invoked if it does not exist
+ }
+ }
+ if (m != null) {
+ m.invoke(null, context);
+ }
+ }
+ }
+
+ /**
+ * Let's do validation by script compile at this point.
+ *
+ * @param context provides a mechanism for obtaining externally managed values, such as property values and supplies convenience methods for operating on those values
+ * @return Collection of ValidationResult objects that will be added to any other validation findings - may be null
+ */
+ protected Collection<ValidationResult> customValidate(final ValidationContext context) {
+ this.scriptFile = asFile(context.getProperty(SCRIPT_FILE).evaluateAttributeExpressions().getValue()); //SCRIPT_FILE
+ this.scriptBody = context.getProperty(SCRIPT_BODY).getValue(); //SCRIPT_BODY
+ this.addClasspath = context.getProperty(ADD_CLASSPATH).evaluateAttributeExpressions().getValue(); //ADD_CLASSPATH
+ this.groovyClasspath = context.newPropertyValue(GROOVY_CLASSPATH).evaluateAttributeExpressions().getValue(); //evaluated from ${groovy.classes.path} global property
+
+ final Collection<ValidationResult> results = new HashSet<>();
+ try {
+ getGroovyScript();
+ } catch (Throwable t) {
+ results.add(new ValidationResult.Builder().subject("GroovyScript").input(this.scriptFile != null ? this.scriptFile.toString() : null).valid(false).explanation(t.toString()).build());
+ }
+ return results;
+ }
+
+ /**
+ * Hook method allowing subclasses to eagerly react to a configuration
+ * change for the given property descriptor. As an alternative to using this
+ * method a processor may simply get the latest value whenever it needs it
+ * and if necessary lazily evaluate it.
+ *
+ * @param descriptor of the modified property
+ * @param oldValue non-null property value (previous)
+ * @param newValue the new property value or if null indicates the property was removed
+ */
+ @Override
+ public void onPropertyModified(final PropertyDescriptor descriptor, final String oldValue, final String newValue) {
+ this.shell = null;
+ this.compiled = null;
+ this.scriptLastModified = 0;
+ }
+
+ /**
+ * Performs setup operations when the processor is scheduled to run. This includes evaluating the processor's
+ * properties, as well as reloading the script (from file or the "Script Body" property)
+ *
+ * @param context the context in which to perform the setup operations
+ */
+ @OnScheduled
+ public void onScheduled(final ProcessContext context) {
+ this.scriptFile = asFile(context.getProperty(SCRIPT_FILE).evaluateAttributeExpressions().getValue()); //SCRIPT_FILE
+ this.scriptBody = context.getProperty(SCRIPT_BODY).getValue(); //SCRIPT_BODY
+ this.addClasspath = context.getProperty(ADD_CLASSPATH).evaluateAttributeExpressions().getValue(); //ADD_CLASSPATH
+ this.groovyClasspath = context.newPropertyValue(GROOVY_CLASSPATH).evaluateAttributeExpressions().getValue(); //evaluated from ${groovy.classes.path} global property
+ try {
+ //compile if needed
+ getGroovyScript();
+ } catch (Throwable t) {
+ getLogger().error("Load script failed: " + t);
+ throw new ProcessException("Load script failed: " + t, t);
+ }
+ try {
+ callScriptStatic("onStart", context);
+ } catch (Throwable t) {
+ getLogger().error("onStart failed: " + t);
+ throw new ProcessException("onStart failed: " + t, t);
+ }
+ }
+
+ @OnStopped
+ public void onStopped(final ProcessContext context) {
+ try {
+ callScriptStatic("onStop", context);
+ } catch (Throwable t) {
+ throw new ProcessException("Failed to finalize groovy script:\n" + t, t);
+ }
+ //reset of compiled script not needed here because we did it onPropertyModified
+ }
+
+ // used in validation and processing
+ @SuppressWarnings("unchecked")
+ Script getGroovyScript() throws Throwable {
+ GroovyMethods.init();
+ if (scriptBody != null && scriptFile != null) {
+ throw new ProcessException("Only one parameter accepted: `" + SCRIPT_BODY.getDisplayName() + "` or `" + SCRIPT_FILE.getDisplayName() + "`");
+ }
+ if (scriptBody == null && scriptFile == null) {
+ throw new ProcessException("At least one parameter required: `" + SCRIPT_BODY.getDisplayName() + "` or `" + SCRIPT_FILE.getDisplayName() + "`");
+ }
+
+ if (shell == null) {
+ CompilerConfiguration conf = new CompilerConfiguration();
+ conf.setDebug(true);
+ shell = new GroovyShell(conf);
+ if (addClasspath != null && addClasspath.length() > 0) {
+ for (File fcp : Files.listPathsFiles(addClasspath)) {
+ if (!fcp.exists()) {
+ throw new ProcessException("Path not found `" + fcp + "` for `" + ADD_CLASSPATH.getDisplayName() + "`");
+ }
+ shell.getClassLoader().addClasspath(fcp.toString());
+ }
+ }
+ //try to add classpath with groovy classes
+ if (groovyClasspath != null && groovyClasspath.length() > 0) {
+ shell.getClassLoader().addClasspath(groovyClasspath);
+ }
+ }
+ Script script = null;
+ if (compiled != null && scriptFile != null && scriptLastModified != scriptFile.lastModified() && System.currentTimeMillis() - scriptFile.lastModified() > 3000) {
+ //force recompile if script file has been changed
+ compiled = null;
+ }
+ if (compiled == null) {
+ String scriptName;
+ String scriptText;
+ if (scriptFile != null) {
+ scriptName = scriptFile.getName();
+ scriptLastModified = scriptFile.lastModified();
+ scriptText = ResourceGroovyMethods.getText(scriptFile, "UTF-8");
+ } else {
+ scriptName = "Script" + Long.toHexString(scriptBody.hashCode()) + ".groovy";
+ scriptText = scriptBody;
+ }
+ script = shell.parse(PRELOADS + scriptText, scriptName);
+ compiled = (Class<Script>) script.getClass();
+ }
+ if (script == null) {
+ script = compiled.newInstance();
+ }
+ Thread.currentThread().setContextClassLoader(shell.getClassLoader());
+ return script;
+ }
+
+ /**
+ * init SQL variables from DBCP services
+ */
+ @SuppressWarnings("unchecked")
+ private void onInitSQL(HashMap SQL) throws SQLException {
+ for (Map.Entry e : (Set<Map.Entry>) SQL.entrySet()) {
+ DBCPService s = (DBCPService) e.getValue();
+ OSql sql = new OSql(s.getConnection());
+ //try to set autocommit to false
+ try {
+ if (sql.getConnection().getAutoCommit()) {
+ sql.getConnection().setAutoCommit(false);
+ }
+ } catch (Throwable ei) {
+ getLogger().warn("Failed to set autocommit=false for `" + e.getKey() + "`", ei);
+ }
+ e.setValue(sql);
+ }
+ }
+
+ /**
+ * before commit SQL services
+ */
+ @SuppressWarnings("unchecked")
+ private void onCommitSQL(HashMap SQL) throws SQLException {
+ for (Map.Entry e : (Set<Map.Entry>) SQL.entrySet()) {
+ OSql sql = (OSql) e.getValue();
+ if (!sql.getConnection().getAutoCommit()) {
+ sql.commit();
+ }
+ }
+ }
+
+ /**
+ * finalize SQL services. no exceptions should be thrown.
+ */
+ @SuppressWarnings("unchecked")
+ private void onFinitSQL(HashMap SQL) {
+ for (Map.Entry e : (Set<Map.Entry>) SQL.entrySet()) {
+ OSql sql = (OSql) e.getValue();
+ try {
+ if (!sql.getConnection().getAutoCommit()) {
+ sql.getConnection().setAutoCommit(true); //default autocommit value in nifi
+ }
+ } catch (Throwable ei) {
+ getLogger().warn("Failed to set autocommit=true for `" + e.getKey() + "`", ei);
+ }
+ try {
+ sql.close();
+ sql = null;
+ } catch (Throwable ei) {
+ // Nothing to do
+ }
+ }
+ }
+
+ /**
+ * exception SQL services
+ */
+ @SuppressWarnings("unchecked")
+ private void onFailSQL(HashMap SQL) {
+ for (Map.Entry e : (Set<Map.Entry>) SQL.entrySet()) {
+ OSql sql = (OSql) e.getValue();
+ try {
+ if (!sql.getConnection().getAutoCommit()) {
+ sql.rollback();
+ }
+ } catch (Throwable ei) {
+ //the rollback error is usually not important, rather it is the DML error that is really important
+ }
+ }
+ }
+
+ @Override
+ public void onTrigger(final ProcessContext context, final ProcessSession _session) throws ProcessException {
+ boolean toFailureOnError = VALID_FAIL_STRATEGY[1].equals(context.getProperty(FAIL_STRATEGY).getValue());
+ //create wrapped session to control list of newly created and files got from this session.
+ //so transfer original input to failure will be possible
+ GroovyProcessSessionWrap session = new GroovyProcessSessionWrap(_session, toFailureOnError);
+
+ HashMap CTL = new AccessMap("CTL");
+ HashMap SQL = new AccessMap("SQL");
+
+ try {
+ Script script = getGroovyScript(); //compilation must be moved to validation
+ Map bindings = script.getBinding().getVariables();
+
+ bindings.clear();
+
+ // Find the user-added properties and bind them for the script
+ for (Map.Entry<PropertyDescriptor, String> property : context.getProperties().entrySet()) {
+ if (property.getKey().isDynamic()) {
+ if (property.getKey().getName().startsWith("CTL.")) {
+ //get controller service
+ ControllerService ctl = context.getProperty(property.getKey()).asControllerService(ControllerService.class);
+ CTL.put(property.getKey().getName().substring(4), ctl);
+ } else if (property.getKey().getName().startsWith("SQL.")) {
+ DBCPService dbcp = context.getProperty(property.getKey()).asControllerService(DBCPService.class);
+ SQL.put(property.getKey().getName().substring(4), dbcp);
+ } else {
+ // Add the dynamic property bound to its full PropertyValue to the script engine
+ if (property.getValue() != null) {
+ bindings.put(property.getKey().getName(), context.getProperty(property.getKey()));
+ }
+ }
+ }
+ }
+ onInitSQL(SQL);
+
+ bindings.put("session", session);
+ bindings.put("context", context);
+ bindings.put("log", getLogger());
+ bindings.put("REL_SUCCESS", REL_SUCCESS);
+ bindings.put("REL_FAILURE", REL_FAILURE);
+ bindings.put("CTL", CTL);
+ bindings.put("SQL", SQL);
+
+ script.run();
+ bindings.clear();
+
+ onCommitSQL(SQL);
+ session.commit();
+ } catch (Throwable t) {
+ getLogger().error(t.toString(), t);
+ onFailSQL(SQL);
+ if (toFailureOnError) {
+ //transfer all received to failure with two new attributes: ERROR_MESSAGE and ERROR_STACKTRACE.
+ session.revertReceivedTo(REL_FAILURE, StackTraceUtils.deepSanitize(t));
+ } else {
+ session.rollback(true);
+ }
+ } finally {
+ onFinitSQL(SQL);
+ }
+
+ }
+
+ /**
+ * Returns a PropertyDescriptor for the given name. This is for the user to be able to define their own properties
+ * which will be available as variables in the script
+ *
+ * @param propertyDescriptorName used to lookup if any property descriptors exist for that name
+ * @return a PropertyDescriptor object corresponding to the specified dynamic property name
+ */
+ @Override
+ protected PropertyDescriptor getSupportedDynamicPropertyDescriptor(final String propertyDescriptorName) {
+ if (propertyDescriptorName.startsWith("CTL.")) {
+ return new PropertyDescriptor.Builder()
+ .name(propertyDescriptorName)
+ .required(false)
+ .description("Controller service accessible from code as `" + propertyDescriptorName + "`")
+ .dynamic(true)
+ .identifiesControllerService(ControllerService.class)
+ .build();
+ }
+ if (propertyDescriptorName.startsWith("SQL.")) {
+ return new PropertyDescriptor.Builder()
+ .name(propertyDescriptorName)
+ .required(false)
+ .description("The `groovy.sql.Sql` object created from DBCP Controller service and accessible from code as `" + propertyDescriptorName + "`")
+ .dynamic(true)
+ .identifiesControllerService(DBCPService.class)
+ .build();
+ }
+ return new PropertyDescriptor.Builder()
+ .name(propertyDescriptorName)
+ .required(false)
+ .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+ .expressionLanguageSupported(true)
+ .dynamic(true)
+ .build();
+ }
+
+ /** simple HashMap with exception on access of non-existent key */
+ private class AccessMap extends HashMap {
+ private String parentKey;
+ AccessMap(String parentKey){
+ this.parentKey=parentKey;
+ }
+ @Override
+ public Object get(Object key) {
+ if (!containsKey(key)) {
+ throw new RuntimeException("The `" + parentKey + "." + key + "` not defined in processor properties");
+ }
+ return super.get(key);
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/nifi/blob/c7a5a09b/nifi-nar-bundles/nifi-groovyx-bundle/nifi-groovyx-processors/src/main/java/org/apache/nifi/processors/groovyx/GroovyMethods.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-groovyx-bundle/nifi-groovyx-processors/src/main/java/org/apache/nifi/processors/groovyx/GroovyMethods.java b/nifi-nar-bundles/nifi-groovyx-bundle/nifi-groovyx-processors/src/main/java/org/apache/nifi/processors/groovyx/GroovyMethods.java
new file mode 100644
index 0000000..d77a193
--- /dev/null
+++ b/nifi-nar-bundles/nifi-groovyx-bundle/nifi-groovyx-processors/src/main/java/org/apache/nifi/processors/groovyx/GroovyMethods.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.processors.groovyx;
+
+import groovy.lang.DelegatingMetaClass;
+import groovy.lang.GroovySystem;
+
+import org.apache.nifi.processors.groovyx.flow.ProcessSessionWrap;
+import org.apache.nifi.processors.groovyx.flow.SessionFile;
+
+import org.apache.nifi.processor.Relationship;
+import org.apache.nifi.flowfile.FlowFile;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Class to initialize additional groovy methods to work with SessionFile, Relationship, and Sessions easier
+ */
+class GroovyMethods {
+ private static boolean initialized = false;
+
+ static void init() {
+ if (!initialized) {
+ synchronized (GroovyMethods.class) {
+ if (!initialized) {
+ initialized = metaRelationship();
+ }
+ }
+ }
+ }
+
+ private static boolean metaRelationship() {
+ GroovySystem.getMetaClassRegistry().setMetaClass(Relationship.class, new DelegatingMetaClass(Relationship.class) {
+ @Override
+ public Object invokeMethod(Object object, String methodName, Object[] args) {
+ if (object instanceof Relationship) {
+ if ("leftShift".equals(methodName) && args.length == 1) {
+ if (args[0] instanceof SessionFile) {
+ return this.leftShift((Relationship) object, (SessionFile) args[0]);
+ } else if (args[0] instanceof Collection) {
+ return this.leftShift((Relationship) object, (Collection) args[0]);
+ }
+ }
+ }
+ return super.invokeMethod(object, methodName, args);
+ }
+
+ /** to support: REL_SUCCESS << sessionFile */
+ private Relationship leftShift(Relationship r, SessionFile f) {
+ f.transfer(r);
+ return r;
+ }
+
+ /** to support: REL_SUCCESS << sessionFileCollection */
+ @SuppressWarnings("unchecked")
+ private Relationship leftShift(Relationship r, Collection sfl) {
+ if (sfl != null && sfl.size() > 0) {
+ ProcessSessionWrap session = ((SessionFile) sfl.iterator().next()).session();
+ List<FlowFile> ffl = session.unwrap(sfl);
+ //assume all files has the same session
+ session.transfer(ffl, r);
+ }
+ return r;
+ }
+
+ });
+ return true;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/nifi/blob/c7a5a09b/nifi-nar-bundles/nifi-groovyx-bundle/nifi-groovyx-processors/src/main/java/org/apache/nifi/processors/groovyx/flow/GroovyProcessSessionWrap.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-groovyx-bundle/nifi-groovyx-processors/src/main/java/org/apache/nifi/processors/groovyx/flow/GroovyProcessSessionWrap.java b/nifi-nar-bundles/nifi-groovyx-bundle/nifi-groovyx-processors/src/main/java/org/apache/nifi/processors/groovyx/flow/GroovyProcessSessionWrap.java
new file mode 100644
index 0000000..f1a139e
--- /dev/null
+++ b/nifi-nar-bundles/nifi-groovyx-bundle/nifi-groovyx-processors/src/main/java/org/apache/nifi/processors/groovyx/flow/GroovyProcessSessionWrap.java
@@ -0,0 +1,72 @@
+/*
+ * 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.processors.groovyx.flow;
+
+import org.apache.nifi.flowfile.FlowFile;
+import org.apache.nifi.processor.ProcessSession;
+import org.apache.nifi.processor.FlowFileFilter;
+
+import groovy.lang.Closure;
+
+import java.util.List;
+
+/**
+ * Wrapped session that produces groovy wrapped session-file.
+ */
+@SuppressWarnings("unused")
+public class GroovyProcessSessionWrap extends ProcessSessionWrap {
+
+ public GroovyProcessSessionWrap(ProcessSession s, boolean toFailureOnError) {
+ super(s, toFailureOnError);
+ }
+
+ /**
+ * function returns wrapped flow file with session for the simplified script access.
+ */
+ public SessionFile wrap(FlowFile f) {
+ if (f == null) {
+ return null;
+ }
+ if (f instanceof SessionFile) {
+ return ((SessionFile) f);
+ }
+ return new GroovySessionFile(this, f);
+ }
+
+ /**
+ * returns filtered list of input files. the closure receives each file from input queue and should return one of values:
+ * true - accept and continue, false - reject and continue, null - reject and stop, or any FlowFileFilterResult value.
+ */
+ public List<FlowFile> get(Closure filter) {
+ return this.get(new FlowFileFilter() {
+ @SuppressWarnings("ConstantConditions")
+ public FlowFileFilterResult filter(FlowFile flowFile) {
+ Object res = filter.call(wrap(flowFile));
+ if (res == null) {
+ return FlowFileFilterResult.REJECT_AND_TERMINATE;
+ }
+ if (res instanceof Boolean) {
+ return ((Boolean) res ? FlowFileFilterResult.ACCEPT_AND_CONTINUE : FlowFileFilterResult.REJECT_AND_CONTINUE);
+ }
+ if (res instanceof FlowFileFilterResult) {
+ return (FlowFileFilterResult) res;
+ }
+ return (org.codehaus.groovy.runtime.DefaultGroovyMethods.asBoolean(res) ? FlowFileFilterResult.ACCEPT_AND_CONTINUE : FlowFileFilterResult.REJECT_AND_CONTINUE);
+ }
+ });
+ }
+}
http://git-wip-us.apache.org/repos/asf/nifi/blob/c7a5a09b/nifi-nar-bundles/nifi-groovyx-bundle/nifi-groovyx-processors/src/main/java/org/apache/nifi/processors/groovyx/flow/GroovySessionFile.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-groovyx-bundle/nifi-groovyx-processors/src/main/java/org/apache/nifi/processors/groovyx/flow/GroovySessionFile.java b/nifi-nar-bundles/nifi-groovyx-bundle/nifi-groovyx-processors/src/main/java/org/apache/nifi/processors/groovyx/flow/GroovySessionFile.java
new file mode 100644
index 0000000..25ef2fb
--- /dev/null
+++ b/nifi-nar-bundles/nifi-groovyx-bundle/nifi-groovyx-processors/src/main/java/org/apache/nifi/processors/groovyx/flow/GroovySessionFile.java
@@ -0,0 +1,284 @@
+/*
+ * 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.processors.groovyx.flow;
+
+import org.apache.nifi.flowfile.FlowFile;
+import org.apache.nifi.processor.io.OutputStreamCallback;
+import org.apache.nifi.processor.io.StreamCallback;
+import org.apache.nifi.processor.io.InputStreamCallback;
+
+import groovy.lang.Writable;
+import groovy.lang.Closure;
+import groovy.lang.MetaClass;
+import groovy.lang.GroovyObject;
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+
+/**
+ * SessionFile with groovy specific methods.
+ */
+@SuppressWarnings("unused")
+public class GroovySessionFile extends SessionFile implements GroovyObject {
+ private transient MetaClass metaClass;
+
+ protected GroovySessionFile(ProcessSessionWrap session, FlowFile f) {
+ super(session, f);
+ setMetaClass(null); //set default meta-class
+ }
+ /*----------------------GroovyObject methods >>---------------------------*/
+
+ /**
+ * alias method to getAttribute that will act in groovy as a property except for `size` and `attributes`
+ */
+ @Override
+ public Object getProperty(String key) {
+ if ("size".equals(key)) return getSize();
+ if ("attributes".equals(key)) return getAttributes();
+ return this.getAttribute(key);
+ }
+
+ /**
+ * Calls putAttribute if value defined and removeAttribute if value is null
+ */
+ @Override
+ public void setProperty(String key, Object value) {
+ if (value == null) {
+ this.removeAttribute(key);
+ } else if (value instanceof String) {
+ this.putAttribute(key, (String) value);
+ } else {
+ this.putAttribute(key, value.toString());
+ }
+ }
+
+ /**
+ * GroovyObject support method
+ */
+ @Override
+ public MetaClass getMetaClass() {
+ return this.metaClass;
+ }
+
+ /**
+ * GroovyObject support method
+ */
+ @Override
+ public void setMetaClass(MetaClass metaClass) {
+ this.metaClass = metaClass == null ? InvokerHelper.getMetaClass(this.getClass()) : metaClass;
+ }
+
+ /**
+ * GroovyObject support method
+ */
+ @Override
+ public Object invokeMethod(String name, Object args) {
+ return this.metaClass.invokeMethod(this, name, args);
+ }
+ /*----------------------<< GroovyObject methods---------------------------*/
+
+ /*----------------------Extended Groovy methods >>------------------------*/
+
+ /**
+ * Write flow file contents through writer with defined charset.
+ *
+ * @param charset charset to use for writer
+ * @param c Closure that will receive writer as a parameter to write file content
+ * @return reference to self
+ */
+ public GroovySessionFile write(String charset, Closure c) {
+ this.write(new OutputStreamCallback() {
+ public void process(OutputStream out) throws IOException {
+ Writer w = new OutputStreamWriter(out, charset);
+ c.call(w);
+ w.flush();
+ w.close();
+ }
+ });
+ return this;
+ }
+
+ /**
+ * Instantly writes into flow file contents the char sequence (string).
+ *
+ * @param charset charset to use for writer
+ * @param c content
+ * @return reference to self
+ */
+ public GroovySessionFile write(String charset, CharSequence c) {
+ this.write(new OutputStreamCallback() {
+ public void process(OutputStream out) throws IOException {
+ Writer w = new OutputStreamWriter(out, charset);
+ w.append(c);
+ w.flush();
+ w.close();
+ }
+ });
+ return this;
+ }
+
+ /**
+ * Write flow file contents through writer with defined charset.
+ *
+ * @param charset charset to use for writer
+ * @param c content defined as writable
+ * @return reference to self
+ */
+ public GroovySessionFile write(String charset, Writable c) {
+ this.write(new OutputStreamCallback() {
+ public void process(OutputStream out) throws IOException {
+ Writer w = new OutputStreamWriter(out, charset);
+ c.writeTo(w);
+ w.flush();
+ w.close();
+ }
+ });
+ return this;
+ }
+
+ /**
+ * Write or read+write flow file contents through streams.
+ *
+ * @param c Closure that could receive one parameter OutputStream to perform write,
+ * or two parameters InputStream and OutputStream to perform read and write.
+ * @return reference to self
+ */
+ public GroovySessionFile write(Closure c) {
+ if (c.getMaximumNumberOfParameters() == 1) {
+ this.write(new OutputStreamCallback() {
+ public void process(OutputStream out) throws IOException {
+ c.call(out);
+ }
+ });
+ } else {
+ this.write(new StreamCallback() {
+ public void process(InputStream in, OutputStream out) throws IOException {
+ c.call(in, out);
+ }
+ });
+ }
+ return this;
+ }
+
+ /**
+ * Append the existing content of the flow file.
+ *
+ * @param c Closure that receives one parameter OutputStream to perform append.
+ * @return reference to self
+ */
+ public GroovySessionFile append(Closure c) {
+ this.append(new OutputStreamCallback() {
+ public void process(OutputStream out) throws IOException {
+ c.call(out);
+ }
+ });
+ return this;
+ }
+
+ /**
+ * Append the existing content of the flow file through Writer with defined charset.
+ *
+ * @param charset charset to use for writer
+ * @param c content to append.
+ * @return reference to self
+ */
+ public GroovySessionFile append(String charset, Writable c) {
+ this.append(new OutputStreamCallback() {
+ public void process(OutputStream out) throws IOException {
+ Writer w = new OutputStreamWriter(out, charset);
+ c.writeTo(w);
+ w.flush();
+ w.close();
+ }
+ });
+ return this;
+ }
+
+ /**
+ * Append the existing content of the flow file through Writer with defined charset.
+ *
+ * @param charset charset to use for writer
+ * @param c Closure with one parameter - Writer.
+ * @return reference to self
+ */
+ public GroovySessionFile append(String charset, Closure c) {
+ this.append(new OutputStreamCallback() {
+ public void process(OutputStream out) throws IOException {
+ Writer w = new OutputStreamWriter(out, charset);
+ c.call(w);
+ w.flush();
+ w.close();
+ }
+ });
+ return this;
+ }
+
+ /**
+ * Append the existing content of the flow file through Writer with defined charset.
+ *
+ * @param charset charset to use for writer
+ * @param c content to append.
+ * @return reference to self
+ */
+ public GroovySessionFile append(String charset, CharSequence c) {
+ this.append(new OutputStreamCallback() {
+ public void process(OutputStream out) throws IOException {
+ Writer w = new OutputStreamWriter(out, charset);
+ w.append(c);
+ w.flush();
+ w.close();
+ }
+ });
+ return this;
+ }
+
+ /**
+ * Reads content of the flow file and closes input stream.
+ *
+ * @param c Closure with one parameter InputStream.
+ */
+ public void read(Closure c) {
+ this.read(new InputStreamCallback() {
+ public void process(InputStream in) throws IOException {
+ c.call(in);
+ }
+ });
+ }
+
+ /**
+ * Reads content of the flow file through Reader and closes the stream.
+ *
+ * @param charset charset to use for Reader
+ * @param c Closure with one parameter Reader.
+ */
+ public void read(String charset, Closure c) {
+ this.read(new InputStreamCallback() {
+ public void process(InputStream in) throws IOException {
+ InputStreamReader r = new InputStreamReader(in, charset);
+ c.call(r);
+ r.close();
+ }
+ });
+ }
+
+}