NIFI-270 sent an e-mail to dev explaining all the moving parts

This commit is contained in:
joewitt 2015-01-17 03:18:50 -05:00
parent 6f189266b4
commit bc94f716c6
155 changed files with 617 additions and 4493 deletions

View File

@ -4,35 +4,15 @@ Apache NiFi is a dataflow system based on the concepts of flow-based programming
## Table of Contents ## Table of Contents
- [Features](#features)
- [Getting Started](#getting-started) - [Getting Started](#getting-started)
- [Getting Help](#getting-help)
- [Requirements](#requirements)
- [License](#license) - [License](#license)
- [Disclaimer](#disclaimer) - [Disclaimer](#disclaimer)
## Features
Apache NiFi supports powerful and scalable directed graphs of data routing, transformation, and system mediation logic. Some of the high-level capabilities and objectives of Apache NiFi include:
- Web-based user interface for seamless experience between design, control, feedback, and monitoring of data flows
- Highly configurable along several dimensions of quality of service such as loss tolerant versus guaranteed delivery, low latency versus high throughput, and priority based queuing
- Fine-grained data provenance for all data received, forked, joined, cloned, modified, sent, and ultimately dropped as data reaches its configured end-state
- Component-based extension model along well defined interfaces enabling rapid development and effective testing
## Getting Started ## Getting Started
Execute <nifi install dir>/bin/nifi.sh start - Build the nar-maven-plugin. Change directory to 'nar-maven-plugin' and
follow the directions found there.
## Getting Help - Build nifi. Change directory to 'nifi' and follow the directions found there.
If you have questions, you can reach out to our mailing list: dev@nifi.incubator.apache.org
([archive](http://mail-archives.apache.org/mod_mbox/incubator-nifi-dev)).
We're also often available in IRC: #nifi on
[irc.freenode.net](http://webchat.freenode.net/?channels=#nifi).
## Requirements
* JDK 1.7 or higher
## License ## License
@ -64,32 +44,3 @@ While incubation status is not necessarily a reflection of the completeness
or stability of the code, it does indicate that the project has yet to be or stability of the code, it does indicate that the project has yet to be
fully endorsed by the ASF. fully endorsed by the ASF.
## Export Control
This distribution includes cryptographic software. The country in which you
currently reside may have restrictions on the import, possession, use, and/or
re-export to another country, of encryption software. BEFORE using any
encryption software, please check your country's laws, regulations and
policies concerning the import, possession, or use, and re-export of encryption
software, to see if this is permitted. See <http://www.wassenaar.org/> for more
information.
The U.S. Government Department of Commerce, Bureau of Industry and Security
(BIS), has classified this software as Export Commodity Control Number (ECCN)
5D002.C.1, which includes information security software using or performing
cryptographic functions with asymmetric algorithms. The form and manner of this
Apache Software Foundation distribution makes it eligible for export under the
License Exception ENC Technology Software Unrestricted (TSU) exception (see the
BIS Export Administration Regulations, Section 740.13) for both object code and
source code.
The following provides more details on the included cryptographic software:
Apache NiFi uses BouncyCastle, Jasypt, JCraft Inc., and the built-in
java cryptography libraries for SSL, SSH, and the protection
of sensitive configuration parameters. See
http://bouncycastle.org/about.html
http://www.jasypt.org/faq.html
http://jcraft.com/c-info.html
http://www.oracle.com/us/products/export/export-regulations-345813.html
for more details on each of these libraries cryptography features.

202
nar-maven-plugin/LICENSE Normal file
View File

@ -0,0 +1,202 @@
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.

5
nar-maven-plugin/NOTICE Normal file
View File

@ -0,0 +1,5 @@
Apache NiFi
Copyright 2014-2015 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).

View File

@ -0,0 +1,57 @@
# Apache NiFi NAR Maven Plugin
Apache NiFi NAR Maven Plugin helps to build NiFi Archive bundles to support the classloader isolation model of NiFi.
## Table of Contents
- [Getting Started](#getting-started)
- [Getting Help](#getting-help)
- [Requirements](#requirements)
- [License](#license)
- [Disclaimer](#disclaimer)
## Getting Started
To Build:
- Executed 'mvn clean install'
## Getting Help
If you have questions, you can reach out to our mailing list: dev@nifi.incubator.apache.org
([archive](http://mail-archives.apache.org/mod_mbox/incubator-nifi-dev)).
We're also often available in IRC: #nifi on
[irc.freenode.net](http://webchat.freenode.net/?channels=#nifi).
## Requirements
* JDK 1.7 or higher
## License
Except as otherwise noted this software is licensed under the
[Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html)
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.
## Disclaimer
Apache NiFi is an effort undergoing incubation at the Apache Software
Foundation (ASF), sponsored by the Apache Incubator PMC.
Incubation is required of all newly accepted projects until a further review
indicates that the infrastructure, communications, and decision making process
have stabilized in a manner consistent with other successful ASF projects.
While incubation status is not necessarily a reflection of the completeness
or stability of the code, it does indicate that the project has yet to be
fully endorsed by the ASF.

15
nifi/DISCLAIMER Normal file
View File

@ -0,0 +1,15 @@
Apache NiFi is an effort undergoing incubation at the Apache Software
Foundation (ASF), sponsored by the Apache Incubator PMC.
Incubation is required of all newly accepted projects until a further review
indicates that the infrastructure, communications, and decision making process
have stabilized in a manner consistent with other successful ASF projects.
While incubation status is not necessarily a reflection of the completeness
or stability of the code, it does indicate that the project has yet to be
fully endorsed by the ASF.
For more information about the incubation status of the Apache NiFi project
you can go to the following page:
http://nifi.incubator.apache.org/

View File

@ -1,5 +1,5 @@
Apache NiFi Apache NiFi
Copyright 2014 The Apache Software Foundation Copyright 2014-2015 The Apache Software Foundation
This product includes software developed at This product includes software developed at
The Apache Software Foundation (http://www.apache.org/). The Apache Software Foundation (http://www.apache.org/).

101
nifi/README.md Normal file
View File

@ -0,0 +1,101 @@
# Apache NiFi
Apache NiFi is a dataflow system based on the concepts of flow-based programming. It is currently apart of the Apache Incubator.
## Table of Contents
- [Features](#features)
- [Getting Started](#getting-started)
- [Getting Help](#getting-help)
- [Requirements](#requirements)
- [License](#license)
- [Disclaimer](#disclaimer)
- [Export Control] (#export-control)
## Features
Apache NiFi supports powerful and scalable directed graphs of data routing, transformation, and system mediation logic. Some of the high-level capabilities and objectives of Apache NiFi include:
- Web-based user interface for seamless experience between design, control, feedback, and monitoring of data flows
- Highly configurable along several dimensions of quality of service such as loss tolerant versus guaranteed delivery, low latency versus high throughput, and priority based queuing
- Fine-grained data provenance for all data received, forked, joined, cloned, modified, sent, and ultimately dropped as data reaches its configured end-state
- Component-based extension model along well defined interfaces enabling rapid development and effective testing
## Getting Started
To build:
- Execute 'mvn clean install' or for parallel build execute 'mvn -T 2.0C clean install'
To start NiFi:
- Change directory to 'assembly'. In the target directory there should be a build of nifi.
- Unpack the build wherever you like or use the already unpacked build. '<install_location>/bin/nifi.sh start'
- Direct your browser to http://localhost:8080/nifi/
## Getting Help
If you have questions, you can reach out to our mailing list: dev@nifi.incubator.apache.org
([archive](http://mail-archives.apache.org/mod_mbox/incubator-nifi-dev)).
We're also often available in IRC: #nifi on
[irc.freenode.net](http://webchat.freenode.net/?channels=#nifi).
## Requirements
* JDK 1.7 or higher
## License
Except as otherwise noted this software is licensed under the
[Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html)
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.
## Disclaimer
Apache NiFi is an effort undergoing incubation at the Apache Software
Foundation (ASF), sponsored by the Apache Incubator PMC.
Incubation is required of all newly accepted projects until a further review
indicates that the infrastructure, communications, and decision making process
have stabilized in a manner consistent with other successful ASF projects.
While incubation status is not necessarily a reflection of the completeness
or stability of the code, it does indicate that the project has yet to be
fully endorsed by the ASF.
## Export Control
This distribution includes cryptographic software. The country in which you
currently reside may have restrictions on the import, possession, use, and/or
re-export to another country, of encryption software. BEFORE using any
encryption software, please check your country's laws, regulations and
policies concerning the import, possession, or use, and re-export of encryption
software, to see if this is permitted. See <http://www.wassenaar.org/> for more
information.
The U.S. Government Department of Commerce, Bureau of Industry and Security
(BIS), has classified this software as Export Commodity Control Number (ECCN)
5D002.C.1, which includes information security software using or performing
cryptographic functions with asymmetric algorithms. The form and manner of this
Apache Software Foundation distribution makes it eligible for export under the
License Exception ENC Technology Software Unrestricted (TSU) exception (see the
BIS Export Administration Regulations, Section 740.13) for both object code and
source code.
The following provides more details on the included cryptographic software:
Apache NiFi uses BouncyCastle, Jasypt, JCraft Inc., and the built-in
java cryptography libraries for SSL, SSH, and the protection
of sensitive configuration parameters. See
http://bouncycastle.org/about.html
http://www.jasypt.org/faq.html
http://jcraft.com/c-info.html
http://www.oracle.com/us/products/export/export-regulations-345813.html
for more details on each of these libraries cryptography features.

View File

@ -18,10 +18,10 @@
<parent> <parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>nifi-parent</artifactId> <artifactId>nifi-parent</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
</parent> </parent>
<artifactId>nifi</artifactId> <artifactId>nifi</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<name>NiFi Release</name> <name>NiFi Release</name>
<description>This is the assembly Apache NiFi (incubating)</description> <description>This is the assembly Apache NiFi (incubating)</description>
@ -385,16 +385,16 @@
<directory>/opt/nifi/nifi-${project.version}</directory> <directory>/opt/nifi/nifi-${project.version}</directory>
<sources> <sources>
<source> <source>
<location>../../LICENSE</location> <location>../LICENSE</location>
</source> </source>
<source> <source>
<location>../../NOTICE</location> <location>../NOTICE</location>
</source> </source>
<source> <source>
<location>../../DISCLAIMER</location> <location>../DISCLAIMER</location>
</source> </source>
<source> <source>
<location>../../README.md</location> <location>../README.md</location>
<destination>README</destination> <destination>README</destination>
</source> </source>
</sources> </sources>

View File

@ -109,28 +109,28 @@
</dependencySets> </dependencySets>
<files> <files>
<file> <file>
<source>../../README.md</source> <source>../README.md</source>
<outputDirectory>./</outputDirectory> <outputDirectory>./</outputDirectory>
<destName>README</destName> <destName>README</destName>
<fileMode>0644</fileMode> <fileMode>0644</fileMode>
<filtered>true</filtered> <filtered>true</filtered>
</file> </file>
<file> <file>
<source>../../DISCLAIMER</source> <source>../DISCLAIMER</source>
<outputDirectory>./</outputDirectory> <outputDirectory>./</outputDirectory>
<destName>DISCLAIMER</destName> <destName>DISCLAIMER</destName>
<fileMode>0644</fileMode> <fileMode>0644</fileMode>
<filtered>true</filtered> <filtered>true</filtered>
</file> </file>
<file> <file>
<source>../../LICENSE</source> <source>../LICENSE</source>
<outputDirectory>./</outputDirectory> <outputDirectory>./</outputDirectory>
<destName>LICENSE</destName> <destName>LICENSE</destName>
<fileMode>0644</fileMode> <fileMode>0644</fileMode>
<filtered>true</filtered> <filtered>true</filtered>
</file> </file>
<file> <file>
<source>../../NOTICE</source> <source>../NOTICE</source>
<outputDirectory>./</outputDirectory> <outputDirectory>./</outputDirectory>
<destName>NOTICE</destName> <destName>NOTICE</destName>
<fileMode>0644</fileMode> <fileMode>0644</fileMode>

View File

@ -18,11 +18,11 @@
<parent> <parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>nifi-commons-parent</artifactId> <artifactId>nifi-commons-parent</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
</parent> </parent>
<artifactId>data-provenance-utils</artifactId> <artifactId>data-provenance-utils</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>data-provenance-utils</name> <name>data-provenance-utils</name>

View File

@ -18,11 +18,11 @@
<parent> <parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>nifi-commons-parent</artifactId> <artifactId>nifi-commons-parent</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
</parent> </parent>
<artifactId>flowfile-packager</artifactId> <artifactId>flowfile-packager</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>FlowFile Packager</name> <name>FlowFile Packager</name>

View File

@ -18,11 +18,11 @@
<parent> <parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>nifi-commons-parent</artifactId> <artifactId>nifi-commons-parent</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
</parent> </parent>
<artifactId>nifi-expression-language</artifactId> <artifactId>nifi-expression-language</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>NiFi Expression Language</name> <name>NiFi Expression Language</name>

View File

@ -19,11 +19,11 @@
<parent> <parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>nifi-commons-parent</artifactId> <artifactId>nifi-commons-parent</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
</parent> </parent>
<artifactId>nifi-logging-utils</artifactId> <artifactId>nifi-logging-utils</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
<name>NiFi Logging Utils</name> <name>NiFi Logging Utils</name>
<description>Utilities for logging</description> <description>Utilities for logging</description>

View File

@ -19,11 +19,11 @@
<parent> <parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>nifi-commons-parent</artifactId> <artifactId>nifi-commons-parent</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
</parent> </parent>
<artifactId>nifi-properties</artifactId> <artifactId>nifi-properties</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
<name>NiFi Properties</name> <name>NiFi Properties</name>
</project> </project>

View File

@ -18,11 +18,11 @@
<parent> <parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>nifi-commons-parent</artifactId> <artifactId>nifi-commons-parent</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
</parent> </parent>
<artifactId>nifi-security-utils</artifactId> <artifactId>nifi-security-utils</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
<name>NiFi Security Utils</name> <name>NiFi Security Utils</name>
<description>Contains security functionality.</description> <description>Contains security functionality.</description>

View File

@ -19,11 +19,11 @@
<parent> <parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>nifi-commons-parent</artifactId> <artifactId>nifi-commons-parent</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
</parent> </parent>
<artifactId>nifi-socket-utils</artifactId> <artifactId>nifi-socket-utils</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
<name>NiFi Socket Utils</name> <name>NiFi Socket Utils</name>
<description>Utilities for socket communication</description> <description>Utilities for socket communication</description>

View File

@ -19,11 +19,11 @@
<parent> <parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>nifi-commons-parent</artifactId> <artifactId>nifi-commons-parent</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
</parent> </parent>
<artifactId>nifi-utils</artifactId> <artifactId>nifi-utils</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>NiFi Utils</name> <name>NiFi Utils</name>
<!-- <!--

View File

@ -19,11 +19,11 @@
<parent> <parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>nifi-commons-parent</artifactId> <artifactId>nifi-commons-parent</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
</parent> </parent>
<artifactId>nifi-web-utils</artifactId> <artifactId>nifi-web-utils</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
<name>NiFi Web Utils</name> <name>NiFi Web Utils</name>
<dependencies> <dependencies>

View File

@ -20,7 +20,7 @@
<parent> <parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>nifi-parent</artifactId> <artifactId>nifi-parent</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
</parent> </parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>

View File

@ -19,11 +19,11 @@
<parent> <parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>nifi-commons-parent</artifactId> <artifactId>nifi-commons-parent</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
</parent> </parent>
<artifactId>nifi-processor-utils</artifactId> <artifactId>nifi-processor-utils</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>NiFi Processor Utils</name> <name>NiFi Processor Utils</name>

View File

@ -19,11 +19,11 @@
<parent> <parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>nifi-commons-parent</artifactId> <artifactId>nifi-commons-parent</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
</parent> </parent>
<artifactId>wali</artifactId> <artifactId>wali</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>WALI : Write-Ahead Log Implementation</name> <name>WALI : Write-Ahead Log Implementation</name>

View File

@ -1,81 +0,0 @@
<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/maven-v4_0_0.xsd">
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.nifi</groupId>
<artifactId>execute-script-bundle</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>execute-script-processors</artifactId>
<description>NiFi Processors to Run Scripts</description>
<name>NiFi Script Execution Processors</name>
<dependencies>
<dependency>
<groupId>org.jruby</groupId>
<artifactId>jruby</artifactId>
<exclusions>
<exclusion>
<artifactId>jnr-netdb</artifactId>
<groupId>com.github.jnr</groupId>
</exclusion>
<exclusion>
<artifactId>jnr-posix</artifactId>
<groupId>com.github.jnr</groupId>
</exclusion>
<exclusion>
<artifactId>jffi</artifactId>
<groupId>com.github.jnr</groupId>
</exclusion>
<exclusion>
<artifactId>nailgun-server</artifactId>
<groupId>com.martiansoftware</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.python</groupId>
<artifactId>jython-standalone</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-mock</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-processor-utils</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-core-flowfile-attributes</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-stream-utils</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -1,566 +0,0 @@
/*
* 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.script;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import javax.script.ScriptException;
import org.apache.nifi.components.PropertyDescriptor;
import org.apache.nifi.components.ValidationContext;
import org.apache.nifi.components.ValidationResult;
import org.apache.nifi.components.Validator;
import org.apache.nifi.flowfile.FlowFile;
import org.apache.nifi.io.BufferedInputStream;
import org.apache.nifi.io.BufferedOutputStream;
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.annotation.CapabilityDescription;
import org.apache.nifi.processor.annotation.EventDriven;
import org.apache.nifi.processor.annotation.Tags;
import org.apache.nifi.processor.exception.ProcessException;
import org.apache.nifi.processor.io.InputStreamCallback;
import org.apache.nifi.processor.io.StreamCallback;
import org.apache.nifi.processor.util.StandardValidators;
import org.apache.nifi.scripting.ConverterScript;
import org.apache.nifi.scripting.ReaderScript;
import org.apache.nifi.scripting.Script;
import org.apache.nifi.scripting.ScriptFactory;
import org.apache.nifi.scripting.WriterScript;
/**
* <!-- Processor Documentation ================================================== -->
* <h2>Description:</h2>
* <p>
* This processor provides the capability to execute scripts in various
* scripting languages, and passes into the scripts the input stream and output
* stream(s) representing an incoming flow file and any created flow files. The
* processor is designed to be thread safe, so multiple concurrent tasks may
* execute against a single script. The processor provides a framework which
* enables script writers to implement 3 different types of scripts:
* <ul>
* ReaderScript - which enables stream-based reading of a FlowFile's
* content</br> WriterScript - which enables stream-based reading and
* writing/modifying of a FlowFile's content</br> ConverterScript - which
* enables stream-based reading a FlowFile's content and stream-based writing to
* newly created FlowFiles</br>
* </ul>
* Presently, the processor supports 3 scripting languages: Ruby, Python, and
* JavaScript. The processor is built on the javax.script API which enables
* ScriptEngine discovery, thread management, and encapsulates much of the low
* level bridging-code that enables Java to Script language integration. Thus,
* it is designed to be easily extended to other scripting languages. </br> The
* attributes of a FlowFile and properties of the Processor are exposed to the
* script by either a variable in the base class or a getter method. A script
* may declare new Processor Properties and different Relationships via
* overriding the getPropertyDescriptors and getRelationships methods,
* respectively.
* </p>
* <p>
* <strong>Properties:</strong>
* </p>
* <p>
* In the list below, the names of required properties appear in bold. Any other
* properties (not in bold) are considered optional. If a property has a default
* value, it is indicated. If a property supports the use of the NiFi Expression
* Language (or simply, "expression language"), that is also indicated. Of
* particular note: This processor allows scripts to define additional Processor
* properties, which will not be initially visible. Once the processor's
* configuration is validated, script defined properties will become visible,
* and may affect the validity of the processor.
* </p>
* <ul>
* <li>
* <strong>Script File Name</strong>
* <ul>
* <li>Script location, can be relative or absolute path.</li>
* <li>Default value: no default</li>
* <li>Supports expression language: false</li>
* </ul>
* </li>
* <li>
* <strong>Script Check Interval</strong>
* <ul>
* <li>The time period between checking for updates to a script.</li>
* <li>Default value: 15 sec</li>
* <li>Supports expression language: false</li>
* </ul>
* </li>
* </ul>
*
* <p>
* <strong>Relationships:</strong>
* </p>
* <p>
* The initial 'out of the box' relationships are below. Of particular note is
* the ability of a script to change the set of relationships. However, any
* relationships defined by the script will not be visible until the processor's
* configuration has been validated. Once done, new relationships will become
* visible.
* </p>
* <ul>
* <li>
* success
* <ul>
* <li>Used when a file is successfully processed by a script.</li>
* </ul>
* </li>
* <li>
* failure
* <ul>
* <li>Used when an error occurs while processing a file with a script.</li>
* </ul>
* </li>
* </ul>
*
* <p>
* <strong>Example Scripts:</strong>
* </p>
* <ul>
* JavaScript example - the 'with' statement imports packages defined in the
* framework. Since the 'instance' variable is intended to be local scope (not
* global), it must be named 'instance' as it it not passed back to the
* processor upon script evaluation and must be fetched. If you make it global,
* you can name it whatever you'd like...but this is intended to be
* multi-threaded so do so at your own risk. Presently, there are issues with
* the JavaScript scripting engine that prevent sub-classing the base classes in
* the Processor's Java framework. So, what is actually happening is an instance
* of the ReaderScript is created with a provided callback object. When we are
* able to move to a more competent scripting engine, the code below will remain
* the same, but the 'instance' variable will actually be a sub-class of
* ReaderScript.
*
* <pre>
* with (Scripting) {
* var instance = new ReaderScript({
* route : function(input) {
* var str = IOUtils.toString(input);
* var expr = instance.getProperty("expr");
* filename = instance.attributes.get("filename");
* instance.setAttribute("filename", filename + ".modified");
* if (str.match(expr)) {
* return Script.FAIL_RELATIONSHIP;
* } else {
* return Script.SUCCESS_RELATIONSHIP;
* }
* }
* });
* }
* </pre>
*
* Ruby example - the 'OutputStreamHandler' is an interface which is called when
* creating flow files.
*
* <pre>
* java_import 'org.apache.nifi.scripting.OutputStreamHandler'
* class SimpleConverter < ConverterScript
* field_reader :FAIL_RELATIONSHIP, :SUCCESS_RELATIONSHIP, :logger, :attributes
*
* def convert(input)
* in_io = input.to_io
* createFlowFile("firstLine", FAIL_RELATIONSHIP, OutputStreamHandler.impl do |method, out|
* out_io = out.to_io
* out_io << in_io.readline.to_java_bytes
* out_io.close
* logger.debug("Wrote data to failure...this message logged with logger from super class")
* end)
*
* createFlowFile("otherLines", SUCCESS_RELATIONSHIP, OutputStreamHandler.impl do |method, out|
* out_io = out.to_io
* in_io.each_line { |line|
* out_io << line
* }
* out_io.close
* logger.debug("Wrote data to success...this message logged with logger from super class")
* end)
* in_io.close
* end
*
* end
*
* $logger.debug("Creating SimpleConverter...this message logged with logger from shared variables")
* SimpleConverter.new
* </pre>
*
* Python example - The difficulty with Python is that it does not return
* objects upon script evaluation, so the instance of the Script class must be
* fetched by name. Thus, you must define a variable called 'instance'.
*
* <pre>
* import re
*
* class RoutingReader(ReaderScript):
* A = Relationship.Builder().name("a").description("some good stuff").build()
* B = Relationship.Builder().name("b").description("some other stuff").build()
* C = Relationship.Builder().name("c").description("some bad stuff").build()
*
* def getRelationships(self):
* return [self.A,self.B,self.C]
*
* def getExceptionRoute(self):
* return self.C
*
* def route( self, input ):
* for line in FileUtil.wrap(input):
* if re.match("^bad", line, re.IGNORECASE):
* return self.B
* if re.match("^sed", line):
* raise RuntimeError("That's no good!")
*
* return self.A
*
* instance = RoutingReader()
* </pre>
*
* </ul>
* <p>
* <strong>Shared Variables</strong>
* </p>
* <ul>
* <li>logger : global scope</li>
* <li>properties : local/instance scope</li>
* </ul>
* <p>
* <strong>Script API:</strong>
* </p>
* <ul>
* <li>getAttribute(String) : String</li>
* <li>getAttributes() : Map(String,String)</li>
* <li>getExceptionRoute() : Relationship</li>
* <li>getFileName() : String</li>
* <li>getFlowFileEntryDate() : Calendar</li>
* <li>getFlowFileSize() : long</li>
* <li>getProperties() : Map(String, String)</li>
* <li>getProperty(String) : String</li>
* <li>getPropertyDescriptors() : List(PropertyDescriptor)</li>
* <li>getRelationships() : Collection(Relationship)</li>
* <li>getRoute() : Relationship</li>
* <li>setRoute(Relationship)</li>
* <li>setAttribute(String, String)</li>
* <li>validate() : Collection(String)</li>
* </ul>
* <p>
* <strong>ReaderScript API:</strong>
* </p>
* <ul>
* <li>route(InputStream) : Relationship</li>
* </ul>
* <p>
* <strong>WriterScript API:</strong>
* </p>
* <ul>
* <li>process(InputStream, OutputStream)</li>
* </ul>
* <p>
* <strong>ConverterScript API:</strong>
* </p>
* <ul>
* <li>convert(InputStream)</li>
* <li>createFlowFile(String, Relationship, OutputStreamHandler)</li>
* </ul>
* <p>
* <strong>OutputStreamHandler API:</strong>
* </p>
* <ul>
* <li>write(OutputStream)</li>
* </ul>
*/
@EventDriven
@Tags({"script", "ruby", "python", "javascript", "execute"})
@CapabilityDescription("Execute scripts in various scripting languages, and passes into the scripts the input stream and output stream(s) "
+ "representing an incoming flow file and any created flow files.")
public class ExecuteScript extends AbstractProcessor {
private final AtomicBoolean doCustomValidate = new AtomicBoolean(true);
private final AtomicReference<Set<Relationship>> relationships = new AtomicReference<>();
private final AtomicReference<List<PropertyDescriptor>> propertyDescriptors = new AtomicReference<>();
private volatile ScriptFactory scriptFactory;
private volatile Relationship exceptionRoute;
/**
* Script location, can be relative or absolute path -- passed as-is to
* {@link File#File(String) File constructor}
*/
public static final PropertyDescriptor SCRIPT_FILE_NAME = new PropertyDescriptor.Builder()
.name("Script File Name")
.description("Script location, can be relative or absolute path")
.required(true)
.addValidator(new Validator() {
@Override
public ValidationResult validate(String subject, String input, ValidationContext context) {
ValidationResult result = StandardValidators.FILE_EXISTS_VALIDATOR.validate(subject, input, context);
if (result.isValid()) {
int dotPos = input.lastIndexOf('.');
if (dotPos < 1) {
result = new ValidationResult.Builder()
.subject(subject)
.valid(false)
.explanation("Filename must have an extension")
.input(input)
.build();
}
}
return result;
}
})
.build();
static final PropertyDescriptor SCRIPT_CHECK_INTERVAL = new PropertyDescriptor.Builder()
.name("Script Check Interval")
.addValidator(StandardValidators.TIME_PERIOD_VALIDATOR)
.description("The time period between checking for updates to a script")
.required(true)
.defaultValue("15 sec")
.build();
@Override
protected void init(ProcessorInitializationContext context) {
Set<Relationship> empty = Collections.emptySet();
relationships.set(empty);
ArrayList<PropertyDescriptor> propDescs = new ArrayList<>();
propDescs.add(SCRIPT_FILE_NAME);
propDescs.add(SCRIPT_CHECK_INTERVAL);
propertyDescriptors.set(Collections.unmodifiableList(propDescs));
scriptFactory = new ScriptFactory(getLogger());
}
@Override
public List<PropertyDescriptor> getSupportedPropertyDescriptors() {
return propertyDescriptors.get();
}
@Override
protected PropertyDescriptor getSupportedDynamicPropertyDescriptor(String propertyDescriptorName) {
return new PropertyDescriptor.Builder()
.name(propertyDescriptorName)
.dynamic(true)
.addValidator(Validator.VALID)
.build();
}
@Override
public void onPropertyModified(PropertyDescriptor descriptor, String oldValue, String newValue) {
doCustomValidate.set(true);
}
@Override
public Set<Relationship> getRelationships() {
return relationships.get();
}
/**
* Called by framework.
*
* Returns a list of reasons why this processor cannot be run.
* @return
*/
@Override
protected Collection<ValidationResult> customValidate(ValidationContext validationContext) {
if (doCustomValidate.getAndSet(false)) {
long interval = validationContext.getProperty(SCRIPT_CHECK_INTERVAL).asTimePeriod(TimeUnit.MILLISECONDS);
scriptFactory.setScriptCheckIntervalMS(interval);
List<ValidationResult> results = new ArrayList<>();
String file = validationContext.getProperty(SCRIPT_FILE_NAME).getValue();
try {
Script s = scriptFactory.getScript(file);
// set the relationships of the processor
relationships.set(new HashSet<>(s.getRelationships()));
// need to get script's prop. descs. and validate. May, or may not, have dynamic
// props already...depends if this is the first time the processor is being configured.
Map<PropertyDescriptor, String> properties = validationContext.getProperties();
// need to compare props, if any, against script-expected props that are required.
// script may be expecting required props that are not known, or some props may have invalid
// values.
// processor may be configured with dynamic props that the script will use...but does not declare which would
// be a bad thing
List<PropertyDescriptor> scriptPropDescs = s.getPropertyDescriptors();
getLogger().debug("Script is {}", new Object[]{s});
getLogger().debug("Script file name is {}", new Object[]{s.getFileName()});
getLogger().debug("Script Prop Descs are: {}", new Object[]{scriptPropDescs.toString()});
getLogger().debug("Thread is: {}", new Object[]{Thread.currentThread().toString()});
for (PropertyDescriptor propDesc : scriptPropDescs) {
// need to check for missing props
if (propDesc.isRequired() && !properties.containsKey(propDesc)) {
results.add(new ValidationResult.Builder()
.subject("Script Properties")
.valid(false)
.explanation("Missing Property " + propDesc.getName())
.build());
// need to validate current value against script provided validator
} else if (properties.containsKey(propDesc)) {
String value = properties.get(propDesc);
ValidationResult result = propDesc.validate(value, validationContext);
if (!result.isValid()) {
results.add(result);
}
} // else it is an optional prop according to the script and it is not specified by
// the configuration of the processor
}
// need to update the known prop desc's with what we just got from the script
List<PropertyDescriptor> pds = new ArrayList<>(propertyDescriptors.get());
pds.addAll(scriptPropDescs);
propertyDescriptors.set(Collections.unmodifiableList(pds));
if (results.isEmpty()) {
// so needed props are supplied and individually validated, now validate script
Collection<String> reasons;
reasons = s.validate();
if (null == reasons) {
getLogger().warn("Script had invalid return value for validate(), ignoring.");
} else {
for (String reason : reasons) {
ValidationResult result = new ValidationResult.Builder()
.subject("ScriptValidation")
.valid(false)
.explanation(reason)
.build();
results.add(result);
}
}
}
// get the exception route
exceptionRoute = s.getExceptionRoute();
return results;
} catch (ScriptException | IOException | NoSuchMethodException e) {
doCustomValidate.set(true);
results.add(new ValidationResult.Builder()
.subject("ScriptValidation")
.valid(false)
.explanation("Cannot create script due to " + e.getMessage())
.input(file)
.build());
getLogger().error("Cannot create script due to " + e, e);
return results;
}
}
return null;
}
@Override
public void onTrigger(ProcessContext context, ProcessSession session) throws ProcessException {
FlowFile flowFile = session.get();
if (flowFile == null) {
return; // fail-fast if there is no work to do
}
final String scriptFileName = context.getProperty(SCRIPT_FILE_NAME).getValue();
// doing this cloning because getProperties does not initialize props that have only their default values
// must do a getProperty for that value to be initialized
Map<String, String> props = new HashMap<>();
for (PropertyDescriptor propDesc : context.getProperties().keySet()) {
if (propDesc.isExpressionLanguageSupported()) {
props.put(propDesc.getName(), context.getProperty(propDesc).evaluateAttributeExpressions(flowFile).getValue());
} else {
props.put(propDesc.getName(), context.getProperty(propDesc).getValue());
}
}
Script script = null;
try {
final Script finalScript = scriptFactory.getScript(scriptFileName, props, flowFile);
script = finalScript;
if (finalScript instanceof ReaderScript) {
session.read(flowFile, new InputStreamCallback() {
@Override
public void process(InputStream in) throws IOException {
try {
((ReaderScript) finalScript).process(new BufferedInputStream(in));
} catch (NoSuchMethodException | ScriptException e) {
getLogger().error("Failed to execute ReaderScript", e);
throw new IOException(e);
}
}
});
} else if (finalScript instanceof WriterScript) {
flowFile = session.write(flowFile, new StreamCallback() {
@Override
public void process(InputStream in, OutputStream out) throws IOException {
try {
((WriterScript) finalScript).process(new BufferedInputStream(in), new BufferedOutputStream(out));
out.flush();
} catch (NoSuchMethodException | ScriptException e) {
getLogger().error("Failed to execute WriterScript", e);
throw new IOException(e);
}
}
});
} else if (finalScript instanceof ConverterScript) {
((ConverterScript) finalScript).process(session);
// Note that these scripts don't pass the incoming FF through,
// they always create new outputs
session.remove(flowFile);
return;
} else {
// only thing we can do is assume script has already run and done it's thing, so just transfer the incoming
// flowfile
getLogger().debug("Successfully executed script from {}", new Object[]{scriptFileName});
}
// update flow file attributes
flowFile = session.putAllAttributes(flowFile, finalScript.getAttributes());
Relationship route = finalScript.getRoute();
if (null == route) {
session.remove(flowFile);
getLogger().info("Removing flowfile {}", new Object[]{flowFile});
} else {
session.transfer(flowFile, route);
getLogger().info("Transferring flowfile {} to {}", new Object[]{flowFile, route});
}
} catch (ScriptException | IOException e) {
getLogger().error("Failed to create script from {} with flowFile {}. Rolling back session.",
new Object[]{scriptFileName, flowFile}, e);
throw new ProcessException(e);
} catch (Exception e) {
if (null != script) {
getLogger().error("Failed to execute script from {}. Transferring flow file {} to {}",
new Object[]{scriptFileName, flowFile, exceptionRoute}, e);
session.transfer(flowFile, exceptionRoute);
} else {
getLogger().error("Failed to execute script from {} with flowFile {}. Rolling back session",
new Object[]{scriptFileName, flowFile}, e);
throw new ProcessException(e);
}
}
}
}

View File

@ -1,131 +0,0 @@
/*
* 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.scripting;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Map;
import javax.script.Invocable;
import javax.script.ScriptException;
import org.apache.nifi.flowfile.FlowFile;
import org.apache.nifi.flowfile.attributes.CoreAttributes;
import org.apache.nifi.io.BufferedInputStream;
import org.apache.nifi.processor.ProcessSession;
import org.apache.nifi.processor.Relationship;
import org.apache.nifi.processor.io.InputStreamCallback;
import org.apache.nifi.processor.io.OutputStreamCallback;
/**
* <p>
* Script authors should extend this class if they want to perform complex
* conversions in a NiFi processor.
* </p>
*
* <p>
* Scripts must implement {@link #convert(FileInputStream)}. This method may
* create new FlowFiles and pass them to one or more routes. The input FlowFile
* will be removed from the repository after execution of this method completes.
* </p>
*
* <p>
* In general, the {@link #convert(FileInputStream)} will read from the supplied
* stream, then create one or more output sinks and route the result to the
* relationship of choice using
* {@link #routeStream(ByteArrayOutputStream, String, String)} or
* {@link #routeBytes(byte[], String, String)}.
*
* <p>
* Implement {@link #getProcessorRelationships()} to allow writing to
* relationships other than <code>success</code> and <code>failure</code>. The
* {@link #getRoute()} superclass method is *not* used by Converter Scripts.
* </p>
*
*/
public class ConverterScript extends Script {
private ProcessSession session; // used to create files
private Object convertCallback;
public ConverterScript() {
}
public ConverterScript(Object... callbacks) {
super(callbacks);
for (Object callback : callbacks) {
if (callback instanceof Map<?, ?>) {
convertCallback = convertCallback == null && ((Map<?, ?>) callback).containsKey("convert") ? callback : convertCallback;
}
}
}
// Subclasses should implement this to define basic logic
protected void convert(InputStream stream) throws NoSuchMethodException, ScriptException {
if (convertCallback != null) {
((Invocable) engine).invokeMethod(convertCallback, "convert", stream);
}
}
/**
* Owning processor uses this method to kick off handling of a single file
*
* @param aSession the owning processor's Repository (needed to make new
* files)
*/
public void process(ProcessSession aSession) {
this.session = aSession;
this.session.read(this.flowFile, new InputStreamCallback() {
@Override
public void process(InputStream in) throws IOException {
BufferedInputStream stream = new BufferedInputStream(in);
try {
convert(stream);
} catch (NoSuchMethodException | ScriptException e) {
logger.error("Failed to execute 'convert' function in script", e);
throw new IOException(e);
}
}
});
}
// this should go back to protected once we get Nashorn
public void createFlowFile(final String flowFileName, final Relationship relationship, final OutputStreamHandler handler) {
FlowFile result = session.create(this.flowFile);
result = session.putAttribute(result, CoreAttributes.FILENAME.key(), flowFileName);
try {
result = session.write(result, new OutputStreamCallback() {
@Override
public void process(OutputStream out) throws IOException {
handler.write(out);
}
});
this.logger.info("Transfer flow file {} to {}", new Object[]{result, relationship});
session.transfer(result, relationship);
} catch (Exception e) {
this.logger.error("Could not create new flow file from script", e);
session.remove(result);
}
}
}

View File

@ -1,46 +0,0 @@
/*
* 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.scripting;
import java.io.File;
import java.io.IOException;
import org.apache.commons.io.FileUtils;
public enum JRubyScriptFactory {
INSTANCE;
private static final String PRELOADS = "include Java\n"
+ "\n"
+ "java_import 'org.apache.nifi.components.PropertyDescriptor'\n"
+ "java_import 'org.apache.nifi.components.Validator'\n"
+ "java_import 'org.apache.nifi.processor.util.StandardValidators'\n"
+ "java_import 'org.apache.nifi.processor.Relationship'\n"
+ "java_import 'org.apache.nifi.logging.ProcessorLog'\n"
+ "java_import 'org.apache.nifi.scripting.ReaderScript'\n"
+ "java_import 'org.apache.nifi.scripting.WriterScript'\n"
+ "java_import 'org.apache.nifi.scripting.ConverterScript'\n"
+ "\n";
public String getScript(File scriptFile) throws IOException {
StringBuilder sb = new StringBuilder();
sb.append(PRELOADS)
.append(FileUtils.readFileToString(scriptFile, "UTF-8"));
return sb.toString();
}
}

View File

@ -1,56 +0,0 @@
/*
* 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.scripting;
import java.io.File;
import java.io.IOException;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
public enum JavaScriptScriptFactory {
INSTANCE;
private static final String PRELOADS = "var Scripting = JavaImporter(\n"
+ " Packages.org.apache.nifi.components,\n"
+ " Packages.org.apache.nifi.processor.util,\n"
+ " Packages.org.apache.nifi.processor,\n"
+ " Packages.org.apache.nifi.logging,\n"
+ " Packages.org.apache.nifi.scripting,\n"
+ " Packages.org.apache.commons.io);\n"
+ "var readFile = function (file) {\n"
+ " var script = Packages.org.apache.commons.io.FileUtils.readFileToString("
+ " new java.io.File($PATH, file)"
+ " );\n"
+ " return \"\" + script;\n"
+ "}\n"
+ "var require = function (file){\n"
+ " var exports={}, module={};\n"
+ " module.__defineGetter__('id', function(){return file;});"
+ " eval(readFile(file));\n"
+ " return exports;\n"
+ "}\n";
public String getScript(File scriptFile) throws IOException {
StringBuilder sb = new StringBuilder();
final String parent = StringUtils.replace(scriptFile.getParent(), "\\", "/");
sb.append(PRELOADS).append("var $PATH = \"").append(parent).append("\"\n")
.append(FileUtils.readFileToString(scriptFile, "UTF-8"));
return sb.toString();
}
}

View File

@ -1,45 +0,0 @@
/*
* 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.scripting;
import java.io.File;
import java.io.IOException;
import org.apache.commons.io.FileUtils;
public enum JythonScriptFactory {
INSTANCE;
private final static String PRELOADS = "from org.python.core.util import FileUtil\n"
+ "from org.apache.nifi.components import PropertyDescriptor\n"
+ "from org.apache.nifi.components import Validator\n"
+ "from org.apache.nifi.processor.util import StandardValidators\n"
+ "from org.apache.nifi.processor import Relationship\n"
+ "from org.apache.nifi.logging import ProcessorLog\n"
+ "from org.apache.nifi.scripting import ReaderScript\n"
+ "from org.apache.nifi.scripting import WriterScript\n"
+ "from org.apache.nifi.scripting import ConverterScript\n";
public String getScript(File scriptFile) throws IOException {
StringBuilder sb = new StringBuilder();
sb.append(PRELOADS)
.append(FileUtils.readFileToString(scriptFile, "UTF-8"));
return sb.toString();
}
}

View File

@ -1,24 +0,0 @@
/*
* 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.scripting;
import java.io.OutputStream;
public interface OutputStreamHandler {
void write(OutputStream out);
}

View File

@ -1,79 +0,0 @@
/*
* 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.scripting;
import java.io.InputStream;
import java.util.Map;
import javax.script.Invocable;
import javax.script.ScriptException;
import org.apache.nifi.processor.Relationship;
/**
* <p>
* Script authors should extend this class if they want to follow the "reader"
* paradigm for NiFi processors.
* </p>
*
* <p>
* User scripts should implement {@link #route(InputStream)}. <code>route</code>
* uses a returned relationship name to determine where FlowFiles go. Scripts
* may also implement {@link #getProcessorRelationships()} to specify available
* relationship names.
* </p>
*
*/
public class ReaderScript extends Script {
private Object routeCallback;
public ReaderScript(Object... callbacks) {
super(callbacks);
for (Object callback : callbacks) {
if (callback instanceof Map<?, ?>) {
routeCallback = routeCallback == null && ((Map<?, ?>) callback).containsKey("route") ? callback : routeCallback;
}
}
}
public ReaderScript() {
}
// Simple helper
public void process(InputStream input) throws NoSuchMethodException, ScriptException {
lastRoute = route(input);
}
/**
* Subclasses should examine the provided inputstream, then determine which
* relationship the file will be sent down and return its name.
*
*
* @param in a Java InputStream containing the incoming FlowFile.
* @return a relationship name
* @throws ScriptException
* @throws NoSuchMethodException
*/
public Relationship route(InputStream in) throws NoSuchMethodException, ScriptException {
Relationship relationship = null;
Invocable invocable = (Invocable) this.engine;
relationship = (Relationship) invocable.invokeMethod(routeCallback, "route", in);
return relationship;
}
}

View File

@ -1,303 +0,0 @@
/*
* 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.scripting;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
import org.apache.nifi.components.PropertyDescriptor;
import org.apache.nifi.flowfile.FlowFile;
import org.apache.nifi.logging.ProcessorLog;
import org.apache.nifi.processor.Relationship;
/**
* <p>
* Base class for all scripts. In this framework, only ScriptEngines that
* implement javax.script.Invocable are supported.
*
* </p>
*
*/
public class Script {
public static final Relationship SUCCESS_RELATIONSHIP = new Relationship.Builder()
.name("success")
.description("Destination of successfully created flow files")
.build();
public static final Relationship FAIL_RELATIONSHIP = new Relationship.Builder()
.name("failure")
.description("Destination of flow files when a error occurs in the script")
.build();
static final Set<Relationship> RELATIONSHIPS;
static {
Set<Relationship> rels = new HashSet<>();
rels.add(FAIL_RELATIONSHIP);
rels.add(SUCCESS_RELATIONSHIP);
RELATIONSHIPS = Collections.unmodifiableSet(rels);
}
FlowFile flowFile = null;
ScriptEngine engine = null;
protected Map<String, String> properties = new HashMap<>();
protected Relationship lastRoute = SUCCESS_RELATIONSHIP;
protected ProcessorLog logger;
protected String scriptFileName;
protected Map<String, String> attributes = new HashMap<>();
protected long flowFileSize = 0;
protected long flowFileEntryDate = System.currentTimeMillis();
// the following are needed due to an inadequate JavaScript ScriptEngine. It will not allow
// subclassing a Java Class, only implementing a Java Interface. So, the syntax of JavaScript
// scripts looks like subclassing, but actually is just constructing a Script instance and
// passing in functions as args to the constructor. When we move to Nashorn JavaScript ScriptEngine
// in Java 8, we can get rid of these and revert the subclasses of this class to abstract.
protected Object propDescCallback;
protected Object relationshipsCallback;
protected Object validateCallback;
protected Object exceptionRouteCallback;
/**
* Create a Script without any parameters
*/
public Script() {
}
public Script(Object... callbacks) {
for (Object callback : callbacks) {
if (callback instanceof Map<?, ?>) {
propDescCallback = propDescCallback == null && ((Map<?, ?>) callback).containsKey("getPropertyDescriptors") ? callback
: propDescCallback;
relationshipsCallback = relationshipsCallback == null && ((Map<?, ?>) callback).containsKey("getRelationships") ? callback
: relationshipsCallback;
validateCallback = validateCallback == null && ((Map<?, ?>) callback).containsKey("validate") ? callback : validateCallback;
exceptionRouteCallback = exceptionRouteCallback == null && ((Map<?, ?>) callback).containsKey("getExceptionRoute") ? callback
: exceptionRouteCallback;
}
}
}
/**
* Specify a set of properties with corresponding NiFi validators.
*
* Subclasses that do not override this method will still have access to all
* properties via the "properties" field
*
* @return a list of PropertyDescriptors
* @throws ScriptException
* @throws NoSuchMethodException
*/
@SuppressWarnings("unchecked")
public List<PropertyDescriptor> getPropertyDescriptors() throws NoSuchMethodException, ScriptException {
if (propDescCallback != null) {
return (List<PropertyDescriptor>) ((Invocable) engine).invokeMethod(propDescCallback, "getPropertyDescriptors", (Object) null);
}
return Collections.emptyList();
}
/**
* Specify a set of reasons why this processor should be invalid.
*
* Subclasses that do not override this method will depend only on
* individual property validators as specified in
* {@link #getPropertyDescriptors()}.
*
* @return a Collection of messages to display to the user, or an empty
* Collection if the processor configuration is OK.
* @throws ScriptException
* @throws NoSuchMethodException
*/
@SuppressWarnings("unchecked")
public Collection<String> validate() throws NoSuchMethodException, ScriptException {
if (validateCallback != null) {
return (Collection<String>) ((Invocable) engine).invokeMethod(validateCallback, "validate", (Object) null);
}
return Collections.emptyList();
}
void setFlowFile(FlowFile ff) {
flowFile = ff;
if (null != ff) {
// have to clone because ff.getAttributes is unmodifiable
this.attributes = new HashMap<>(ff.getAttributes());
this.flowFileSize = ff.getSize();
this.flowFileEntryDate = ff.getEntryDate();
}
}
void setProperties(Map<String, String> map) {
properties = new HashMap<>(map);
}
/**
* Required to access entire properties map -- Jython (at least) won't let
* you read the member variable without a getter
*
* @return entire parameter map
*/
// change back to protected when we get nashorn
public Map<String, String> getProperties() {
return properties;
}
/**
* Get the named parameter. Some scripting languages make a method call
* easier than accessing a member field, so this is a convenience method to
* look up values in the properties field.
*
* @param key a hash key
* @return the value pointed at by the key specified
*/
public String getProperty(String key) {
return properties.get(key);
}
/**
* Name the various relationships by which a file can leave this processor.
* Subclasses may override this method to change available relationships.
*
* @return a collection of relationship names
* @throws ScriptException
* @throws NoSuchMethodException
*/
@SuppressWarnings("unchecked")
public Collection<Relationship> getRelationships() throws NoSuchMethodException, ScriptException {
if (relationshipsCallback != null) {
return (Collection<Relationship>) ((Invocable) engine).invokeMethod(relationshipsCallback, "getRelationships", (Object) null);
}
return RELATIONSHIPS;
}
/**
* Determine what do with a file that has just been processed.
*
* After a script runs its "read" or "write" method, it should update the
* "lastRoute" field to specify the relationship to which the resulting file
* will be sent.
*
* @return a relationship name
*/
public Relationship getRoute() {
return lastRoute;
}
// Required because of a potential issue in Rhino -- protected methods are visible in
// subclasses but protected fields (like "lastRoute") are not
// change back to protected when we get nashorn
public void setRoute(Relationship route) {
lastRoute = route;
}
/**
* Determine where to send a file if an exception is thrown during
* processing.
*
* Subclasses may override this method to use a different relationship, or
* to determine the relationship dynamically. Returning null causes the file
* to be deleted instead.
*
* Defaults to "failure".
*
* @return the name of the relationship to use in event of an exception, or
* null to delete the file.
* @throws ScriptException
* @throws NoSuchMethodException
*/
public Relationship getExceptionRoute() throws NoSuchMethodException, ScriptException {
if (exceptionRouteCallback != null) {
return (Relationship) ((Invocable) engine).invokeMethod(exceptionRouteCallback, "getExceptionRoute", (Object) null);
}
return FAIL_RELATIONSHIP;
}
/*
* Some scripting languages make a method call easier than accessing a member field, so this is a convenience method to get
* the incoming flow file size.
*/
// Change back to protected when we get nashorn
public long getFlowFileSize() {
return flowFileSize;
}
/*
* Some scripting languages make a method call easier than accessing a member field, so this is a convenience method to get
* entry date of the flow file.
*/
// Change back to protected when we get nashorn
public long getFlowFileEntryDate() {
return flowFileEntryDate;
}
void setLogger(ProcessorLog logger) {
this.logger = logger;
}
/*
* Required so that scripts in some languages can read access the attribute. Jython (at least) won't let you read the member
* variable without a getter
*/
protected ProcessorLog getLogger() {
return this.logger;
}
void setFileName(String scriptFileName) {
this.scriptFileName = scriptFileName;
}
public String getFileName() {
return this.scriptFileName;
}
// this one's public because it's needed by ExecuteScript to update the flow file's attributes AFTER processing is done
public Map<String, String> getAttributes() {
return this.attributes;
}
/*
* Some scripting languages make a method call easier than accessing a member field, so this is a convenience method to look
* up values in the attributes field.
*/
// Change back to protected when we get nashorn
public String getAttribute(String key) {
return this.attributes.get(key);
}
/*
* Some scripting languages make a method call easier than accessing a member field, so this is a convenience method to set
* key/value pairs in the attributes field.
*/
// Change back to protected when we get nashorn
public void setAttribute(String key, String value) {
this.attributes.put(key, value);
}
void setEngine(ScriptEngine scriptEngine) {
this.engine = scriptEngine;
}
}

View File

@ -1,117 +0,0 @@
/*
* 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.scripting;
import java.io.File;
import java.util.concurrent.ConcurrentHashMap;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import org.apache.commons.lang3.StringUtils;
import org.jruby.embed.PropertyName;
public class ScriptEngineFactory {
private static final String THREADING = "THREADING";
private static final String MULTITHREADED = "MULTITHREADED";
private static final String STATELESS = "STATELESS";
private static final String THREAD_ISOLATED = "THREAD-ISOLATED";
final static ScriptEngineManager scriptEngMgr;
static {
System.setProperty(PropertyName.LOCALCONTEXT_SCOPE.toString(), "singlethread");
System.setProperty(PropertyName.COMPILEMODE.toString(), "jit");
System.setProperty(PropertyName.COMPATVERSION.toString(), "JRuby1.9");
System.setProperty(PropertyName.LOCALVARIABLE_BEHAVIOR.toString(), "transient");
System.setProperty("compile.invokedynamic", "false");
System.setProperty(PropertyName.LAZINESS.toString(), "true");
scriptEngMgr = new ScriptEngineManager();
}
final ConcurrentHashMap<String, ScriptEngine> threadSafeEngines = new ConcurrentHashMap<>();
ScriptEngine getEngine(String extension) {
ScriptEngine engine = threadSafeEngines.get(extension);
if (null == engine) {
engine = scriptEngMgr.getEngineByExtension(extension);
if (null == engine) {
throw new IllegalArgumentException("No ScriptEngine exists for extension " + extension);
}
Object threading = engine.getFactory().getParameter(THREADING);
// the MULTITHREADED status means that the scripts need to be careful about sharing state
if (THREAD_ISOLATED.equals(threading) || STATELESS.equals(threading) || MULTITHREADED.equals(threading)) {
ScriptEngine cachedEngine = threadSafeEngines.putIfAbsent(extension, engine);
if (null != cachedEngine) {
engine = cachedEngine;
}
}
}
return engine;
}
ScriptEngine getNewEngine(File scriptFile, String extension) throws ScriptException {
ScriptEngine engine = scriptEngMgr.getEngineByExtension(extension);
if (null == engine) {
throw new IllegalArgumentException("No ScriptEngine exists for extension " + extension);
}
// Initialize some paths
StringBuilder sb = new StringBuilder();
switch (extension) {
case "rb":
String parent = scriptFile.getParent();
parent = StringUtils.replace(parent, "\\", "/");
sb.append("$:.unshift '")
.append(parent)
.append("'\n")
.append("$:.unshift File.join '")
.append(parent)
.append("', 'lib'\n");
engine.eval(sb.toString());
break;
case "py":
parent = scriptFile.getParent();
parent = StringUtils.replace(parent, "\\", "/");
String lib = parent + "/lib";
sb.append("import sys\n").append("sys.path.append('").append(parent)
.append("')\n").append("sys.path.append('")
.append(lib)
.append("')\n")
.append("__file__ = '")
.append(scriptFile.getAbsolutePath())
.append("'\n");
engine.eval(sb.toString());
break;
default:
break;
}
Object threading = engine.getFactory().getParameter(THREADING);
// the MULTITHREADED status means that the scripts need to be careful about sharing state
if (THREAD_ISOLATED.equals(threading) || STATELESS.equals(threading) || MULTITHREADED.equals(threading)) {
// replace prior instance if any
threadSafeEngines.put(extension, engine);
}
return engine;
}
boolean isThreadSafe(String scriptExtension) {
return threadSafeEngines.containsKey(scriptExtension);
}
}

View File

@ -1,269 +0,0 @@
/*
* 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.scripting;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import javax.script.Bindings;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
import javax.script.SimpleBindings;
import org.apache.nifi.flowfile.FlowFile;
import org.apache.nifi.io.BufferedInputStream;
import org.apache.nifi.logging.ProcessorLog;
import org.apache.commons.io.FileUtils;
/**
* While this is a 'factory', it is not a singleton because we want a factory
* per processor. This factory has state, all of which belong to only one
* processor.
*
*/
public class ScriptFactory {
private final ScriptEngineFactory engineFactory = new ScriptEngineFactory();
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private final ReadLock readLock = lock.readLock();
private final WriteLock writeLock = lock.writeLock();
private final ProcessorLog logger;
private volatile CompiledScript compiledScript;
private volatile String scriptText;
private volatile byte[] md5Hash;
private volatile long lastTimeChecked;
private volatile String scriptFileName;
private volatile long scriptCheckIntervalMS = 15000;
public ScriptFactory(ProcessorLog logger) {
this.logger = logger;
}
public void setScriptCheckIntervalMS(long msecs) {
this.scriptCheckIntervalMS = msecs;
}
/**
* @param aScriptFileName
* @param properties
* @param flowFile
* @return
* @throws IOException
* @throws ScriptException
*/
public Script getScript(final String aScriptFileName, final Map<String, String> properties, final FlowFile flowFile)
throws IOException, ScriptException {
final Script instance;
long now = System.currentTimeMillis();
readLock.lock();
try {
if (!aScriptFileName.equals(this.scriptFileName)) {
readLock.unlock();
writeLock.lock();
try {
if (!aScriptFileName.equals(this.scriptFileName)) {
// need to get brand new engine
compiledScript = null;
this.md5Hash = getMD5Hash(aScriptFileName);
this.lastTimeChecked = now;
this.scriptFileName = aScriptFileName;
updateEngine();
} // else another thread beat me to the change...so just get a script
} finally {
readLock.lock();
writeLock.unlock();
}
} else if (lastTimeChecked + scriptCheckIntervalMS < now) {
readLock.unlock();
writeLock.lock();
try {
if (lastTimeChecked + scriptCheckIntervalMS < now) {
byte[] md5 = getMD5Hash(this.scriptFileName);
if (!MessageDigest.isEqual(md5Hash, md5)) {
// need to get brand new engine
compiledScript = null;
updateEngine();
this.md5Hash = md5;
} // else no change to script, so just update time checked
this.lastTimeChecked = now;
} // else another thread beat me to the check...so just get a script
} finally {
readLock.lock();
writeLock.unlock();
}
}
try {
instance = getScriptInstance(properties);
instance.setFileName(this.scriptFileName);
instance.setProperties(properties);
instance.setLogger(logger);
instance.setFlowFile(flowFile);
} catch (ScriptException e) {
// need to reset state to enable re-initialization
this.lastTimeChecked = 0;
this.scriptFileName = null;
throw e;
}
} finally {
readLock.unlock();
}
return instance;
}
public Script getScript(String aScriptFileName) throws ScriptException, IOException {
Map<String, String> props = new HashMap<>();
return getScript(aScriptFileName, props, null);
}
private byte[] getMD5Hash(String aScriptFileName) throws FileNotFoundException, IOException {
byte[] messageDigest = null;
try (FileInputStream fis = new FileInputStream(aScriptFileName);
DigestInputStream dis = new DigestInputStream(new BufferedInputStream(fis), MessageDigest.getInstance("MD5"))) {
byte[] bytes = new byte[8192];
while (dis.read(bytes) != -1) {
// do nothing...just computing the md5 hash
}
messageDigest = dis.getMessageDigest().digest();
} catch (NoSuchAlgorithmException swallow) {
// MD5 is a legitimate format
}
return messageDigest;
}
private String getScriptText(File scriptFile, String extension) throws IOException {
final String script;
switch (extension) {
case "rb":
script = JRubyScriptFactory.INSTANCE.getScript(scriptFile);
break;
case "js":
script = JavaScriptScriptFactory.INSTANCE.getScript(scriptFile);
break;
case "py":
script = JythonScriptFactory.INSTANCE.getScript(scriptFile);
break;
default:
script = FileUtils.readFileToString(scriptFile);
}
return script;
}
private Script getScriptInstance(final Map<String, String> properties) throws ScriptException {
Map<String, Object> localThreadVariables = new HashMap<>();
final String extension = getExtension(scriptFileName);
String loggerVariableKey = getVariableName("GLOBAL", "logger", extension);
localThreadVariables.put(loggerVariableKey, logger);
String propertiesVariableKey = getVariableName("INSTANCE", "properties", extension);
localThreadVariables.put(propertiesVariableKey, properties);
localThreadVariables.put(ScriptEngine.FILENAME, scriptFileName);
final Bindings bindings = new SimpleBindings(localThreadVariables);
final ScriptEngine scriptEngine = engineFactory.getEngine(extension);
Script instance;
if (compiledScript == null) {
instance = (Script) scriptEngine.eval(scriptText, bindings);
if (instance == null) { // which it will be for python and also for local variables in javascript
instance = (Script) scriptEngine.eval("instance", bindings);
}
} else {
instance = (Script) compiledScript.eval(bindings);
if (instance == null) { // which it will be for python and also for local variables in javascript
instance = (Script) compiledScript.getEngine().eval("instance", bindings);
}
}
instance.setEngine(scriptEngine);
return instance;
}
/*
* Must have writeLock when calling this!!!!
*/
private void updateEngine() throws IOException, ScriptException {
final String extension = getExtension(scriptFileName);
// if engine is thread safe, it's being reused...if it's a JrubyEngine it
File scriptFile = new File(this.scriptFileName);
ScriptEngine scriptEngine = engineFactory.getNewEngine(scriptFile, extension);
scriptText = getScriptText(scriptFile, extension);
Map<String, Object> localThreadVariables = new HashMap<>();
String loggerVariableKey = getVariableName("GLOBAL", "logger", extension);
localThreadVariables.put(loggerVariableKey, logger);
String propertiesVariableKey = getVariableName("INSTANCE", "properties", extension);
localThreadVariables.put(propertiesVariableKey, new HashMap<String, String>());
localThreadVariables.put(ScriptEngine.FILENAME, scriptFileName);
if (scriptEngine instanceof Compilable) {
Bindings bindings = new SimpleBindings(localThreadVariables);
scriptEngine.setBindings(bindings, ScriptContext.ENGINE_SCOPE);
compiledScript = ((Compilable) scriptEngine).compile(scriptText);
}
logger.debug("Updating Engine!!");
}
private String getVariableName(String scope, String variableName, String extension) {
String result;
switch (extension) {
case "rb":
switch (scope) {
case "GLOBAL":
result = '$' + variableName;
break;
case "INSTANCE":
result = '@' + variableName;
break;
default:
result = variableName;
break;
}
break;
default:
result = variableName;
break;
}
return result;
}
private String getExtension(String aScriptFileName) {
int dotPos = aScriptFileName.lastIndexOf('.');
if (dotPos < 1) {
throw new IllegalArgumentException("Script file name must have an extension");
}
final String extension = aScriptFileName.substring(dotPos + 1);
return extension;
}
}

View File

@ -1,67 +0,0 @@
/*
* 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.scripting;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Map;
import javax.script.Invocable;
import javax.script.ScriptException;
/**
* <p>
* Script authors should extend this class if they want to follow the
* "processCallback" paradigm for NiFi processors.
* </p>
*
* <p>
* At a minimum, scripts must implement
* <code>process(FileInputStream, FileOutputStream)</code>.
* </p>
*
* <p>
* By default, all files processed will be sent to the relationship
* <em>success</em>, unless the scriptFileName raises an exception, in which
* case the file will be sent to <em>failure</em>. Implement
* {@link #getProcessorRelationships()} and/or {@link #getRoute()} to change
* this behavior.
* </p>
*
*/
public class WriterScript extends Script {
private Object processCallback;
public WriterScript() {
}
public WriterScript(Object... callbacks) {
super(callbacks);
for (Object callback : callbacks) {
if (callback instanceof Map<?, ?>) {
processCallback = processCallback == null && ((Map<?, ?>) callback).containsKey("process") ? callback : processCallback;
}
}
}
public void process(InputStream in, OutputStream out) throws NoSuchMethodException, ScriptException {
Invocable inv = (Invocable) engine;
inv.invokeMethod(processCallback, "process", in, out);
}
}

View File

@ -1,15 +0,0 @@
# 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.
org.apache.nifi.processors.script.ExecuteScript

View File

@ -1,264 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<!--
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.
-->
<head>
<meta charset="utf-8" />
<title>ExecuteScript</title>
<link rel="stylesheet" href="../../css/component-usage.css" type="text/css" />
</head>
<body>
<!-- Processor Documentation ================================================== -->
<h2>Description:</h2>
<p>
This processor provides the capability to execute scripts in various scripting languages, and passes into the scripts
the input stream and output stream(s) representing an incoming flow file and any created flow files. The processor is designed to be
thread safe, so multiple concurrent tasks may execute against a single script. The processor provides a framework which enables
script writers to implement 3 different types of scripts:
<ul>
ReaderScript - which enables stream-based reading of a FlowFile's content</br>
WriterScript - which enables stream-based reading and writing/modifying of a FlowFile's content</br>
ConverterScript - which enables stream-based reading a FlowFile's content and stream-based writing to newly created FlowFiles</br>
</ul>
Presently, the processor supports 3 scripting languages: Ruby, Python, and JavaScript. The processor is built on the
javax.script API which enables ScriptEngine discovery, thread management, and encapsulates much of the low level bridging-code that
enables Java to Script language integration. Thus, it is designed to be easily extended to other scripting languages. </br>
The attributes of a FlowFile and properties of the Processor are exposed to the script by either a variable in the base class or
a getter method. A script may declare new Processor Properties and different Relationships via overriding the getPropertyDescriptors
and getRelationships methods, respectively.
</p>
The processor provides some boilerplate script to aid in the creation of the three different types of scripts. For example,
the processor provides import statements for classes commonly used within a processor.
<pre>
'org.apache.nifi.components.PropertyDescriptor'
'org.apache.nifi.components.Validator'
'org.apache.nifi.processor.util.StandardValidators'
'org.apache.nifi.processor.Relationship'
'org.apache.nifi.logging.ProcessorLog'
'org.apache.nifi.scripting.ReaderScript'
'org.apache.nifi.scripting.WriterScript'
'org.apache.nifi.scripting.ConverterScript'
</pre>
The processor appends to the script's execution path the parent directory of the specified script file and a sub-directory
called 'lib', which may be useful for supporting scripts. </p>
<p>
<strong>Shared Variables</strong>
</p>
The following variables are provided as shared variables for the scripts:
<ul>
<li>logger
<ul>
<li> The processor's logger </li>
<li> Scope is GLOBAL, thus in Ruby the syntax is $logger</li>
</ul>
</li>
<li>properties
<ul>
<li> A Map of the processor's configuration properties; key and value are strings</li>
<li> Scope is INSTANCE, thus in Ruby the syntax is @properties</li>
</ul>
</li>
</ul>
<p>
<strong>Properties:</strong>
</p>
<p>
In the list below, the names of required properties appear in bold. Any other properties (not in bold) are considered
optional. If a property has a default value, it is indicated. If a property supports the use of the NiFi Expression Language
(or simply, "expression language"), that is also indicated. Of particular note: This processor allows scripts to define additional
Processor properties, which will not be initially visible. Once the processor's configuration is validated, script defined properties
will become visible, and may affect the validity of the processor.
</p>
<ul>
<li>
<strong>Script File Name</strong>
<ul>
<li>Script location, can be relative or absolute path.</li>
<li>Default value: no default</li>
<li>Supports expression language: false</li>
</ul>
</li>
<li>
<strong>Script Check Interval</strong>
<ul>
<li>The time period between checking for updates to a script.</li>
<li>Default value: 15 sec</li>
<li>Supports expression language: false</li>
</ul>
</li>
</ul>
<p>
<strong>Relationships:</strong>
</p>
<p>
The initial 'out of the box' relationships are below. Of particular note is the ability of a script to change the set of
relationships. However, any relationships defined by the script will not be visible until the processor's configuration has been
validated. Once done, new relationships will become visible.
</p>
<ul>
<li>
success
<ul>
<li>Used when a file is successfully processed by a script.</li>
</ul>
</li>
<li>
failure
<ul>
<li>Used when an error occurs while processing a file with a script.</li>
</ul>
</li>
</ul>
<p>
<strong>Example Scripts:</strong>
</p>
<ul>
JavaScript example - the 'with' statement imports packages defined in the framework and limits the importing to the local scope,
rather than global. The 'Scripting' variable uses the JavaImporter class within JavaScript. Since the 'instance' variable is intended to
be local scope (not global), it must be named 'instance' as it it not passed back to the processor upon script evaluation and must be
fetched. If you make it global, you can name it whatever you'd like...but this is intended to be multi-threaded so do so at your own
risk.</p>
Presently, there are issues with the JavaScript scripting engine that prevent sub-classing the base classes in the Processor's Java
framework. So, what is actually happening is an instance of the ReaderScript is created with a provided callback object. When we are able
to move to a more competent scripting engine (supposedly in Java 8), the code below will remain the same, but the 'instance' variable
will actually be a sub-class of ReaderScript.
<pre>
with (Scripting) {
var instance = new ReaderScript({
route : function(input) {
var str = IOUtils.toString(input);
var expr = instance.getProperty("expr");
filename = instance.attributes.get("filename");
instance.setAttribute("filename", filename + ".modified");
if (str.match(expr)) {
return Script.FAIL_RELATIONSHIP;
} else {
return Script.SUCCESS_RELATIONSHIP;
}
}
});
}
</pre>
Ruby example - the 'OutputStreamHandler' is an interface which is called when creating flow files.
<pre>
java_import 'org.apache.nifi.scripting.OutputStreamHandler'
class SimpleConverter < ConverterScript
field_reader :FAIL_RELATIONSHIP, :SUCCESS_RELATIONSHIP, :logger, :attributes
def convert(input)
in_io = input.to_io
createFlowFile("firstLine", FAIL_RELATIONSHIP, OutputStreamHandler.impl do |method, out|
out_io = out.to_io
out_io << in_io.readline.to_java_bytes
out_io.close
logger.debug("Wrote data to failure...this message logged with logger from super class")
end)
createFlowFile("otherLines", SUCCESS_RELATIONSHIP, OutputStreamHandler.impl do |method, out|
out_io = out.to_io
in_io.each_line { |line|
out_io << line
}
out_io.close
logger.debug("Wrote data to success...this message logged with logger from super class")
end)
in_io.close
end
end
$logger.debug("Creating SimpleConverter...this message logged with logger from shared variables")
SimpleConverter.new
</pre>
Python example - The difficulty with Python is that it does not return objects upon script evaluation, so the instance of the Script
class must be fetched by name. Thus, you must define a variable called 'instance'.
<pre>
import re
class RoutingReader(ReaderScript):
A = Relationship.Builder().name("a").description("some good stuff").build()
B = Relationship.Builder().name("b").description("some other stuff").build()
C = Relationship.Builder().name("c").description("some bad stuff").build()
def getRelationships(self):
return [self.A,self.B,self.C]
def getExceptionRoute(self):
return self.C
def route( self, input ):
logger.info("Executing route")
for line in FileUtil.wrap(input):
if re.match("^bad", line, re.IGNORECASE):
return self.B
if re.match("^sed", line):
raise RuntimeError("That's no good!")
return self.A
logger.debug("Constructing instance")
instance = RoutingReader()
</pre>
</ul>
<p>
<strong>Script API:</strong>
</p>
<ul>
<li>getAttribute(String) : String</li>
<li>getAttributes() : Map(String,String)</li>
<li>getExceptionRoute() : Relationship</li>
<li>getFileName() : String</li>
<li>getFlowFileEntryDate() : Calendar</li>
<li>getFlowFileSize() : long</li>
<li>getProperties() : Map(String, String)</li>
<li>getProperty(String) : String</li>
<li>getPropertyDescriptors() : List(PropertyDescriptor)</li>
<li>getRelationships() : Collection(Relationship)</li>
<li>getRoute() : Relationship</li>
<li>setRoute(Relationship)</li>
<li>setAttribute(String, String)</li>
<li>validate() : Collection(String)</li>
</ul>
<p>
<strong>ReaderScript API:</strong>
</p>
<ul>
<li>route(InputStream) : Relationship</li>
</ul>
<p>
<strong>WriterScript API:</strong>
</p>
<ul>
<li>process(InputStream, OutputStream)</li>
</ul>
<p>
<strong>ConverterScript API:</strong>
</p>
<ul>
<li>convert(InputStream)</li>
<li>createFlowFile(String, Relationship, OutputStreamHandler)</li>
</ul>
<p>
<strong>OutputStreamHandler API:</strong>
</p>
<ul>
<li>write(OutputStream)</li>
</ul>
</body>
</html>

View File

@ -1,939 +0,0 @@
/*
* 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.script;
import org.apache.nifi.processors.script.ExecuteScript;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.nifi.processor.Relationship;
import org.apache.nifi.util.MockFlowFile;
import org.apache.nifi.util.TestRunner;
import org.apache.nifi.util.TestRunners;
import org.apache.commons.io.FileUtils;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author unattributed
*
*/
public class TestExecuteScript {
static Logger LOG;
static {
System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "info");
System.setProperty("org.slf4j.simpleLogger.showDateTime", "true");
System.setProperty("org.slf4j.simpleLogger.log.nifi.processors.script.ExecuteScript", "trace");
System.setProperty("org.slf4j.simpleLogger.log.nifi.processors.script.TestExecuteScript", "debug");
System.setProperty("org.slf4j.simpleLogger.log.nifi.processors.AbstractProcessor", "debug");
LOG = LoggerFactory.getLogger(TestExecuteScript.class);
}
private TestRunner controller;
private final String multiline = "Lorem ipsum dolor sit amet,\n"
+ "consectetur adipisicing elit,\n"
+ "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\n"
+ "Ut enim ad minim veniam,\n"
+ "quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.\n"
+ "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.\n"
+ "Excepteur sint occaecat cupidatat non proident,\n"
+ "sunt in culpa qui officia deserunt mollit anim id est laborum.";
/**
* Create a mock SingleProcessorController using our processor and pass data
* to it via byte array. Returns the Sink that provides access to any files
* that pass out of the processor
*/
@Before
public void setupEach() throws IOException {
controller = TestRunners.newTestRunner(ExecuteScript.class);
controller.setValidateExpressionUsage(false);
// copy all scripts to target directory and run from there. some python
// scripts create .class files that end up in src/test/resources.
FileUtils.copyDirectory(new File("src/test/resources"), new File("target/test-scripts"));
}
// Fail if the specified relationship does not contain exactly one file
// with the expected value
private void assertRelationshipContents(String expected, String relationship) {
controller.assertTransferCount(relationship, 1);
MockFlowFile ff = controller.getFlowFilesForRelationship(relationship).get(0);
ff.assertContentEquals(expected);
}
// Fail if the specified relationship does not contain specified number of files
// with the expected value
private void assertRelationshipContents(String expected, String relationship, int count) {
controller.assertTransferCount(relationship, count);
MockFlowFile ff = controller.getFlowFilesForRelationship(relationship).get(count - 1);
ff.assertContentEquals(expected);
}
// ////////////////////////////////////
// General tests
@Test(expected = IllegalArgumentException.class)
public void failOnBadName() {
LOG.info("Supplying bad script file names");
// None of these should result in actually setting the property, because they're non-existent / bad files
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, "not/really.rb");
controller.assertNotValid();
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, "fakey/fake.js");
controller.assertNotValid();
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, "pom.xml");
controller.assertNotValid();
}
// ////////////////////////////////////
// Ruby script tests
@Test
public void testSimpleReadR() {
LOG.info("Ruby script: fail file based on reading contents");
controller.enqueue("This stuff is fine".getBytes());
controller.enqueue(multiline.getBytes());
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, "target/test-scripts/readTest.rb");
controller.setThreadCount(2);
controller.run(2);
assertRelationshipContents(multiline, "failure");
assertRelationshipContents("This stuff is fine", "success");
controller.getFlowFilesForRelationship("success").get(0).assertAttributeEquals("filename", "NewFileNameFromReadTest");
}
@Test
public void testParamReadR() {
LOG.info("Ruby script: Failing file based on reading contents");
Map<String, String> attrs1 = new HashMap<>();
attrs1.put("filename", "StuffIsFine.txt");
Map<String, String> attrs2 = new HashMap<>();
attrs2.put("filename", "multiline.txt");
controller.enqueue("This stuff is fine".getBytes(), attrs1);
controller.enqueue(multiline.getBytes(), attrs2);
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, "target/test-scripts/readWithParams.rb");
controller.setProperty("expr", "rehenderit");
controller.run(2);
assertRelationshipContents(multiline, "failure");
assertRelationshipContents("This stuff is fine", "success");
}
@Test
public void testWriteLastLineR() {
LOG.info("Running Ruby script to output last line of file");
controller.enqueue(multiline.getBytes());
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, "target/test-scripts/writeTest.rb");
controller.run();
List<MockFlowFile> files = controller.getFlowFilesForRelationship("success");
assertEquals("Process did not generate an output file", 1, files.size());
byte[] blob = files.get(0).toByteArray();
String[] lines = new String(blob).split("\n");
assertEquals("File had more than one line", 1, lines.length);
assertEquals("sunt in culpa qui officia deserunt mollit anim id est laborum.", lines[0]);
}
@Test
public void testWriteOptionalParametersR() {
LOG.info("Ruby script that uses optional parameters");
controller.enqueue(multiline.getBytes());
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, "target/test-scripts/paramTest.rb");
controller.setProperty("repeat", "3");
controller.run();
List<MockFlowFile> files = controller.getFlowFilesForRelationship("success");
assertEquals("Process did not generate an output file", 1, files.size());
byte[] blob = files.get(0).toByteArray();
String[] lines = new String(blob).split("\n");
assertEquals("File did not have 3 lines", 3, lines.length);
assertEquals("sunt in culpa qui officia deserunt mollit anim id est laborum.", lines[0]);
}
@Test
public void testSetupOptionalValidationR() {
LOG.info("Ruby script creating validators for optional properties");
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, "target/test-scripts/optionalValidators.rb");
controller.assertNotValid();
controller.setProperty("int", "abc");
controller.assertNotValid();
controller.setProperty("url", "not@valid");
controller.assertNotValid();
controller.setProperty("nonEmpty", "");
controller.assertNotValid();
controller.setProperty("int", "123");
controller.setProperty("url", "http://localhost");
controller.setProperty("nonEmpty", "abc123");
controller.assertValid();
}
@Test
public void testTwoScriptsSameThreadSameClassName() {
LOG.info("Test 2 different scripts with the same ruby class name");
Map<String, String> attrs1 = new HashMap<>();
attrs1.put("filename", "StuffIsFine.txt");
Map<String, String> attrs2 = new HashMap<>();
attrs2.put("filename", "multiline.txt");
controller.enqueue("This stuff is fine".getBytes(), attrs1);
controller.enqueue(multiline.getBytes(), attrs2);
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, "target/test-scripts/readWithParams.rb");
controller.setProperty("expr", "rehenderit");
controller.run(2);
assertRelationshipContents(multiline, "failure");
assertRelationshipContents("This stuff is fine", "success");
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, "target/test-scripts/optionalValidators.rb");
controller.assertNotValid();
controller.setProperty("int", "abc");
controller.assertNotValid();
controller.setProperty("url", "not@valid");
controller.assertNotValid();
controller.setProperty("nonEmpty", "");
controller.assertNotValid();
controller.setProperty("int", "123");
controller.setProperty("url", "http://localhost");
controller.setProperty("nonEmpty", "abc123");
controller.assertValid();
}
@Test
public void testUpdateScriptR() throws Exception {
LOG.info("Test one script with updated class");
File testFile = File.createTempFile("script", ".rb");
File original = new File("target/test-scripts/readWithParams.rb");
FileUtils.copyFile(original, testFile);
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, testFile.getPath());
controller.assertValid();
original = new File("target/test-scripts/optionalValidators.rb");
FileUtils.copyFile(original, testFile);
controller.setProperty(ExecuteScript.SCRIPT_CHECK_INTERVAL, "5 secs");
Thread.sleep(6000);
controller.assertNotValid();
controller.setProperty("int", "abc");
controller.assertNotValid();
controller.setProperty("url", "not@valid");
controller.assertNotValid();
controller.setProperty("nonEmpty", "");
controller.assertNotValid();
controller.setProperty("int", "123");
controller.setProperty("url", "http://localhost");
controller.setProperty("nonEmpty", "abc123");
controller.assertValid();
FileUtils.deleteQuietly(testFile);
}
@Test
public void testMultiThreadExecR() {
LOG.info("Ruby script 20 threads: Failing file based on reading contents");
Map<String, String> attrs1 = new HashMap<>();
attrs1.put("filename", "StuffIsFine.txt");
Map<String, String> attrs2 = new HashMap<>();
attrs2.put("filename", "multiline.txt");
controller.setThreadCount(20);
for (int i = 0; i < 10; i++) {
controller.enqueue("This stuff is fine".getBytes(), attrs1);
controller.enqueue(multiline.getBytes(), attrs2);
}
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, "target/test-scripts/readWithParams.rb");
controller.setProperty("expr", "rehenderit");
controller.run(20);
controller.assertTransferCount("failure", 10);
controller.assertTransferCount("success", 10);
for (int i = 0; i < 10; i++) {
MockFlowFile ff = controller.getFlowFilesForRelationship("failure").get(i);
ff.assertContentEquals(multiline);
assertTrue(ff.getAttribute("filename").endsWith("modified"));
ff = controller.getFlowFilesForRelationship("success").get(i);
ff.assertContentEquals("This stuff is fine");
assertTrue(ff.getAttribute("filename").endsWith("modified"));
}
}
@Test
public void testManualValidationR() {
LOG.info("Ruby script defining manual validator");
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, "target/test-scripts/alwaysFail.rb");
controller.assertNotValid();
}
@Test
public void testGetRelationshipsR() {
LOG.info("Ruby script: getRelationships");
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, "target/test-scripts/routeTest.rb");
// at this point, the script has not been instantiated so the processor simply returns an empty set
Set<Relationship> rels = controller.getProcessor().getRelationships();
assertEquals(0, rels.size());
// this will instantiate the script
controller.assertValid();
// this will call the script
rels = controller.getProcessor().getRelationships();
assertEquals(3, rels.size());
}
@Test
public void testGetExceptionRouteR() {
LOG.info("Ruby script defining route taken in event of exception");
controller.enqueue("This stuff is fine".getBytes());
controller.enqueue("Bad things go to 'b'.".getBytes());
controller.enqueue(multiline.getBytes());
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, "target/test-scripts/routeTest.rb");
controller.run(3);
assertRelationshipContents("This stuff is fine", "a");
assertRelationshipContents("Bad things go to 'b'.", "b");
assertRelationshipContents(multiline, "c");
}
@Test
public void testSimpleConverterR() {
LOG.info("Running Ruby converter script");
for (int i = 0; i < 20; i++) {
controller.enqueue(multiline.getBytes());
}
controller.setThreadCount(20);
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, "target/test-scripts/simpleConverter.rb");
controller.run(20);
List<MockFlowFile> successFiles = controller.getFlowFilesForRelationship("success");
List<MockFlowFile> failFiles = controller.getFlowFilesForRelationship("failure");
assertEquals("Process did not generate 20 SUCCESS files", 20, successFiles.size());
assertEquals("Process did not generate 20 FAILURE files", 20, failFiles.size());
MockFlowFile sFile = successFiles.get(19);
MockFlowFile fFile = failFiles.get(19);
byte[] blob = fFile.toByteArray();
String[] lines = new String(blob).split("\n");
assertEquals("File had more than one line", 1, lines.length);
assertEquals("Lorem ipsum dolor sit amet,", lines[0]);
blob = sFile.toByteArray();
lines = new String(blob).split("\n");
assertEquals("SUCCESS had wrong number of lines", 7, lines.length);
assertEquals("consectetur adipisicing elit,", lines[0]);
}
@Test
public void testLoadLocalR() {
LOG.info("Ruby: load another script file");
controller.enqueue("This stuff is fine".getBytes());
controller.enqueue(multiline.getBytes());
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, "target/test-scripts/loadLocal.rb");
controller.run(2);
assertRelationshipContents(multiline, "failure");
assertRelationshipContents("This stuff is fine", "success");
}
@Test
public void testFlowFileR() {
LOG.info("Ruby: get FlowFile properties");
controller.enqueue(multiline.getBytes());
HashMap<String, String> meta = new HashMap<String, String>();
meta.put("evict", "yup");
controller.enqueue("This would be plenty long but it's also evicted.".getBytes(), meta);
controller.enqueue("This is too short".getBytes());
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, "target/test-scripts/ffTest.rb");
controller.run(3);
assertRelationshipContents(multiline, "success");
assertRelationshipContents("This is too short", "failure");
assertRelationshipContents("This would be plenty long but it's also evicted.", "evict");
}
// //////////////////////////////////// // JS tests
@Test
public void testSimpleReadJS() {
LOG.info("Javascript: fail file based on reading contents");
controller.enqueue("This stuff is fine".getBytes());
controller.enqueue(multiline.getBytes());
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, "target/test-scripts/readTest.js");
controller.run(2);
assertRelationshipContents(multiline, "failure");
assertRelationshipContents("This stuff is fine", "success");
}
@Test
public void testParamReadJS() {
LOG.info("Javascript: read contents and fail based on parameter");
controller.enqueue("This stuff is fine".getBytes());
controller.enqueue(multiline.getBytes());
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, "target/test-scripts/readWithParams.js");
controller.setProperty("expr", "sed do");
controller.run(2);
assertRelationshipContents(multiline, "failure");
assertRelationshipContents("This stuff is fine", "success");
}
@Test
public void testWriteLastLineJS() {
LOG.info("Running Javascript to output last line of file");
controller.enqueue(multiline.getBytes());
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, "target/test-scripts/writeTest.js");
controller.run();
List<MockFlowFile> sunkFiles = controller.getFlowFilesForRelationship("success");
assertEquals("Process did not generate an output file", 1, sunkFiles.size());
MockFlowFile sunkFile = sunkFiles.iterator().next();
byte[] blob = sunkFile.toByteArray();
String[] lines = new String(blob).split("\n");
assertEquals("File had more than one line", 1, lines.length);
assertEquals("sunt in culpa qui officia deserunt mollit anim id est laborum.", lines[0]);
}
@Test
public void testWriteOptionalParametersJS() {
LOG.info("Javascript processCallback that uses optional parameters");
controller.enqueue(multiline.getBytes());
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, "target/test-scripts/paramTest.js");
controller.setProperty("repeat", "3");
controller.run();
List<MockFlowFile> sunkFiles = controller.getFlowFilesForRelationship("success");
assertEquals("Process did not generate an output file", 1, sunkFiles.size());
MockFlowFile sunkFile = sunkFiles.iterator().next();
byte[] blob = sunkFile.toByteArray();
String[] lines = new String(blob).split("\n");
assertEquals("File did not have 3 lines", 3, lines.length);
assertEquals("sunt in culpa qui officia deserunt mollit anim id est laborum.", lines[0]);
}
@Test
public void testSetupOptionalValidationJS() {
LOG.info("Javascript creating validators for optional properties");
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, "target/test-scripts/optionalValidators.js");
controller.setProperty("int", "abc");
controller.setProperty("url", "not@valid");
controller.setProperty("nonEmpty", "");
assertEquals(2, controller.getProcessor().getPropertyDescriptors().size());
controller.assertNotValid(); // due to invalid values above
assertEquals(5, controller.getProcessor().getPropertyDescriptors().size());
controller.setProperty("int", "123");
controller.setProperty("url", "http://localhost");
controller.setProperty("nonEmpty", "abc123");
assertEquals(5, controller.getProcessor().getPropertyDescriptors().size());
controller.assertValid();
}
@Test
public void testManualValidationJS() {
LOG.info("Javascript defining manual validator");
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, "target/test-scripts/alwaysFail.js");
controller.assertNotValid();
}
@Test
public void testGetExceptionRouteJS() {
LOG.info("Javascript defining route taken in event of exception");
controller.enqueue("This stuff is fine".getBytes());
controller.enqueue("Bad things go to 'b'.".getBytes());
controller.enqueue(multiline.getBytes());
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, "target/test-scripts/routeTest.js");
controller.run(3);
assertRelationshipContents("This stuff is fine", "a");
assertRelationshipContents("Bad things go to 'b'.", "b");
assertRelationshipContents(multiline, "c");
}
@Test
public void testSimpleConverterJS() {
LOG.info("Running Javascript converter script");
for (int i = 0; i < 20; i++) {
controller.enqueue(multiline.getBytes());
}
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, "target/test-scripts/simpleConverter.js");
controller.run(20);
List<MockFlowFile> successFiles = controller.getFlowFilesForRelationship("success");
List<MockFlowFile> failFiles = controller.getFlowFilesForRelationship("failure");
assertEquals("Process did not generate 20 SUCCESS files", 20, successFiles.size());
assertEquals("Process did not generate 20 FAILURE file", 20, failFiles.size());
MockFlowFile sFile = successFiles.get(19);
MockFlowFile fFile = failFiles.get(0);
byte[] blob = sFile.toByteArray();
String[] lines = new String(blob).split("\n");
assertEquals("SUCCESS had wrong number of lines", 7, lines.length);
assertTrue(lines[0].startsWith("consectetur adipisicing elit,"));
blob = fFile.toByteArray();
lines = new String(blob).split("\n");
assertEquals("File had more than one line", 1, lines.length);
assertTrue(lines[0].startsWith("Lorem ipsum dolor sit amet,"));
}
@Test
public void testLoadLocalJS() {
LOG.info("Javascript: load another script file");
controller.enqueue("This stuff is fine".getBytes());
controller.enqueue(multiline.getBytes());
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, "target/test-scripts/loadLocal.js");
controller.run(2);
assertRelationshipContents(multiline, "failure");
assertRelationshipContents("This stuff is fine", "success");
}
@Test
public void testXMLJS() {
LOG.info("Javascript: native XML parser");
controller.enqueue("<a><b foo='bar'>Bad</b><b good='true'>Good</b><b good='false'>Bad</b></a>".getBytes());
controller.enqueue("<a><b>Hello</b><b>world</b></a>".getBytes());
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, "target/test-scripts/parseXml.js");
controller.run(2);
assertRelationshipContents("Good", "success");
assertRelationshipContents("<a><b>Hello</b><b>world</b></a>", "failure");
}
@Test
public void testFlowFileJS() {
LOG.info("JavaScript: get FlowFile properties");
controller.enqueue("This is too short".getBytes());
controller.enqueue(multiline.getBytes());
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, "target/test-scripts/ffTest.js");
controller.run(2);
assertRelationshipContents(multiline, "success");
assertRelationshipContents("This is too short", "failure");
}
@Test
public void testMultiThreadExecJS() {
LOG.info("JavaScript script 20 threads: Failing file based on reading contents");
Map<String, String> attrs1 = new HashMap<>();
attrs1.put("filename", "StuffIsFine.txt");
Map<String, String> attrs2 = new HashMap<>();
attrs2.put("filename", "multiline.txt");
controller.setThreadCount(20);
for (int i = 0; i < 10; i++) {
controller.enqueue("This stuff is fine".getBytes(), attrs1);
controller.enqueue(multiline.getBytes(), attrs2);
}
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, "target/test-scripts/readWithParams.js");
controller.setProperty("expr", "rehenderit");
controller.run(20);
controller.assertTransferCount("failure", 10);
controller.assertTransferCount("success", 10);
for (int i = 0; i < 10; i++) {
MockFlowFile ff = controller.getFlowFilesForRelationship("failure").get(i);
ff.assertContentEquals(multiline);
assertTrue(ff.getAttribute("filename").endsWith("modified"));
ff = controller.getFlowFilesForRelationship("success").get(i);
ff.assertContentEquals("This stuff is fine");
assertTrue(ff.getAttribute("filename").endsWith("modified"));
}
}
@Test
public void testUpdateScriptJS() throws Exception {
LOG.info("Test one script with updated class");
File testFile = File.createTempFile("script", ".js");
File original = new File("target/test-scripts/readWithParams.js");
FileUtils.copyFile(original, testFile);
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, testFile.getPath());
controller.assertValid();
original = new File("target/test-scripts/optionalValidators.js");
FileUtils.copyFile(original, testFile);
controller.setProperty(ExecuteScript.SCRIPT_CHECK_INTERVAL, "5 secs");
Thread.sleep(6000);
controller.assertNotValid();
controller.setProperty("int", "abc");
controller.assertNotValid();
controller.setProperty("url", "not@valid");
controller.assertNotValid();
controller.setProperty("nonEmpty", "");
controller.assertNotValid();
controller.setProperty("int", "123");
controller.setProperty("url", "http://localhost");
controller.setProperty("nonEmpty", "abc123");
controller.assertValid();
FileUtils.deleteQuietly(testFile);
}
// ////////////////////////////////// // Python script tests
@Test
public void testSimpleReadP() {
LOG.info("Python script: fail file based on reading contents");
for (int i = 0; i < 20; i++) {
Map<String, String> attr1 = new HashMap<>();
attr1.put("filename", "FineStuff");
attr1.put("counter", Integer.toString(i));
Map<String, String> attr2 = new HashMap<>();
attr2.put("filename", "MultiLine");
attr2.put("counter", Integer.toString(i));
controller.enqueue("This stuff is fine".getBytes(), attr1);
controller.enqueue(multiline.getBytes(), attr2);
}
controller.setThreadCount(40);
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, "target/test-scripts/readTest.py");
controller.run(40);
assertRelationshipContents(multiline, "failure", 20);
assertRelationshipContents("This stuff is fine", "success", 20);
List<MockFlowFile> fails = controller.getFlowFilesForRelationship("failure");
List<MockFlowFile> successes = controller.getFlowFilesForRelationship("success");
for (int i = 0; i < 20; i++) {
assertTrue(fails.get(i).getAttribute("filename").matches("^.*\\d+$"));
assertTrue(successes.get(i).getAttribute("filename").matches("^.*\\d+$"));
}
}
@Test
public void testParamReadP() {
LOG.info("Python script: read contents and fail based on parameter");
controller.enqueue("This stuff is fine".getBytes());
controller.enqueue(multiline.getBytes());
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, "target/test-scripts/readWithParams.py");
controller.setProperty("expr", "sed do");
controller.run(2);
assertRelationshipContents(multiline, "failure");
assertRelationshipContents("This stuff is fine", "success");
}
@Test
public void testWriteLastLineP() {
LOG.info("Running Python script to output last line of file");
controller.enqueue(multiline.getBytes());
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, "target/test-scripts/writeTest.py");
controller.run();
List<MockFlowFile> sunkFiles = controller.getFlowFilesForRelationship("success");
assertEquals("Process did not generate an output file", 1, sunkFiles.size());
MockFlowFile sunkFile = sunkFiles.iterator().next();
byte[] blob = sunkFile.toByteArray();
String[] lines = new String(blob).split("\n");
assertEquals("File had more than one line", 1, lines.length);
assertEquals("sunt in culpa qui officia deserunt mollit anim id est laborum.", lines[0]);
}
@Test
public void testWriteOptionalParametersP() {
LOG.info("Python script processCallback that uses optional parameters");
controller.enqueue(multiline.getBytes());
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, "target/test-scripts/paramTest.py");
controller.setProperty("repeat", "3");
controller.run();
List<MockFlowFile> sunkFiles = controller.getFlowFilesForRelationship("success");
assertEquals("Process did not generate an output file", 1, sunkFiles.size());
MockFlowFile sunkFile = sunkFiles.iterator().next();
byte[] blob = sunkFile.toByteArray();
String[] lines = new String(blob).split("\n");
assertEquals("File did not have 3 lines", 3, lines.length);
assertTrue(lines[2].startsWith("sunt in culpa qui officia deserunt mollit anim id est laborum."));
}
@Test
public void testManualValidationP() {
LOG.info("Python defining manual validator");
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, "target/test-scripts/alwaysFail.py");
controller.assertNotValid();
}
@Test
public void testSetupOptionalValidationP() {
LOG.info("Python script creating validators for optional properties");
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, "target/test-scripts/optionalValidators.py");
controller.setProperty("int", "abc");
controller.setProperty("url", "not@valid");
controller.setProperty("nonEmpty", "");
assertEquals(2, controller.getProcessor().getPropertyDescriptors().size());
controller.assertNotValid();
controller.setProperty("int", "123");
controller.setProperty("url", "http://localhost");
controller.setProperty("nonEmpty", "abc123");
assertEquals(5, controller.getProcessor().getPropertyDescriptors().size());
controller.assertValid();
}
@Test
public void testGetExceptionRouteP() {
LOG.info("Python script defining route taken in event of exception");
controller.enqueue("This stuff is fine".getBytes());
controller.enqueue("Bad things go to 'b'.".getBytes());
controller.enqueue(multiline.getBytes());
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, "target/test-scripts/routeTest.py");
// Don't put the error in the logs
// TestableAppender ta = new TestableAppender();
// ta.attach(Logger.getLogger(ExecuteScript.class));
controller.run(3);
// ta.detach();
assertRelationshipContents("This stuff is fine", "a");
assertRelationshipContents("Bad things go to 'b'.", "b");
assertRelationshipContents(multiline, "c");
// ta.assertFound("threw exception");
}
@Test
public void testLoadLocalP() throws Exception {
final Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
testGetExceptionRouteP();
setupEach();
} catch (Exception e) {
}
}
});
t.start();
t.join();
LOG.info("Python: load another script file");
controller.enqueue("This stuff is fine".getBytes());
controller.enqueue(multiline.getBytes());
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, "target/test-scripts/loadLocal.py");
controller.run(2);
assertRelationshipContents(multiline, "failure");
assertRelationshipContents("This stuff is fine", "success");
}
@Test
public void testSimpleConverterP() {
LOG.info("Running Python converter script");
for (int i = 0; i < 20; i++) {
controller.enqueue(multiline.getBytes());
}
controller.setThreadCount(20);
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, "target/test-scripts/simpleConverter.py");
controller.run(20);
List<MockFlowFile> successFiles = controller.getFlowFilesForRelationship("success");
List<MockFlowFile> failFiles = controller.getFlowFilesForRelationship("failure");
assertEquals("Process did not generate 20 SUCCESS files", 20, successFiles.size());
assertEquals("Process did not generate 20 FAILURE files", 20, failFiles.size());
MockFlowFile sFile = successFiles.iterator().next();
MockFlowFile fFile = failFiles.iterator().next();
byte[] blob = sFile.toByteArray();
String[] lines = new String(blob).split("\n");
assertEquals("SUCCESS had wrong number of lines", 7, lines.length);
assertTrue(lines[0].startsWith("consectetur adipisicing elit,"));
blob = fFile.toByteArray();
lines = new String(blob).split("\n");
assertEquals("File had more than one line", 1, lines.length);
assertTrue(lines[0].startsWith("Lorem ipsum dolor sit amet,"));
}
@Test
public void testFlowFileP() {
LOG.info("Python: get FlowFile properties");
controller.enqueue("This is too short".getBytes());
controller.enqueue(multiline.getBytes());
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, "target/test-scripts/ffTest.py");
controller.run(2);
assertRelationshipContents(multiline, "success");
assertRelationshipContents("This is too short", "failure");
}
@Test
public void testMultiThreadExecP() {
LOG.info("Pthon script 20 threads: Failing file based on reading contents");
Map<String, String> attrs1 = new HashMap<>();
attrs1.put("filename", "StuffIsFine.txt");
Map<String, String> attrs2 = new HashMap<>();
attrs2.put("filename", "multiline.txt");
for (int i = 0; i < 10; i++) {
controller.enqueue("This stuff is fine".getBytes(), attrs1);
controller.enqueue(multiline.getBytes(), attrs2);
}
controller.setThreadCount(20);
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, "target/test-scripts/readWithParams.py");
controller.setProperty("expr", "sed do");
controller.run(20);
controller.assertTransferCount("failure", 10);
controller.assertTransferCount("success", 10);
for (int i = 0; i < 10; i++) {
MockFlowFile ff = controller.getFlowFilesForRelationship("failure").get(i);
ff.assertContentEquals(multiline);
assertTrue(ff.getAttribute("filename").endsWith("modified"));
ff = controller.getFlowFilesForRelationship("success").get(i);
ff.assertContentEquals("This stuff is fine");
assertTrue(ff.getAttribute("filename").endsWith("modified"));
}
}
@Test
public void testUpdateScriptP() throws Exception {
LOG.info("Test one script with updated class");
File testFile = File.createTempFile("script", ".py");
File original = new File("target/test-scripts/readTest.py");
FileUtils.copyFile(original, testFile);
controller.setProperty(ExecuteScript.SCRIPT_FILE_NAME, testFile.getPath());
controller.assertValid();
original = new File("target/test-scripts/readWithParams.py");
FileUtils.copyFile(original, testFile);
controller.setProperty(ExecuteScript.SCRIPT_CHECK_INTERVAL, "5 secs");
Thread.sleep(6000);
controller.assertNotValid(); // need to set 'expr'
controller.setProperty("int", "abc");
controller.assertNotValid();
controller.setProperty("url", "not@valid");
controller.assertNotValid();
controller.setProperty("nonEmpty", "");
controller.assertNotValid();
controller.setProperty("expr", "sed do");
controller.assertValid();
assertEquals(6, controller.getProcessContext().getProperties().size());
FileUtils.deleteQuietly(testFile);
}
}

View File

@ -1,24 +0,0 @@
/*
* 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.
*/
with (Scripting) {
var instance = new ReaderScript({
validate: function () {
return ["This will never work."];
}
});
}

View File

@ -1,19 +0,0 @@
# 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.
# type(name, superclass_tuple, member_dict) is a shorthand for defining an
# anonymous class. Note the trailing parens (), because scriptBuilder must
# return an *instance* of the class.
instance = type("FailingReader", (ReaderScript, object),\
{"validate": lambda self : ["I am broken"]})()

View File

@ -1,21 +0,0 @@
# 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.
class NeverSatisfied < ReaderScript
def validate
return ["This is supposed to fail"]
end
end
NeverSatisfied.new

View File

@ -1,28 +0,0 @@
/*
* 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.
*/
with (Scripting) {
var instance = new ReaderScript({
route: function (input) {
if (instance.getFlowFileSize() < 20) {
return Script.FAIL_RELATIONSHIP;
} else {
return Script.SUCCESS_RELATIONSHIP;
}
}
});
}

View File

@ -1,22 +0,0 @@
# 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.
class SimpleJythonReader(ReaderScript):
def route(self, input):
if self.getFlowFileSize() < 20 : return self.FAIL_RELATIONSHIP
return self.SUCCESS_RELATIONSHIP
instance = SimpleJythonReader()

View File

@ -1,30 +0,0 @@
# 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.
class SimpleJRubyReader < ReaderScript
field_reader :FAIL_RELATIONSHIP, :SUCCESS_RELATIONSHIP
def getRelationships
@@evict = Relationship::Builder.new.name("evict").description("some evicted stuff").build()
[FAIL_RELATIONSHIP, SUCCESS_RELATIONSHIP, @@evict]
end
def route( input )
return FAIL_RELATIONSHIP if getFlowFileSize < 20
return @@evict if !getAttribute("evict").nil?
return SUCCESS_RELATIONSHIP
end
end
SimpleJRubyReader.new

View File

@ -1,18 +0,0 @@
# 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.
class TestFilter:
def notAllowed(self):
return "^sed"

View File

@ -1,22 +0,0 @@
/*
* 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.
*/
function notAllowed() { // Works for eval(readFile(...))
return /sed do/i;
}
exports.notAllowed = notAllowed; // Works for require(...)

View File

@ -1,17 +0,0 @@
# 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.
def notAllowed
return /^sed/i
end

View File

@ -1,30 +0,0 @@
/*
* 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.
*/
var sub = require("lib/sub.js");
with (Scripting) {
var instance = new ReaderScript({
route: function (input) {
var str = IOUtils.toString(input);
if (str.match(sub.notAllowed())) {
return Script.FAIL_RELATIONSHIP;
} else {
return Script.SUCCESS_RELATIONSHIP;
}
}
});
}

View File

@ -1,26 +0,0 @@
# 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.
import re
from Sub import TestFilter
class SimpleJythonReader(ReaderScript):
def route(self, input):
tf = TestFilter()
for line in FileUtil.wrap(input):
if re.match(tf.notAllowed(),line): return self.FAIL_RELATIONSHIP
return self.SUCCESS_RELATIONSHIP
instance = SimpleJythonReader()

View File

@ -1,29 +0,0 @@
# 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.
require 'sub'
class SimpleJRubyReader < ReaderScript
field_reader :FAIL_RELATIONSHIP, :SUCCESS_RELATIONSHIP
def route( input )
input.to_io.each_line do |line|
return FAIL_RELATIONSHIP if line.match notAllowed
end
return SUCCESS_RELATIONSHIP
end
end
SimpleJRubyReader.new

View File

@ -1,54 +0,0 @@
<?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.
-->
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
<param name="Target" value="System.out"/>
<param name="Threshold" value="DEBUG"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{MM-dd-yy HH:mm:ss.SSS} %-5p %c{2} %x - %m%n"/>
</layout>
</appender>
<logger name="org.nifi.model.processor.FlowFileProcessor" additivity="false">
<level value="INFO"/>
<appender-ref ref="CONSOLE"/>
</logger>
<logger name="junit.TestableAppender" additivity="false">
<level value="TRACE"/>
<appender-ref ref="CONSOLE"/>
</logger>
<logger name="org.nifi.model.processor.impl.ScriptRunnerProcessor" additivity="false">
<level value="WARN"/>
<appender-ref ref="CONSOLE"/>
</logger>
<logger name="org.nifi.model.processor.impl.ScriptRunnerProcessorTest" additivity="false">
<level value="INFO"/>
<appender-ref ref="CONSOLE"/>
</logger>
<root>
<level value="INFO"/>
<appender-ref ref="CONSOLE" />
</root>
</log4j:configuration>

View File

@ -1,28 +0,0 @@
/*
* 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.
*/
with (Scripting) {
var instance = new WriterScript({
getPropertyDescriptors: function () {
i = new PropertyDescriptor.Builder().name("int").description("an int").required(true).addValidator(StandardValidators.POSITIVE_INTEGER_VALIDATOR).build();
u = new PropertyDescriptor.Builder().name("url").description("a url").required(true).addValidator(StandardValidators.URL_VALIDATOR).build();
s = new PropertyDescriptor.Builder().name("nonEmpty").description("a non empty property").required(true).addValidator(StandardValidators.NON_EMPTY_VALIDATOR).build();
return [i, u, s];
}
});
}

View File

@ -1,22 +0,0 @@
# 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.
class SimpleJythonReader(ReaderScript):
def getPropertyDescriptors( self ):
nev = PropertyDescriptor.Builder().name("nonEmpty").required(1).addValidator(StandardValidators.NON_EMPTY_VALIDATOR).build()
iv = PropertyDescriptor.Builder().name("int").required(1).addValidator(StandardValidators.POSITIVE_INTEGER_VALIDATOR).build()
uv = PropertyDescriptor.Builder().name("url").required(1).addValidator(StandardValidators.URL_VALIDATOR).build()
return [nev, iv, uv]
instance = SimpleJythonReader()

View File

@ -1,39 +0,0 @@
# 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.
class SimpleJRubyReader < ReaderScript
field_reader :FAIL_RELATIONSHIP, :SUCCESS_RELATIONSHIP, :logger
def getPropertyDescriptors
logger.debug("Defining descriptors");
i = StandardValidators::INTEGER_VALIDATOR
u = StandardValidators::URL_VALIDATOR
s = StandardValidators::NON_EMPTY_VALIDATOR
intPropDesc = PropertyDescriptor::Builder.new().name("int").required(true).addValidator(i).build()
urlPropDesc = PropertyDescriptor::Builder.new().name("url").required(true).addValidator(u).build()
nonEmptyPropDesc = PropertyDescriptor::Builder.new().name("nonEmpty").addValidator(s).build()
return [intPropDesc, urlPropDesc, nonEmptyPropDesc]
end
def route( input )
logger.debug("Routing input");
input.to_io.each_line do |line|
return FAIL_RELATIONSHIP if line.match /^sed/i
end
return SUCCESS_RELATIONSHIP
end
end
$logger.debug("Creating SimpleJRubyReader with props" + @properties.to_s)
SimpleJRubyReader.new

View File

@ -1,28 +0,0 @@
/*
* 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.
*/
with (Scripting) {
var instance = new WriterScript({
process: function (input, output) {
var str = IOUtils.toString(input);
var last = str.split("\n").pop() + "\n";
for (var i = 0; i < instance.getProperty("repeat"); i++) {
IOUtils.write(last, output);
}
output.flush();
}
});
}

View File

@ -1,26 +0,0 @@
# 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.
class ParamUsingWriter(WriterScript):
def process ( self, input, output ):
last = FileUtil.wrap(input).readlines()[-1] + '\n'
writer = FileUtil.wrap(output)
times = int(self.getProperty("repeat"))
lines = [last] * times
writer.writelines(lines)
writer.close()
instance = ParamUsingWriter()

View File

@ -1,31 +0,0 @@
# 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.
class ParamUsingWriter < WriterScript
def process ( input, output )
reader = input.to_io
writer = output.to_io
last = reader.readlines.last
getProperty("repeat").to_i.times do
writer << last + "\n"
end
writer.close
reader.close
end
end
ParamUsingWriter.new

View File

@ -1,36 +0,0 @@
/*
* 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.
*/
with (Scripting) {
var instance = new WriterScript({
process: function (istream, ostream) {
var str = IOUtils.toString(istream);
var obj = new XML(str);
print(obj)
var good = obj.b.(@good == "true");
if (good.length() == 0) {
instance.setRoute(Script.FAIL_RELATIONSHIP);
IOUtils.write(str, ostream);
} else {
instance.setRoute(Script.SUCCESS_RELATIONSHIP);
for each (var goodStr in good) {
IOUtils.write(goodStr, ostream);
}
}
ostream.flush();
}
});
}

View File

@ -1,30 +0,0 @@
/*
* 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.
*/
with (Scripting) {
var instance = new ReaderScript({
route: function (input) {
str = IOUtils.toString(input);
if (str.match(/sed do/i)) {
return Script.FAIL_RELATIONSHIP;
} else {
return Script.SUCCESS_RELATIONSHIP;
}
}
});
logger.debug("Got a logger and properties" + properties);
}

View File

@ -1,32 +0,0 @@
# 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.
import os, re, subprocess
class SimpleJythonReader(ReaderScript):
def route(self, input):
logger.info("In route")
returnid = os.system("c:\\cygwin\\bin\\echo GOOD")
fname = self.getAttribute("filename")
counter = self.getAttribute("counter")
fname = fname + '.' + counter
self.setAttribute("filename", fname)
for line in FileUtil.wrap(input):
if re.match("^sed",line): return self.FAIL_RELATIONSHIP
return self.SUCCESS_RELATIONSHIP
instance = SimpleJythonReader()

View File

@ -1,30 +0,0 @@
# 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.
class SimpleJRubyReader < ReaderScript
field_reader :FAIL_RELATIONSHIP, :SUCCESS_RELATIONSHIP, :logger, :attributes
def route( input )
logger.info("Route Input")
input.to_io.each_line do |line|
return FAIL_RELATIONSHIP if line.match /^sed/i
end
attributes.put("filename", "NewFileNameFromReadTest")
return SUCCESS_RELATIONSHIP
end
end
$logger.info("Logger is made available in shared variables...however, the SimpleJRubyReader.logger is not set till after this script returns")
SimpleJRubyReader.new

View File

@ -1,32 +0,0 @@
/*
* 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.
*/
with (Scripting) {
var instance = new ReaderScript({
route: function (input) {
var str = IOUtils.toString(input);
var expr = instance.getProperty("expr");
filename = instance.attributes.get("filename");
instance.setAttribute("filename", filename + ".modified");
if (str.match(expr)) {
return Script.FAIL_RELATIONSHIP;
} else {
return Script.SUCCESS_RELATIONSHIP;
}
}
});
}

View File

@ -1,32 +0,0 @@
# 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.
import re
class SimpleJythonReader(ReaderScript):
def getPropertyDescriptors( self ):
nev = StandardValidators.NON_EMPTY_VALIDATOR
return [PropertyDescriptor.Builder().name("expr").required(1).addValidator(nev).build()]
def route( self, input ):
expr = self.getProperty("expr")
filename = self.getAttribute("filename")
self.setAttribute("filename", filename + ".modified")
for line in FileUtil.wrap(input):
if re.match(expr, line): return self.FAIL_RELATIONSHIP
return self.SUCCESS_RELATIONSHIP
instance = SimpleJythonReader()

View File

@ -1,33 +0,0 @@
# 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.
class SimpleJRubyReader < ReaderScript
field_reader :FAIL_RELATIONSHIP, :SUCCESS_RELATIONSHIP, :properties, :attributes
def route( input )
expr = properties.get "expr"
raise "Must specify the 'expr' property!" if expr.nil?
filename = attributes.get "filename"
setAttribute("filename", filename + ".modified")
input.to_io.each_line do |line|
return FAIL_RELATIONSHIP if line.match expr
end
return SUCCESS_RELATIONSHIP
end
end
$logger.debug("Can access logger and properties via shared instance variables...props = " + @properties.to_s)
SimpleJRubyReader.new

View File

@ -1,41 +0,0 @@
/*
* 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.
*/
with (Scripting) {
var a = new Relationship.Builder().name("a").description("some good stuff").build()
var b = new Relationship.Builder().name("b").description("some other stuff").build()
var c = new Relationship.Builder().name("c").description("some bad stuff").build()
var instance = new ReaderScript({
getExceptionRoute: function () {
return c;
},
getRelationships: function () {
return [a, b, c];
},
route: function (input) {
var str = IOUtils.toString(input);
var lines = str.split("\n");
for (var line in lines) {
if (lines[line].match(/^bad/i)) {
return b;
} else if (lines[line].match(/^sed/i)) {
throw "That's no good!";
}
}
return a;
}
});
}

View File

@ -1,37 +0,0 @@
# 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.
import re
class RoutingReader(ReaderScript):
A = Relationship.Builder().name("a").description("some good stuff").build()
B = Relationship.Builder().name("b").description("some other stuff").build()
C = Relationship.Builder().name("c").description("some bad stuff").build()
def getRelationships(self):
return [self.A,self.B,self.C]
def getExceptionRoute(self):
return self.C
def route( self, input ):
for line in FileUtil.wrap(input):
if re.match("^bad", line, re.IGNORECASE):
return self.B
if re.match("^sed", line):
raise RuntimeError("That's no good!")
return self.A
instance = RoutingReader()

View File

@ -1,39 +0,0 @@
# 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.
class SimpleJRubyReader < ReaderScript
@@a = Relationship::Builder.new().name("a").description("some good stuff").build()
@@b = Relationship::Builder.new().name("b").description("some bad stuff").build()
@@c = Relationship::Builder.new().name("c").description("some other stuff").build()
def getRelationships
return [@@a, @@b, @@c]
end
def getExceptionRoute
@@c
end
def route( input )
input.to_io.each_line do |line|
return @@b if line.match /^bad/i
raise "That's no good!" if line.match /^sed/i
end
@@a
end
end
SimpleJRubyReader.new

View File

@ -1,45 +0,0 @@
/*
* 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.
*/
with (Scripting) {
var instance = new ConverterScript({
convert: function (input) {
var buffReader = new java.io.BufferedReader(new java.io.InputStreamReader(input));
instance.createFlowFile("firstLine", Script.FAIL_RELATIONSHIP, function (output) {
var out = new java.io.BufferedWriter(new java.io.OutputStreamWriter(output));
var firstLine = buffReader.readLine();
out.write(firstLine, 0, firstLine.length());
out.flush();
out.close();
});
instance.createFlowFile("otherLines", Script.SUCCESS_RELATIONSHIP, function (output) {
var out = new java.io.BufferedWriter(new java.io.OutputStreamWriter(output));
var line = buffReader.readLine();
while (line != null) {
out.write(line, 0, line.length());
out.newLine();
line = buffReader.readLine();
}
out.flush();
out.close();
});
}
});
logger.debug("Processor props" + properties)
}

View File

@ -1,60 +0,0 @@
# 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.
from org.python.core.io import TextIOWrapper,BufferedReader,BufferedWriter,StreamIO
from org.apache.nifi.scripting import OutputStreamHandler
class WriteFirstLine(OutputStreamHandler):
def __init__(self, wrappedIn):
self.wrappedIn = wrappedIn
def write(self, output):
streamOut = StreamIO(output, False)
bufWrtr = BufferedWriter(streamOut, 8192)
wrappedOut = TextIOWrapper(bufWrtr)
wrappedOut.write(self.wrappedIn.readline(8192))
wrappedOut.flush()
wrappedOut.close()
class WriteOtherLines(OutputStreamHandler):
def __init__(self, wrappedIn):
self.wrappedIn = wrappedIn
def write(self, output):
streamOut = StreamIO(output, False)
bufWrtr = BufferedWriter(streamOut, 8192)
wrappedOut = TextIOWrapper(bufWrtr)
line = self.wrappedIn.readline(8192)
while line != '':
wrappedOut.write(line)
line = self.wrappedIn.readline(8192)
wrappedOut.flush()
wrappedOut.close()
class SimpleConverter(ConverterScript):
def convert(self, input):
streamIn = StreamIO(input, False)
bufRdr = BufferedReader(streamIn, 8192)
wrappedIn = TextIOWrapper(bufRdr)
writeFirstLine = WriteFirstLine(wrappedIn)
self.createFlowFile("firstLine", self.FAIL_RELATIONSHIP, writeFirstLine)
writeOtherLines = WriteOtherLines(wrappedIn)
self.createFlowFile("otherLines", self.SUCCESS_RELATIONSHIP, writeOtherLines)
instance = SimpleConverter()

View File

@ -1,42 +0,0 @@
# 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.
java_import 'org.apache.nifi.scripting.OutputStreamHandler'
class SimpleConverter < ConverterScript
field_reader :FAIL_RELATIONSHIP, :SUCCESS_RELATIONSHIP, :logger, :attributes
def convert(input)
in_io = input.to_io
createFlowFile("firstLine", FAIL_RELATIONSHIP, OutputStreamHandler.impl do |method, out|
out_io = out.to_io
out_io << in_io.readline.to_java_bytes
out_io.close
logger.debug("Wrote data to failure...this message logged with logger from super class")
end)
createFlowFile("otherLines", SUCCESS_RELATIONSHIP, OutputStreamHandler.impl do |method, out|
out_io = out.to_io
in_io.each_line { |line|
out_io << line
}
out_io.close
logger.debug("Wrote data to success...this message logged with logger from super class")
end)
in_io.close
end
end
$logger.debug("Creating SimpleConverter...this message logged with logger from shared variables")
SimpleConverter.new

View File

@ -1,26 +0,0 @@
/*
* 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.
*/
with (Scripting) {
var instance = new WriterScript({
process: function (input, output) {
var str = IOUtils.toString(input);
IOUtils.write(str.split("\n").pop(), output);
output.flush();
}
});
}

View File

@ -1,22 +0,0 @@
# 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.
class SimpleWriter(WriterScript):
def process( self, input, output ):
last = FileUtil.wrap(input).readlines()[-1]
writer = FileUtil.wrap(output)
writer.write(last)
writer.close()
instance = SimpleWriter()

View File

@ -1,32 +0,0 @@
# 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.
class SimpleJRubyRunner < WriterScript
def process( input, output )
in_io = input.to_io
out_io = output.to_io
last = nil
in_io.each_line do |line|
last = line
end
out_io << last unless last.nil?
in_io.close
out_io.close
end
end
SimpleJRubyRunner.new

View File

@ -1,36 +0,0 @@
<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/maven-v4_0_0.xsd">
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.nifi</groupId>
<artifactId>execute-script-bundle</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>execute-script-nar</artifactId>
<name>Execute Script NAR</name>
<packaging>nar</packaging>
<description>NiFi Script Running NAR</description>
<dependencies>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>execute-script-processors</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -1,81 +0,0 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.nifi</groupId>
<artifactId>nar-container-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>execute-script-bundle</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Execute Script Bundle</name>
<packaging>pom</packaging>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-processor-utils</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-stream-utils</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-utils</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-core-flowfile-attributes</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-mock</artifactId>
<version>0.0.1-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jruby</groupId>
<artifactId>jruby</artifactId>
<version>1.7.16.1</version>
</dependency>
<dependency>
<groupId>org.python</groupId>
<artifactId>jython-standalone</artifactId>
<version>2.7-b3</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
</dependencies>
</dependencyManagement>
<modules>
<module>execute-script-processors</module>
<module>nar</module>
</modules>
</project>

View File

@ -18,11 +18,11 @@
<parent> <parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>nifi-framework-parent</artifactId> <artifactId>nifi-framework-parent</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
</parent> </parent>
<artifactId>nifi-administration</artifactId> <artifactId>nifi-administration</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
<name>NiFi Administration</name> <name>NiFi Administration</name>
<build> <build>
<resources> <resources>

View File

@ -18,7 +18,7 @@
<parent> <parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>nifi-framework-parent</artifactId> <artifactId>nifi-framework-parent</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
</parent> </parent>
<artifactId>client-dto</artifactId> <artifactId>client-dto</artifactId>

View File

@ -18,7 +18,7 @@
<parent> <parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>nifi-framework-parent</artifactId> <artifactId>nifi-framework-parent</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
</parent> </parent>
<artifactId>cluster-authorization-provider</artifactId> <artifactId>cluster-authorization-provider</artifactId>

View File

@ -18,7 +18,7 @@
<parent> <parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>nifi-framework-parent</artifactId> <artifactId>nifi-framework-parent</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
</parent> </parent>
<artifactId>framework-cluster-protocol</artifactId> <artifactId>framework-cluster-protocol</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>

View File

@ -18,7 +18,7 @@
<parent> <parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>nifi-framework-parent</artifactId> <artifactId>nifi-framework-parent</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
</parent> </parent>
<artifactId>framework-cluster-web</artifactId> <artifactId>framework-cluster-web</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>

View File

@ -18,7 +18,7 @@
<parent> <parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>nifi-framework-parent</artifactId> <artifactId>nifi-framework-parent</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
</parent> </parent>
<artifactId>framework-cluster</artifactId> <artifactId>framework-cluster</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>

View File

@ -18,10 +18,10 @@
<parent> <parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>nifi-framework-parent</artifactId> <artifactId>nifi-framework-parent</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
</parent> </parent>
<artifactId>core-api</artifactId> <artifactId>core-api</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
<name>NiFi Core API</name> <name>NiFi Core API</name>
<dependencies> <dependencies>
<dependency> <dependency>

View File

@ -18,7 +18,7 @@
<parent> <parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>nifi-framework-parent</artifactId> <artifactId>nifi-framework-parent</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
</parent> </parent>
<artifactId>framework-core</artifactId> <artifactId>framework-core</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>

View File

@ -18,11 +18,11 @@
<parent> <parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>nifi-framework-parent</artifactId> <artifactId>nifi-framework-parent</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
</parent> </parent>
<artifactId>file-authorization-provider</artifactId> <artifactId>file-authorization-provider</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
<name>NiFi File Authorization Provider</name> <name>NiFi File Authorization Provider</name>
<build> <build>

View File

@ -17,7 +17,7 @@
<parent> <parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>nifi-framework-parent</artifactId> <artifactId>nifi-framework-parent</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
</parent> </parent>
<artifactId>nifi-nar</artifactId> <artifactId>nifi-nar</artifactId>

View File

@ -18,10 +18,10 @@
<parent> <parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>nifi-framework-bundle</artifactId> <artifactId>nifi-framework-bundle</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
</parent> </parent>
<artifactId>nifi-framework-parent</artifactId> <artifactId>nifi-framework-parent</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<name>NiFi Framework</name> <name>NiFi Framework</name>
<modules> <modules>

View File

@ -18,7 +18,7 @@
<parent> <parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>nifi-framework-parent</artifactId> <artifactId>nifi-framework-parent</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
</parent> </parent>
<artifactId>nifi-resources</artifactId> <artifactId>nifi-resources</artifactId>

View File

@ -17,7 +17,7 @@
<parent> <parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>nifi-framework-parent</artifactId> <artifactId>nifi-framework-parent</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
</parent> </parent>
<artifactId>nifi-runtime</artifactId> <artifactId>nifi-runtime</artifactId>

View File

@ -17,11 +17,11 @@
<parent> <parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>nifi-framework-parent</artifactId> <artifactId>nifi-framework-parent</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
</parent> </parent>
<artifactId>nifi-security</artifactId> <artifactId>nifi-security</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
<name>NiFi Security</name> <name>NiFi Security</name>
<description>Contains security functionality common to NiFi.</description> <description>Contains security functionality common to NiFi.</description>
<dependencies> <dependencies>

View File

@ -18,11 +18,11 @@
<parent> <parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>nifi-framework-parent</artifactId> <artifactId>nifi-framework-parent</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
</parent> </parent>
<artifactId>site-to-site</artifactId> <artifactId>site-to-site</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
<name>NiFi Site-to-Site</name> <name>NiFi Site-to-Site</name>
<dependencies> <dependencies>
<dependency> <!-- This can be removed after testing.... --> <dependency> <!-- This can be removed after testing.... -->

View File

@ -18,10 +18,10 @@
<parent> <parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>nifi-framework-parent</artifactId> <artifactId>nifi-framework-parent</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
</parent> </parent>
<artifactId>nifi-user-actions</artifactId> <artifactId>nifi-user-actions</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
<name>NiFi User Actions</name> <name>NiFi User Actions</name>
</project> </project>

View File

@ -18,7 +18,7 @@
<parent> <parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>web-parent</artifactId> <artifactId>web-parent</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
</parent> </parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>custom-ui-utilities</artifactId> <artifactId>custom-ui-utilities</artifactId>

View File

@ -18,7 +18,7 @@
<parent> <parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>web-parent</artifactId> <artifactId>web-parent</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
</parent> </parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>nifi-jetty</artifactId> <artifactId>nifi-jetty</artifactId>

View File

@ -18,7 +18,7 @@
<parent> <parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>web-parent</artifactId> <artifactId>web-parent</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
</parent> </parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>nifi-web-api</artifactId> <artifactId>nifi-web-api</artifactId>

View File

@ -17,7 +17,7 @@
<parent> <parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>web-parent</artifactId> <artifactId>web-parent</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
</parent> </parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>nifi-web-docs</artifactId> <artifactId>nifi-web-docs</artifactId>

View File

@ -17,7 +17,7 @@
<parent> <parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>web-parent</artifactId> <artifactId>web-parent</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
</parent> </parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>nifi-web-error</artifactId> <artifactId>nifi-web-error</artifactId>

View File

@ -18,7 +18,7 @@
<parent> <parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>web-parent</artifactId> <artifactId>web-parent</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-incubating-SNAPSHOT</version>
</parent> </parent>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>nifi-web</artifactId> <artifactId>nifi-web</artifactId>

Some files were not shown because too many files have changed in this diff Show More