mirror of https://github.com/apache/nifi.git
NIFI-270 sent an e-mail to dev explaining all the moving parts
This commit is contained in:
parent
6f189266b4
commit
bc94f716c6
55
README.md
55
README.md
|
@ -4,35 +4,15 @@ Apache NiFi is a dataflow system based on the concepts of flow-based programming
|
|||
|
||||
## Table of Contents
|
||||
|
||||
- [Features](#features)
|
||||
- [Getting Started](#getting-started)
|
||||
- [Getting Help](#getting-help)
|
||||
- [Requirements](#requirements)
|
||||
- [License](#license)
|
||||
- [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
|
||||
|
||||
Execute <nifi install dir>/bin/nifi.sh start
|
||||
|
||||
## 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
|
||||
- Build the nar-maven-plugin. Change directory to 'nar-maven-plugin' and
|
||||
follow the directions found there.
|
||||
- Build nifi. Change directory to 'nifi' and follow the directions found there.
|
||||
|
||||
## 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
|
||||
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.
|
||||
|
|
|
@ -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.
|
|
@ -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/).
|
|
@ -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.
|
||||
|
|
@ -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/
|
|
@ -1,5 +1,5 @@
|
|||
Apache NiFi
|
||||
Copyright 2014 The Apache Software Foundation
|
||||
Copyright 2014-2015 The Apache Software Foundation
|
||||
|
||||
This product includes software developed at
|
||||
The Apache Software Foundation (http://www.apache.org/).
|
|
@ -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.
|
|
@ -18,10 +18,10 @@
|
|||
<parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-parent</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>nifi</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>NiFi Release</name>
|
||||
<description>This is the assembly Apache NiFi (incubating)</description>
|
||||
|
@ -385,16 +385,16 @@
|
|||
<directory>/opt/nifi/nifi-${project.version}</directory>
|
||||
<sources>
|
||||
<source>
|
||||
<location>../../LICENSE</location>
|
||||
<location>../LICENSE</location>
|
||||
</source>
|
||||
<source>
|
||||
<location>../../NOTICE</location>
|
||||
<location>../NOTICE</location>
|
||||
</source>
|
||||
<source>
|
||||
<location>../../DISCLAIMER</location>
|
||||
<location>../DISCLAIMER</location>
|
||||
</source>
|
||||
<source>
|
||||
<location>../../README.md</location>
|
||||
<location>../README.md</location>
|
||||
<destination>README</destination>
|
||||
</source>
|
||||
</sources>
|
||||
|
|
|
@ -109,28 +109,28 @@
|
|||
</dependencySets>
|
||||
<files>
|
||||
<file>
|
||||
<source>../../README.md</source>
|
||||
<source>../README.md</source>
|
||||
<outputDirectory>./</outputDirectory>
|
||||
<destName>README</destName>
|
||||
<fileMode>0644</fileMode>
|
||||
<filtered>true</filtered>
|
||||
</file>
|
||||
<file>
|
||||
<source>../../DISCLAIMER</source>
|
||||
<source>../DISCLAIMER</source>
|
||||
<outputDirectory>./</outputDirectory>
|
||||
<destName>DISCLAIMER</destName>
|
||||
<fileMode>0644</fileMode>
|
||||
<filtered>true</filtered>
|
||||
</file>
|
||||
<file>
|
||||
<source>../../LICENSE</source>
|
||||
<source>../LICENSE</source>
|
||||
<outputDirectory>./</outputDirectory>
|
||||
<destName>LICENSE</destName>
|
||||
<fileMode>0644</fileMode>
|
||||
<filtered>true</filtered>
|
||||
</file>
|
||||
<file>
|
||||
<source>../../NOTICE</source>
|
||||
<source>../NOTICE</source>
|
||||
<outputDirectory>./</outputDirectory>
|
||||
<destName>NOTICE</destName>
|
||||
<fileMode>0644</fileMode>
|
||||
|
|
|
@ -18,11 +18,11 @@
|
|||
<parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-commons-parent</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>data-provenance-utils</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>data-provenance-utils</name>
|
||||
|
|
|
@ -18,11 +18,11 @@
|
|||
<parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-commons-parent</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>flowfile-packager</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>FlowFile Packager</name>
|
||||
|
|
|
@ -18,11 +18,11 @@
|
|||
<parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-commons-parent</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>nifi-expression-language</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
<name>NiFi Expression Language</name>
|
||||
|
|
|
@ -19,11 +19,11 @@
|
|||
<parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-commons-parent</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>nifi-logging-utils</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
<name>NiFi Logging Utils</name>
|
||||
<description>Utilities for logging</description>
|
||||
|
||||
|
|
|
@ -19,11 +19,11 @@
|
|||
<parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-commons-parent</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>nifi-properties</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
<name>NiFi Properties</name>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -18,11 +18,11 @@
|
|||
<parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-commons-parent</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>nifi-security-utils</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
<name>NiFi Security Utils</name>
|
||||
<description>Contains security functionality.</description>
|
||||
|
||||
|
|
|
@ -19,11 +19,11 @@
|
|||
<parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-commons-parent</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>nifi-socket-utils</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
<name>NiFi Socket Utils</name>
|
||||
<description>Utilities for socket communication</description>
|
||||
|
||||
|
|
|
@ -19,11 +19,11 @@
|
|||
<parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-commons-parent</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>nifi-utils</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
<name>NiFi Utils</name>
|
||||
<!--
|
||||
|
|
|
@ -19,11 +19,11 @@
|
|||
<parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-commons-parent</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>nifi-web-utils</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
<name>NiFi Web Utils</name>
|
||||
|
||||
<dependencies>
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
<parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-parent</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
|
|
|
@ -19,11 +19,11 @@
|
|||
<parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-commons-parent</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>nifi-processor-utils</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
<name>NiFi Processor Utils</name>
|
||||
|
||||
|
|
|
@ -19,11 +19,11 @@
|
|||
<parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-commons-parent</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>wali</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>WALI : Write-Ahead Log Implementation</name>
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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>
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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."];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -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"]})()
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
|
@ -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()
|
|
@ -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
|
|
@ -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"
|
||||
|
|
@ -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(...)
|
||||
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
|
@ -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()
|
|
@ -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
|
|
@ -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>
|
|
@ -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];
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
|
@ -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()
|
|
@ -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
|
|
@ -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();
|
||||
}
|
||||
});
|
||||
}
|
|
@ -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()
|
|
@ -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
|
|
@ -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();
|
||||
}
|
||||
});
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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()
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -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()
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
});
|
||||
}
|
|
@ -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()
|
|
@ -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
|
|
@ -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)
|
||||
}
|
|
@ -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()
|
||||
|
||||
|
|
@ -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
|
|
@ -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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -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()
|
|
@ -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
|
|
@ -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>
|
|
@ -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>
|
|
@ -18,11 +18,11 @@
|
|||
<parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-framework-parent</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>nifi-administration</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
<name>NiFi Administration</name>
|
||||
<build>
|
||||
<resources>
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
<parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-framework-parent</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>client-dto</artifactId>
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
<parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-framework-parent</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>cluster-authorization-provider</artifactId>
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
<parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-framework-parent</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>framework-cluster-protocol</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
<parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-framework-parent</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>framework-cluster-web</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
<parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-framework-parent</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>framework-cluster</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
|
|
@ -18,10 +18,10 @@
|
|||
<parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-framework-parent</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>core-api</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
<name>NiFi Core API</name>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
<parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-framework-parent</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>framework-core</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
|
|
@ -18,11 +18,11 @@
|
|||
<parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-framework-parent</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>file-authorization-provider</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
<name>NiFi File Authorization Provider</name>
|
||||
|
||||
<build>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-framework-parent</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>nifi-nar</artifactId>
|
||||
|
|
|
@ -18,10 +18,10 @@
|
|||
<parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-framework-bundle</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>nifi-framework-parent</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>NiFi Framework</name>
|
||||
<modules>
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
<parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-framework-parent</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>nifi-resources</artifactId>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-framework-parent</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>nifi-runtime</artifactId>
|
||||
|
|
|
@ -17,11 +17,11 @@
|
|||
<parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-framework-parent</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>nifi-security</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
<name>NiFi Security</name>
|
||||
<description>Contains security functionality common to NiFi.</description>
|
||||
<dependencies>
|
||||
|
|
|
@ -18,11 +18,11 @@
|
|||
<parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-framework-parent</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>site-to-site</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
<name>NiFi Site-to-Site</name>
|
||||
<dependencies>
|
||||
<dependency> <!-- This can be removed after testing.... -->
|
||||
|
|
|
@ -18,10 +18,10 @@
|
|||
<parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-framework-parent</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>nifi-user-actions</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
<name>NiFi User Actions</name>
|
||||
</project>
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
<parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>web-parent</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>custom-ui-utilities</artifactId>
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
<parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>web-parent</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-jetty</artifactId>
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
<parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>web-parent</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-web-api</artifactId>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>web-parent</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-web-docs</artifactId>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>web-parent</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-web-error</artifactId>
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
<parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>web-parent</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-web</artifactId>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue