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
|
## Table of Contents
|
||||||
|
|
||||||
- [Features](#features)
|
|
||||||
- [Getting Started](#getting-started)
|
- [Getting Started](#getting-started)
|
||||||
- [Getting Help](#getting-help)
|
|
||||||
- [Requirements](#requirements)
|
|
||||||
- [License](#license)
|
- [License](#license)
|
||||||
- [Disclaimer](#disclaimer)
|
- [Disclaimer](#disclaimer)
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
Apache NiFi supports powerful and scalable directed graphs of data routing, transformation, and system mediation logic. Some of the high-level capabilities and objectives of Apache NiFi include:
|
|
||||||
|
|
||||||
- Web-based user interface for seamless experience between design, control, feedback, and monitoring of data flows
|
|
||||||
- Highly configurable along several dimensions of quality of service such as loss tolerant versus guaranteed delivery, low latency versus high throughput, and priority based queuing
|
|
||||||
- Fine-grained data provenance for all data received, forked, joined, cloned, modified, sent, and ultimately dropped as data reaches its configured end-state
|
|
||||||
- Component-based extension model along well defined interfaces enabling rapid development and effective testing
|
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
Execute <nifi install dir>/bin/nifi.sh start
|
- Build the nar-maven-plugin. Change directory to 'nar-maven-plugin' and
|
||||||
|
follow the directions found there.
|
||||||
## Getting Help
|
- Build nifi. Change directory to 'nifi' and follow the directions found there.
|
||||||
If you have questions, you can reach out to our mailing list: dev@nifi.incubator.apache.org
|
|
||||||
([archive](http://mail-archives.apache.org/mod_mbox/incubator-nifi-dev)).
|
|
||||||
We're also often available in IRC: #nifi on
|
|
||||||
[irc.freenode.net](http://webchat.freenode.net/?channels=#nifi).
|
|
||||||
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
* JDK 1.7 or higher
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
@ -64,32 +44,3 @@ While incubation status is not necessarily a reflection of the completeness
|
||||||
or stability of the code, it does indicate that the project has yet to be
|
or stability of the code, it does indicate that the project has yet to be
|
||||||
fully endorsed by the ASF.
|
fully endorsed by the ASF.
|
||||||
|
|
||||||
## Export Control
|
|
||||||
|
|
||||||
This distribution includes cryptographic software. The country in which you
|
|
||||||
currently reside may have restrictions on the import, possession, use, and/or
|
|
||||||
re-export to another country, of encryption software. BEFORE using any
|
|
||||||
encryption software, please check your country's laws, regulations and
|
|
||||||
policies concerning the import, possession, or use, and re-export of encryption
|
|
||||||
software, to see if this is permitted. See <http://www.wassenaar.org/> for more
|
|
||||||
information.
|
|
||||||
|
|
||||||
The U.S. Government Department of Commerce, Bureau of Industry and Security
|
|
||||||
(BIS), has classified this software as Export Commodity Control Number (ECCN)
|
|
||||||
5D002.C.1, which includes information security software using or performing
|
|
||||||
cryptographic functions with asymmetric algorithms. The form and manner of this
|
|
||||||
Apache Software Foundation distribution makes it eligible for export under the
|
|
||||||
License Exception ENC Technology Software Unrestricted (TSU) exception (see the
|
|
||||||
BIS Export Administration Regulations, Section 740.13) for both object code and
|
|
||||||
source code.
|
|
||||||
|
|
||||||
The following provides more details on the included cryptographic software:
|
|
||||||
|
|
||||||
Apache NiFi uses BouncyCastle, Jasypt, JCraft Inc., and the built-in
|
|
||||||
java cryptography libraries for SSL, SSH, and the protection
|
|
||||||
of sensitive configuration parameters. See
|
|
||||||
http://bouncycastle.org/about.html
|
|
||||||
http://www.jasypt.org/faq.html
|
|
||||||
http://jcraft.com/c-info.html
|
|
||||||
http://www.oracle.com/us/products/export/export-regulations-345813.html
|
|
||||||
for more details on each of these libraries cryptography features.
|
|
||||||
|
|
|
@ -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
|
Apache NiFi
|
||||||
Copyright 2014 The Apache Software Foundation
|
Copyright 2014-2015 The Apache Software Foundation
|
||||||
|
|
||||||
This product includes software developed at
|
This product includes software developed at
|
||||||
The Apache Software Foundation (http://www.apache.org/).
|
The Apache Software Foundation (http://www.apache.org/).
|
|
@ -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>
|
<parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>nifi-parent</artifactId>
|
<artifactId>nifi-parent</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>nifi</artifactId>
|
<artifactId>nifi</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
<name>NiFi Release</name>
|
<name>NiFi Release</name>
|
||||||
<description>This is the assembly Apache NiFi (incubating)</description>
|
<description>This is the assembly Apache NiFi (incubating)</description>
|
||||||
|
@ -385,16 +385,16 @@
|
||||||
<directory>/opt/nifi/nifi-${project.version}</directory>
|
<directory>/opt/nifi/nifi-${project.version}</directory>
|
||||||
<sources>
|
<sources>
|
||||||
<source>
|
<source>
|
||||||
<location>../../LICENSE</location>
|
<location>../LICENSE</location>
|
||||||
</source>
|
</source>
|
||||||
<source>
|
<source>
|
||||||
<location>../../NOTICE</location>
|
<location>../NOTICE</location>
|
||||||
</source>
|
</source>
|
||||||
<source>
|
<source>
|
||||||
<location>../../DISCLAIMER</location>
|
<location>../DISCLAIMER</location>
|
||||||
</source>
|
</source>
|
||||||
<source>
|
<source>
|
||||||
<location>../../README.md</location>
|
<location>../README.md</location>
|
||||||
<destination>README</destination>
|
<destination>README</destination>
|
||||||
</source>
|
</source>
|
||||||
</sources>
|
</sources>
|
||||||
|
|
|
@ -109,28 +109,28 @@
|
||||||
</dependencySets>
|
</dependencySets>
|
||||||
<files>
|
<files>
|
||||||
<file>
|
<file>
|
||||||
<source>../../README.md</source>
|
<source>../README.md</source>
|
||||||
<outputDirectory>./</outputDirectory>
|
<outputDirectory>./</outputDirectory>
|
||||||
<destName>README</destName>
|
<destName>README</destName>
|
||||||
<fileMode>0644</fileMode>
|
<fileMode>0644</fileMode>
|
||||||
<filtered>true</filtered>
|
<filtered>true</filtered>
|
||||||
</file>
|
</file>
|
||||||
<file>
|
<file>
|
||||||
<source>../../DISCLAIMER</source>
|
<source>../DISCLAIMER</source>
|
||||||
<outputDirectory>./</outputDirectory>
|
<outputDirectory>./</outputDirectory>
|
||||||
<destName>DISCLAIMER</destName>
|
<destName>DISCLAIMER</destName>
|
||||||
<fileMode>0644</fileMode>
|
<fileMode>0644</fileMode>
|
||||||
<filtered>true</filtered>
|
<filtered>true</filtered>
|
||||||
</file>
|
</file>
|
||||||
<file>
|
<file>
|
||||||
<source>../../LICENSE</source>
|
<source>../LICENSE</source>
|
||||||
<outputDirectory>./</outputDirectory>
|
<outputDirectory>./</outputDirectory>
|
||||||
<destName>LICENSE</destName>
|
<destName>LICENSE</destName>
|
||||||
<fileMode>0644</fileMode>
|
<fileMode>0644</fileMode>
|
||||||
<filtered>true</filtered>
|
<filtered>true</filtered>
|
||||||
</file>
|
</file>
|
||||||
<file>
|
<file>
|
||||||
<source>../../NOTICE</source>
|
<source>../NOTICE</source>
|
||||||
<outputDirectory>./</outputDirectory>
|
<outputDirectory>./</outputDirectory>
|
||||||
<destName>NOTICE</destName>
|
<destName>NOTICE</destName>
|
||||||
<fileMode>0644</fileMode>
|
<fileMode>0644</fileMode>
|
||||||
|
|
|
@ -18,11 +18,11 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>nifi-commons-parent</artifactId>
|
<artifactId>nifi-commons-parent</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>data-provenance-utils</artifactId>
|
<artifactId>data-provenance-utils</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<name>data-provenance-utils</name>
|
<name>data-provenance-utils</name>
|
||||||
|
|
|
@ -18,11 +18,11 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>nifi-commons-parent</artifactId>
|
<artifactId>nifi-commons-parent</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>flowfile-packager</artifactId>
|
<artifactId>flowfile-packager</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<name>FlowFile Packager</name>
|
<name>FlowFile Packager</name>
|
||||||
|
|
|
@ -18,11 +18,11 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>nifi-commons-parent</artifactId>
|
<artifactId>nifi-commons-parent</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>nifi-expression-language</artifactId>
|
<artifactId>nifi-expression-language</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
|
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
<name>NiFi Expression Language</name>
|
<name>NiFi Expression Language</name>
|
||||||
|
|
|
@ -19,11 +19,11 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>nifi-commons-parent</artifactId>
|
<artifactId>nifi-commons-parent</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>nifi-logging-utils</artifactId>
|
<artifactId>nifi-logging-utils</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
<name>NiFi Logging Utils</name>
|
<name>NiFi Logging Utils</name>
|
||||||
<description>Utilities for logging</description>
|
<description>Utilities for logging</description>
|
||||||
|
|
||||||
|
|
|
@ -19,11 +19,11 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>nifi-commons-parent</artifactId>
|
<artifactId>nifi-commons-parent</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>nifi-properties</artifactId>
|
<artifactId>nifi-properties</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
<name>NiFi Properties</name>
|
<name>NiFi Properties</name>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -18,11 +18,11 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>nifi-commons-parent</artifactId>
|
<artifactId>nifi-commons-parent</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>nifi-security-utils</artifactId>
|
<artifactId>nifi-security-utils</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
<name>NiFi Security Utils</name>
|
<name>NiFi Security Utils</name>
|
||||||
<description>Contains security functionality.</description>
|
<description>Contains security functionality.</description>
|
||||||
|
|
||||||
|
|
|
@ -19,11 +19,11 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>nifi-commons-parent</artifactId>
|
<artifactId>nifi-commons-parent</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>nifi-socket-utils</artifactId>
|
<artifactId>nifi-socket-utils</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
<name>NiFi Socket Utils</name>
|
<name>NiFi Socket Utils</name>
|
||||||
<description>Utilities for socket communication</description>
|
<description>Utilities for socket communication</description>
|
||||||
|
|
||||||
|
|
|
@ -19,11 +19,11 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>nifi-commons-parent</artifactId>
|
<artifactId>nifi-commons-parent</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>nifi-utils</artifactId>
|
<artifactId>nifi-utils</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
<name>NiFi Utils</name>
|
<name>NiFi Utils</name>
|
||||||
<!--
|
<!--
|
||||||
|
|
|
@ -19,11 +19,11 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>nifi-commons-parent</artifactId>
|
<artifactId>nifi-commons-parent</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>nifi-web-utils</artifactId>
|
<artifactId>nifi-web-utils</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
<name>NiFi Web Utils</name>
|
<name>NiFi Web Utils</name>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>nifi-parent</artifactId>
|
<artifactId>nifi-parent</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
|
|
|
@ -19,11 +19,11 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>nifi-commons-parent</artifactId>
|
<artifactId>nifi-commons-parent</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>nifi-processor-utils</artifactId>
|
<artifactId>nifi-processor-utils</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
<name>NiFi Processor Utils</name>
|
<name>NiFi Processor Utils</name>
|
||||||
|
|
||||||
|
|
|
@ -19,11 +19,11 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>nifi-commons-parent</artifactId>
|
<artifactId>nifi-commons-parent</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>wali</artifactId>
|
<artifactId>wali</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<name>WALI : Write-Ahead Log Implementation</name>
|
<name>WALI : Write-Ahead Log Implementation</name>
|
||||||
|
|
|
@ -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>
|
<parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>nifi-framework-parent</artifactId>
|
<artifactId>nifi-framework-parent</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>nifi-administration</artifactId>
|
<artifactId>nifi-administration</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
<name>NiFi Administration</name>
|
<name>NiFi Administration</name>
|
||||||
<build>
|
<build>
|
||||||
<resources>
|
<resources>
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>nifi-framework-parent</artifactId>
|
<artifactId>nifi-framework-parent</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>client-dto</artifactId>
|
<artifactId>client-dto</artifactId>
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>nifi-framework-parent</artifactId>
|
<artifactId>nifi-framework-parent</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>cluster-authorization-provider</artifactId>
|
<artifactId>cluster-authorization-provider</artifactId>
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>nifi-framework-parent</artifactId>
|
<artifactId>nifi-framework-parent</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>framework-cluster-protocol</artifactId>
|
<artifactId>framework-cluster-protocol</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>nifi-framework-parent</artifactId>
|
<artifactId>nifi-framework-parent</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>framework-cluster-web</artifactId>
|
<artifactId>framework-cluster-web</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>nifi-framework-parent</artifactId>
|
<artifactId>nifi-framework-parent</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>framework-cluster</artifactId>
|
<artifactId>framework-cluster</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
|
@ -18,10 +18,10 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>nifi-framework-parent</artifactId>
|
<artifactId>nifi-framework-parent</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>core-api</artifactId>
|
<artifactId>core-api</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
<name>NiFi Core API</name>
|
<name>NiFi Core API</name>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>nifi-framework-parent</artifactId>
|
<artifactId>nifi-framework-parent</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>framework-core</artifactId>
|
<artifactId>framework-core</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
|
@ -18,11 +18,11 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>nifi-framework-parent</artifactId>
|
<artifactId>nifi-framework-parent</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>file-authorization-provider</artifactId>
|
<artifactId>file-authorization-provider</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
<name>NiFi File Authorization Provider</name>
|
<name>NiFi File Authorization Provider</name>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>nifi-framework-parent</artifactId>
|
<artifactId>nifi-framework-parent</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>nifi-nar</artifactId>
|
<artifactId>nifi-nar</artifactId>
|
||||||
|
|
|
@ -18,10 +18,10 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>nifi-framework-bundle</artifactId>
|
<artifactId>nifi-framework-bundle</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>nifi-framework-parent</artifactId>
|
<artifactId>nifi-framework-parent</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
<name>NiFi Framework</name>
|
<name>NiFi Framework</name>
|
||||||
<modules>
|
<modules>
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>nifi-framework-parent</artifactId>
|
<artifactId>nifi-framework-parent</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>nifi-resources</artifactId>
|
<artifactId>nifi-resources</artifactId>
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>nifi-framework-parent</artifactId>
|
<artifactId>nifi-framework-parent</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>nifi-runtime</artifactId>
|
<artifactId>nifi-runtime</artifactId>
|
||||||
|
|
|
@ -17,11 +17,11 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>nifi-framework-parent</artifactId>
|
<artifactId>nifi-framework-parent</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>nifi-security</artifactId>
|
<artifactId>nifi-security</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
<name>NiFi Security</name>
|
<name>NiFi Security</name>
|
||||||
<description>Contains security functionality common to NiFi.</description>
|
<description>Contains security functionality common to NiFi.</description>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|
|
@ -18,11 +18,11 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>nifi-framework-parent</artifactId>
|
<artifactId>nifi-framework-parent</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>site-to-site</artifactId>
|
<artifactId>site-to-site</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
<name>NiFi Site-to-Site</name>
|
<name>NiFi Site-to-Site</name>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency> <!-- This can be removed after testing.... -->
|
<dependency> <!-- This can be removed after testing.... -->
|
||||||
|
|
|
@ -18,10 +18,10 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>nifi-framework-parent</artifactId>
|
<artifactId>nifi-framework-parent</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>nifi-user-actions</artifactId>
|
<artifactId>nifi-user-actions</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
<name>NiFi User Actions</name>
|
<name>NiFi User Actions</name>
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>web-parent</artifactId>
|
<artifactId>web-parent</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>custom-ui-utilities</artifactId>
|
<artifactId>custom-ui-utilities</artifactId>
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>web-parent</artifactId>
|
<artifactId>web-parent</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>nifi-jetty</artifactId>
|
<artifactId>nifi-jetty</artifactId>
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>web-parent</artifactId>
|
<artifactId>web-parent</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>nifi-web-api</artifactId>
|
<artifactId>nifi-web-api</artifactId>
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>web-parent</artifactId>
|
<artifactId>web-parent</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>nifi-web-docs</artifactId>
|
<artifactId>nifi-web-docs</artifactId>
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>web-parent</artifactId>
|
<artifactId>web-parent</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>nifi-web-error</artifactId>
|
<artifactId>nifi-web-error</artifactId>
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>web-parent</artifactId>
|
<artifactId>web-parent</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-incubating-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<groupId>org.apache.nifi</groupId>
|
<groupId>org.apache.nifi</groupId>
|
||||||
<artifactId>nifi-web</artifactId>
|
<artifactId>nifi-web</artifactId>
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue