Merge remote-tracking branch 'upstream/develop' into nifi-solr-bundle

Conflicts:
	nifi/nifi-assembly/pom.xml
	nifi/nifi-nar-bundles/pom.xml
This commit is contained in:
bbende 2015-04-13 20:03:30 -04:00
commit 751d601a92
144 changed files with 8531 additions and 573 deletions

View File

@ -501,6 +501,54 @@ The following binary components are provided under the Apache Software License v
Apache License Version 2.0 http://www.apache.org/licenses/. Apache License Version 2.0 http://www.apache.org/licenses/.
(c) Daniel Lemire, http://lemire.me/en/ (c) Daniel Lemire, http://lemire.me/en/
(ASLv2) Twitter4J
The following NOTICE information applies:
Copyright 2007 Yusuke Yamamoto
Twitter4J includes software from JSON.org to parse JSON response from the Twitter API. You can see the license term at http://www.JSON.org/license.html
(ASLv2) JOAuth
The following NOTICE information applies:
JOAuth
Copyright 2010-2013 Twitter, Inc
Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0
(ASLv2) Hosebird Client
The following NOTICE information applies:
Hosebird Client (hbc)
Copyright 2013 Twitter, Inc.
Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0
(ASLv2) GeoIP2 Java API
The following NOTICE information applies:
GeoIP2 Java API
This software is Copyright (c) 2013 by MaxMind, Inc.
This is free software, licensed under the Apache License, Version 2.0.
(ASLv2) Google HTTP Client Library for Java
The following NOTICE information applies:
Google HTTP Client Library for Java
This is free software, licensed under the Apache License, Version 2.0.
(ASLv2) Amazon Web Services SDK
The following NOTICE information applies:
Copyright 2010-2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
This product includes software developed by
Amazon Technologies, Inc (http://www.amazon.com/).
**********************
THIRD PARTY COMPONENTS
**********************
This software includes third party software subject to the following copyrights:
- XML parsing and utility functions from JetS3t - Copyright 2006-2009 James Murty.
- JSON parsing and utility functions from JSON.org - Copyright 2002 JSON.org.
- PKCS#1 PEM encoded private key parsing and utility functions from oauth.googlecode.com - Copyright 1998-2010 AOL Inc.
************************ ************************
Common Development and Distribution License 1.1 Common Development and Distribution License 1.1
@ -540,6 +588,14 @@ The following binary components are provided under the Common Development and Di
(CDDL 1.0) JavaServer Pages(TM) API (javax.servlet.jsp:jsp-api:jar:2.1 - http://jsp.java.net) (CDDL 1.0) JavaServer Pages(TM) API (javax.servlet.jsp:jsp-api:jar:2.1 - http://jsp.java.net)
(CDDL 1.0) SR 250 Common Annotations For The JavaTM Platform (javax.annotation:jsr250-api:jar:1.0 - http://jcp.org/aboutJava/communityprocess/final/jsr250/index.html) (CDDL 1.0) SR 250 Common Annotations For The JavaTM Platform (javax.annotation:jsr250-api:jar:1.0 - http://jcp.org/aboutJava/communityprocess/final/jsr250/index.html)
************************
Creative Commons Attribution-ShareAlike 3.0
************************
The following binary components are provided under the Creative Commons Attribution-ShareAlike 3.0. See project link for details.
(CCAS 3.0) MaxMind DB (https://github.com/maxmind/MaxMind-DB)
************************ ************************
Eclipse Public License 1.0 Eclipse Public License 1.0
************************ ************************
@ -559,6 +615,15 @@ The following binary components are provided under the Mozilla Public License v2
(MPL 2.0) Saxon HE (net.sf.saxon:Saxon-HE:jar:9.6.0-4 - http://www.saxonica.com/) (MPL 2.0) Saxon HE (net.sf.saxon:Saxon-HE:jar:9.6.0-4 - http://www.saxonica.com/)
*****************
Mozilla Public License v1.1
*****************
The following binary components are provided under the Mozilla Public License v1.1. See project link for details.
(MPL 1.1) HAPI Base (ca.uhn.hapi:hapi-base:2.2 - http://hl7api.sourceforge.net/)
(MPL 1.1) HAPI Structures (ca.uhn.hapi:hapi-structures-v*:2.2 - http://hl7api.sourceforge.net/)
***************** *****************
Public Domain Public Domain
***************** *****************

View File

@ -1,462 +1,489 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor
Licensed to the Apache Software Foundation (ASF) under one or more license agreements. See the NOTICE file distributed with this work for additional
contributor license agreements. See the NOTICE file distributed with information regarding copyright ownership. The ASF licenses this file to
this work for additional information regarding copyright ownership. You under the Apache License, Version 2.0 (the "License"); you may not use
The ASF licenses this file to You under the Apache License, Version 2.0 this file except in compliance with the License. You may obtain a copy of
(the "License"); you may not use this file except in compliance with the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required
the License. You may obtain a copy of the License at by applicable law or agreed to in writing, software distributed under the
http://www.apache.org/licenses/LICENSE-2.0 License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
Unless required by applicable law or agreed to in writing, software OF ANY KIND, either express or implied. See the License for the specific
distributed under the License is distributed on an "AS IS" BASIS, language governing permissions and limitations under the License. -->
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
See the License for the specific language governing permissions and xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
limitations under the License. <modelVersion>4.0.0</modelVersion>
--> <parent>
<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"> <groupId>org.apache.nifi</groupId>
<modelVersion>4.0.0</modelVersion> <artifactId>nifi</artifactId>
<parent> <version>0.1.0-incubating-SNAPSHOT</version>
<groupId>org.apache.nifi</groupId> </parent>
<artifactId>nifi</artifactId> <artifactId>nifi-assembly</artifactId>
<version>0.1.0-incubating-SNAPSHOT</version> <packaging>pom</packaging>
</parent> <description>This is the assembly Apache NiFi (incubating)</description>
<artifactId>nifi-assembly</artifactId> <build>
<packaging>pom</packaging> <plugins>
<description>This is the assembly Apache NiFi (incubating)</description> <plugin>
<build> <artifactId>maven-assembly-plugin</artifactId>
<plugins> <configuration>
<plugin> <finalName>nifi-${project.version}</finalName>
<artifactId>maven-assembly-plugin</artifactId> <attach>false</attach>
<configuration> </configuration>
<finalName>nifi-${project.version}</finalName> <executions>
<attach>false</attach> <execution>
</configuration> <id>make shared resource</id>
<executions> <goals>
<execution> <goal>single</goal>
<id>make shared resource</id> </goals>
<goals> <phase>package</phase>
<goal>single</goal> <configuration>
</goals> <descriptors>
<phase>package</phase> <descriptor>src/main/assembly/dependencies.xml</descriptor>
<configuration> </descriptors>
<descriptors> <tarLongFileMode>posix</tarLongFileMode>
<descriptor>src/main/assembly/dependencies.xml</descriptor> </configuration>
</descriptors> </execution>
<tarLongFileMode>posix</tarLongFileMode> </executions>
</configuration> </plugin>
</execution> </plugins>
</executions> </build>
</plugin> <dependencies>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-runtime</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-resources</artifactId>
<classifier>resources</classifier>
<scope>runtime</scope>
<type>zip</type>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-docs</artifactId>
<classifier>resources</classifier>
<scope>runtime</scope>
<type>zip</type>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-framework-nar</artifactId>
<type>nar</type>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-provenance-repository-nar</artifactId>
<type>nar</type>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-standard-services-api-nar</artifactId>
<type>nar</type>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-ssl-context-service-nar</artifactId>
<type>nar</type>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-distributed-cache-services-nar</artifactId>
<type>nar</type>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-standard-nar</artifactId>
<type>nar</type>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-jetty-bundle</artifactId>
<type>nar</type>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-update-attribute-nar</artifactId>
<type>nar</type>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-hadoop-libraries-nar</artifactId>
<type>nar</type>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-hadoop-nar</artifactId>
<type>nar</type>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-kafka-nar</artifactId>
<type>nar</type>
</dependency>
<dependency> <dependency>
<groupId>org.apache.nifi</groupId> <groupId>ch.qos.logback</groupId>
<artifactId>nifi-http-context-map-nar</artifactId> <artifactId>logback-classic</artifactId>
<type>nar</type> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.nifi</groupId> <groupId>org.slf4j</groupId>
<artifactId>nifi-kite-nar</artifactId> <artifactId>jcl-over-slf4j</artifactId>
<type>nar</type> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-runtime</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-resources</artifactId>
<classifier>resources</classifier>
<scope>runtime</scope>
<type>zip</type>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-docs</artifactId>
<classifier>resources</classifier>
<scope>runtime</scope>
<type>zip</type>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-framework-nar</artifactId>
<type>nar</type>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-provenance-repository-nar</artifactId>
<type>nar</type>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-standard-services-api-nar</artifactId>
<type>nar</type>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-ssl-context-service-nar</artifactId>
<type>nar</type>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-distributed-cache-services-nar</artifactId>
<type>nar</type>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-standard-nar</artifactId>
<type>nar</type>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-jetty-bundle</artifactId>
<type>nar</type>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-update-attribute-nar</artifactId>
<type>nar</type>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-hadoop-libraries-nar</artifactId>
<type>nar</type>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-hadoop-nar</artifactId>
<type>nar</type>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-kafka-nar</artifactId>
<type>nar</type>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-http-context-map-nar</artifactId>
<type>nar</type>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-kite-nar</artifactId>
<type>nar</type>
</dependency>
<dependency> <dependency>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>nifi-solr-nar</artifactId> <artifactId>nifi-solr-nar</artifactId>
<type>nar</type> <type>nar</type>
</dependency> </dependency>
</dependencies> <dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-social-media-nar</artifactId>
<version>0.1.0-incubating-SNAPSHOT</version>
<type>nar</type>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-hl7-nar</artifactId>
<version>0.1.0-incubating-SNAPSHOT</version>
<type>nar</type>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-language-translation-nar</artifactId>
<version>0.1.0-incubating-SNAPSHOT</version>
<type>nar</type>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-geo-nar</artifactId>
<version>0.1.0-incubating-SNAPSHOT</version>
<type>nar</type>
</dependency>
</dependencies>
<properties> <properties>
<!--Wrapper Properties--> <!--Wrapper Properties -->
<nifi.wrapper.jvm.heap.initial.mb>256</nifi.wrapper.jvm.heap.initial.mb> <nifi.wrapper.jvm.heap.initial.mb>256</nifi.wrapper.jvm.heap.initial.mb>
<nifi.wrapper.jvm.heap.max.mb>512</nifi.wrapper.jvm.heap.max.mb> <nifi.wrapper.jvm.heap.max.mb>512</nifi.wrapper.jvm.heap.max.mb>
<nifi.initial.permgen.size.mb>128</nifi.initial.permgen.size.mb> <nifi.initial.permgen.size.mb>128</nifi.initial.permgen.size.mb>
<nifi.max.permgen.size.mb>128</nifi.max.permgen.size.mb> <nifi.max.permgen.size.mb>128</nifi.max.permgen.size.mb>
<nifi.wrapper.logfile.maxsize>10m</nifi.wrapper.logfile.maxsize> <nifi.wrapper.logfile.maxsize>10m</nifi.wrapper.logfile.maxsize>
<nifi.wrapper.logfile.maxfiles>10</nifi.wrapper.logfile.maxfiles> <nifi.wrapper.logfile.maxfiles>10</nifi.wrapper.logfile.maxfiles>
<!-- nifi.properties: core properties --> <!-- nifi.properties: core properties -->
<nifi.version>${project.version}</nifi.version> <nifi.version>${project.version}</nifi.version>
<nifi.flowcontroller.autoResumeState>true</nifi.flowcontroller.autoResumeState> <nifi.flowcontroller.autoResumeState>true</nifi.flowcontroller.autoResumeState>
<nifi.flowcontroller.graceful.shutdown.period>10 sec</nifi.flowcontroller.graceful.shutdown.period> <nifi.flowcontroller.graceful.shutdown.period>10 sec</nifi.flowcontroller.graceful.shutdown.period>
<nifi.flowservice.writedelay.interval>500 ms</nifi.flowservice.writedelay.interval> <nifi.flowservice.writedelay.interval>500 ms</nifi.flowservice.writedelay.interval>
<nifi.administrative.yield.duration>30 sec</nifi.administrative.yield.duration> <nifi.administrative.yield.duration>30 sec</nifi.administrative.yield.duration>
<nifi.bored.yield.duration>10 millis</nifi.bored.yield.duration> <nifi.bored.yield.duration>10 millis</nifi.bored.yield.duration>
<nifi.flow.configuration.file>./conf/flow.xml.gz</nifi.flow.configuration.file> <nifi.flow.configuration.file>./conf/flow.xml.gz</nifi.flow.configuration.file>
<nifi.flow.configuration.archive.dir>./conf/archive/</nifi.flow.configuration.archive.dir> <nifi.flow.configuration.archive.dir>./conf/archive/</nifi.flow.configuration.archive.dir>
<nifi.authority.provider.configuration.file>./conf/authority-providers.xml</nifi.authority.provider.configuration.file> <nifi.authority.provider.configuration.file>./conf/authority-providers.xml</nifi.authority.provider.configuration.file>
<nifi.templates.directory>./conf/templates</nifi.templates.directory> <nifi.templates.directory>./conf/templates</nifi.templates.directory>
<nifi.database.directory>./database_repository</nifi.database.directory> <nifi.database.directory>./database_repository</nifi.database.directory>
<nifi.flowfile.repository.implementation>org.apache.nifi.controller.repository.WriteAheadFlowFileRepository</nifi.flowfile.repository.implementation> <nifi.flowfile.repository.implementation>org.apache.nifi.controller.repository.WriteAheadFlowFileRepository</nifi.flowfile.repository.implementation>
<nifi.flowfile.repository.directory>./flowfile_repository</nifi.flowfile.repository.directory> <nifi.flowfile.repository.directory>./flowfile_repository</nifi.flowfile.repository.directory>
<nifi.flowfile.repository.partitions>256</nifi.flowfile.repository.partitions> <nifi.flowfile.repository.partitions>256</nifi.flowfile.repository.partitions>
<nifi.flowfile.repository.checkpoint.interval>2 mins</nifi.flowfile.repository.checkpoint.interval> <nifi.flowfile.repository.checkpoint.interval>2 mins</nifi.flowfile.repository.checkpoint.interval>
<nifi.flowfile.repository.always.sync>false</nifi.flowfile.repository.always.sync> <nifi.flowfile.repository.always.sync>false</nifi.flowfile.repository.always.sync>
<nifi.swap.manager.implementation>org.apache.nifi.controller.FileSystemSwapManager</nifi.swap.manager.implementation> <nifi.swap.manager.implementation>org.apache.nifi.controller.FileSystemSwapManager</nifi.swap.manager.implementation>
<nifi.queue.swap.threshold>20000</nifi.queue.swap.threshold> <nifi.queue.swap.threshold>20000</nifi.queue.swap.threshold>
<nifi.swap.in.period>5 sec</nifi.swap.in.period> <nifi.swap.in.period>5 sec</nifi.swap.in.period>
<nifi.swap.in.threads>1</nifi.swap.in.threads> <nifi.swap.in.threads>1</nifi.swap.in.threads>
<nifi.swap.out.period>5 sec</nifi.swap.out.period> <nifi.swap.out.period>5 sec</nifi.swap.out.period>
<nifi.swap.out.threads>4</nifi.swap.out.threads> <nifi.swap.out.threads>4</nifi.swap.out.threads>
<nifi.content.repository.implementation>org.apache.nifi.controller.repository.FileSystemRepository</nifi.content.repository.implementation> <nifi.content.repository.implementation>org.apache.nifi.controller.repository.FileSystemRepository</nifi.content.repository.implementation>
<nifi.content.claim.max.appendable.size>10 MB</nifi.content.claim.max.appendable.size> <nifi.content.claim.max.appendable.size>10 MB</nifi.content.claim.max.appendable.size>
<nifi.content.claim.max.flow.files>100</nifi.content.claim.max.flow.files> <nifi.content.claim.max.flow.files>100</nifi.content.claim.max.flow.files>
<nifi.content.repository.directory.default>./content_repository</nifi.content.repository.directory.default> <nifi.content.repository.directory.default>./content_repository</nifi.content.repository.directory.default>
<nifi.content.repository.archive.max.retention.period /> <nifi.content.repository.archive.max.retention.period />
<nifi.content.repository.archive.max.usage.percentage /> <nifi.content.repository.archive.max.usage.percentage />
<nifi.content.repository.archive.enabled>false</nifi.content.repository.archive.enabled> <nifi.content.repository.archive.enabled>false</nifi.content.repository.archive.enabled>
<nifi.content.repository.always.sync>false</nifi.content.repository.always.sync> <nifi.content.repository.always.sync>false</nifi.content.repository.always.sync>
<nifi.content.viewer.url /> <nifi.content.viewer.url />
<nifi.restore.directory /> <nifi.restore.directory />
<nifi.ui.banner.text /> <nifi.ui.banner.text />
<nifi.ui.autorefresh.interval>30 sec</nifi.ui.autorefresh.interval> <nifi.ui.autorefresh.interval>30 sec</nifi.ui.autorefresh.interval>
<nifi.nar.library.directory>./lib</nifi.nar.library.directory> <nifi.nar.library.directory>./lib</nifi.nar.library.directory>
<nifi.nar.working.directory>./work/nar/</nifi.nar.working.directory> <nifi.nar.working.directory>./work/nar/</nifi.nar.working.directory>
<nifi.documentation.working.directory>./work/docs/components</nifi.documentation.working.directory> <nifi.documentation.working.directory>./work/docs/components</nifi.documentation.working.directory>
<nifi.sensitive.props.algorithm>PBEWITHMD5AND256BITAES-CBC-OPENSSL</nifi.sensitive.props.algorithm> <nifi.sensitive.props.algorithm>PBEWITHMD5AND256BITAES-CBC-OPENSSL</nifi.sensitive.props.algorithm>
<nifi.sensitive.props.provider>BC</nifi.sensitive.props.provider> <nifi.sensitive.props.provider>BC</nifi.sensitive.props.provider>
<nifi.h2.url.append>;LOCK_TIMEOUT=25000;WRITE_DELAY=0;AUTO_SERVER=FALSE</nifi.h2.url.append> <nifi.h2.url.append>;LOCK_TIMEOUT=25000;WRITE_DELAY=0;AUTO_SERVER=FALSE</nifi.h2.url.append>
<nifi.remote.input.socket.port>9990</nifi.remote.input.socket.port> <nifi.remote.input.socket.port>9990</nifi.remote.input.socket.port>
<!-- persistent provenance repository properties --> <!-- persistent provenance repository properties -->
<nifi.provenance.repository.implementation>org.apache.nifi.provenance.PersistentProvenanceRepository</nifi.provenance.repository.implementation> <nifi.provenance.repository.implementation>org.apache.nifi.provenance.PersistentProvenanceRepository</nifi.provenance.repository.implementation>
<nifi.provenance.repository.directory.default>./provenance_repository</nifi.provenance.repository.directory.default> <nifi.provenance.repository.directory.default>./provenance_repository</nifi.provenance.repository.directory.default>
<nifi.provenance.repository.max.storage.time>24 hours</nifi.provenance.repository.max.storage.time> <nifi.provenance.repository.max.storage.time>24 hours</nifi.provenance.repository.max.storage.time>
<nifi.provenance.repository.max.storage.size>1 GB</nifi.provenance.repository.max.storage.size> <nifi.provenance.repository.max.storage.size>1 GB</nifi.provenance.repository.max.storage.size>
<nifi.provenance.repository.rollover.time>5 mins</nifi.provenance.repository.rollover.time> <nifi.provenance.repository.rollover.time>5 mins</nifi.provenance.repository.rollover.time>
<nifi.provenance.repository.rollover.size>100 MB</nifi.provenance.repository.rollover.size> <nifi.provenance.repository.rollover.size>100 MB</nifi.provenance.repository.rollover.size>
<nifi.provenance.repository.query.threads>2</nifi.provenance.repository.query.threads> <nifi.provenance.repository.query.threads>2</nifi.provenance.repository.query.threads>
<nifi.provenance.repository.compress.on.rollover>true</nifi.provenance.repository.compress.on.rollover> <nifi.provenance.repository.compress.on.rollover>true</nifi.provenance.repository.compress.on.rollover>
<nifi.provenance.repository.indexed.fields>EventType, FlowFileUUID, Filename, ProcessorID</nifi.provenance.repository.indexed.fields> <nifi.provenance.repository.indexed.fields>EventType, FlowFileUUID,
<nifi.provenance.repository.indexed.attributes /> Filename, ProcessorID</nifi.provenance.repository.indexed.fields>
<nifi.provenance.repository.index.shard.size>500 MB</nifi.provenance.repository.index.shard.size> <nifi.provenance.repository.indexed.attributes />
<nifi.provenance.repository.always.sync>false</nifi.provenance.repository.always.sync> <nifi.provenance.repository.index.shard.size>500 MB</nifi.provenance.repository.index.shard.size>
<nifi.provenance.repository.journal.count>16</nifi.provenance.repository.journal.count> <nifi.provenance.repository.always.sync>false</nifi.provenance.repository.always.sync>
<nifi.provenance.repository.journal.count>16</nifi.provenance.repository.journal.count>
<!-- volatile provenance repository properties --> <!-- volatile provenance repository properties -->
<nifi.provenance.repository.buffer.size>100000</nifi.provenance.repository.buffer.size> <nifi.provenance.repository.buffer.size>100000</nifi.provenance.repository.buffer.size>
<!-- Component status repository properties --> <!-- Component status repository properties -->
<nifi.components.status.repository.implementation>org.apache.nifi.controller.status.history.VolatileComponentStatusRepository</nifi.components.status.repository.implementation> <nifi.components.status.repository.implementation>org.apache.nifi.controller.status.history.VolatileComponentStatusRepository</nifi.components.status.repository.implementation>
<nifi.components.status.repository.buffer.size>288</nifi.components.status.repository.buffer.size> <nifi.components.status.repository.buffer.size>288</nifi.components.status.repository.buffer.size>
<nifi.components.status.snapshot.frequency>5 mins</nifi.components.status.snapshot.frequency> <nifi.components.status.snapshot.frequency>5 mins</nifi.components.status.snapshot.frequency>
<!-- nifi.properties: web properties --> <!-- nifi.properties: web properties -->
<nifi.web.war.directory>./lib</nifi.web.war.directory> <nifi.web.war.directory>./lib</nifi.web.war.directory>
<nifi.web.http.host /> <nifi.web.http.host />
<nifi.web.http.port>8080</nifi.web.http.port> <nifi.web.http.port>8080</nifi.web.http.port>
<nifi.web.https.host /> <nifi.web.https.host />
<nifi.web.https.port /> <nifi.web.https.port />
<nifi.jetty.work.dir>./work/jetty</nifi.jetty.work.dir> <nifi.jetty.work.dir>./work/jetty</nifi.jetty.work.dir>
<nifi.web.jetty.threads>200</nifi.web.jetty.threads> <nifi.web.jetty.threads>200</nifi.web.jetty.threads>
<!-- nifi.properties: security properties --> <!-- nifi.properties: security properties -->
<nifi.security.keystore /> <nifi.security.keystore />
<nifi.security.keystoreType /> <nifi.security.keystoreType />
<nifi.security.keystorePasswd /> <nifi.security.keystorePasswd />
<nifi.security.keyPasswd /> <nifi.security.keyPasswd />
<nifi.security.truststore /> <nifi.security.truststore />
<nifi.security.truststoreType /> <nifi.security.truststoreType />
<nifi.security.truststorePasswd /> <nifi.security.truststorePasswd />
<nifi.security.needClientAuth /> <nifi.security.needClientAuth />
<nifi.security.authorizedUsers.file>./conf/authorized-users.xml</nifi.security.authorizedUsers.file> <nifi.security.authorizedUsers.file>./conf/authorized-users.xml</nifi.security.authorizedUsers.file>
<nifi.security.user.credential.cache.duration>24 hours</nifi.security.user.credential.cache.duration> <nifi.security.user.credential.cache.duration>24 hours</nifi.security.user.credential.cache.duration>
<nifi.security.user.authority.provider>file-provider</nifi.security.user.authority.provider> <nifi.security.user.authority.provider>file-provider</nifi.security.user.authority.provider>
<nifi.security.x509.principal.extractor /> <nifi.security.x509.principal.extractor />
<nifi.security.support.new.account.requests /> <nifi.security.support.new.account.requests />
<nifi.security.ocsp.responder.url /> <nifi.security.ocsp.responder.url />
<nifi.security.ocsp.responder.certificate /> <nifi.security.ocsp.responder.certificate />
<!-- nifi.properties: cluster common properties (cluster manager and nodes must have same values) --> <!-- nifi.properties: cluster common properties (cluster manager and nodes
<nifi.cluster.protocol.heartbeat.interval>5 sec</nifi.cluster.protocol.heartbeat.interval> must have same values) -->
<nifi.cluster.protocol.is.secure>false</nifi.cluster.protocol.is.secure> <nifi.cluster.protocol.heartbeat.interval>5 sec</nifi.cluster.protocol.heartbeat.interval>
<nifi.cluster.protocol.socket.timeout>30 sec</nifi.cluster.protocol.socket.timeout> <nifi.cluster.protocol.is.secure>false</nifi.cluster.protocol.is.secure>
<nifi.cluster.protocol.connection.handshake.timeout>45 sec</nifi.cluster.protocol.connection.handshake.timeout> <nifi.cluster.protocol.socket.timeout>30 sec</nifi.cluster.protocol.socket.timeout>
<nifi.cluster.protocol.use.multicast>false</nifi.cluster.protocol.use.multicast> <nifi.cluster.protocol.connection.handshake.timeout>45 sec</nifi.cluster.protocol.connection.handshake.timeout>
<nifi.cluster.protocol.multicast.address /> <nifi.cluster.protocol.use.multicast>false</nifi.cluster.protocol.use.multicast>
<nifi.cluster.protocol.multicast.port /> <nifi.cluster.protocol.multicast.address />
<nifi.cluster.protocol.multicast.service.broadcast.delay>500 ms</nifi.cluster.protocol.multicast.service.broadcast.delay> <nifi.cluster.protocol.multicast.port />
<nifi.cluster.protocol.multicast.service.locator.attempts>3</nifi.cluster.protocol.multicast.service.locator.attempts> <nifi.cluster.protocol.multicast.service.broadcast.delay>500 ms</nifi.cluster.protocol.multicast.service.broadcast.delay>
<nifi.cluster.protocol.multicast.service.locator.attempts.delay>1 sec</nifi.cluster.protocol.multicast.service.locator.attempts.delay> <nifi.cluster.protocol.multicast.service.locator.attempts>3</nifi.cluster.protocol.multicast.service.locator.attempts>
<nifi.cluster.protocol.multicast.service.locator.attempts.delay>1 sec</nifi.cluster.protocol.multicast.service.locator.attempts.delay>
<!-- nifi.properties: cluster node properties (only configure for cluster nodes) --> <!-- nifi.properties: cluster node properties (only configure for cluster
<nifi.cluster.is.node>false</nifi.cluster.is.node> nodes) -->
<nifi.cluster.node.address /> <nifi.cluster.is.node>false</nifi.cluster.is.node>
<nifi.cluster.node.protocol.port /> <nifi.cluster.node.address />
<nifi.cluster.node.protocol.threads>2</nifi.cluster.node.protocol.threads> <nifi.cluster.node.protocol.port />
<nifi.cluster.node.unicast.manager.address /> <nifi.cluster.node.protocol.threads>2</nifi.cluster.node.protocol.threads>
<nifi.cluster.node.unicast.manager.protocol.port /> <nifi.cluster.node.unicast.manager.address />
<nifi.cluster.node.unicast.manager.protocol.port />
<!-- nifi.properties: cluster manager properties (only configure for cluster manager) --> <!-- nifi.properties: cluster manager properties (only configure for cluster
<nifi.cluster.is.manager>false</nifi.cluster.is.manager> manager) -->
<nifi.cluster.manager.address /> <nifi.cluster.is.manager>false</nifi.cluster.is.manager>
<nifi.cluster.manager.protocol.port /> <nifi.cluster.manager.address />
<nifi.cluster.manager.node.firewall.file /> <nifi.cluster.manager.protocol.port />
<nifi.cluster.manager.node.event.history.size>10</nifi.cluster.manager.node.event.history.size> <nifi.cluster.manager.node.firewall.file />
<nifi.cluster.manager.node.api.connection.timeout>30 sec</nifi.cluster.manager.node.api.connection.timeout> <nifi.cluster.manager.node.event.history.size>10</nifi.cluster.manager.node.event.history.size>
<nifi.cluster.manager.node.api.read.timeout>30 sec</nifi.cluster.manager.node.api.read.timeout> <nifi.cluster.manager.node.api.connection.timeout>30 sec</nifi.cluster.manager.node.api.connection.timeout>
<nifi.cluster.manager.node.api.request.threads>10</nifi.cluster.manager.node.api.request.threads> <nifi.cluster.manager.node.api.read.timeout>30 sec</nifi.cluster.manager.node.api.read.timeout>
<nifi.cluster.manager.flow.retrieval.delay>5 sec</nifi.cluster.manager.flow.retrieval.delay> <nifi.cluster.manager.node.api.request.threads>10</nifi.cluster.manager.node.api.request.threads>
<nifi.cluster.manager.protocol.threads>10</nifi.cluster.manager.protocol.threads> <nifi.cluster.manager.flow.retrieval.delay>5 sec</nifi.cluster.manager.flow.retrieval.delay>
<nifi.cluster.manager.safemode.duration>0 sec</nifi.cluster.manager.safemode.duration> <nifi.cluster.manager.protocol.threads>10</nifi.cluster.manager.protocol.threads>
</properties> <nifi.cluster.manager.safemode.duration>0 sec</nifi.cluster.manager.safemode.duration>
<profiles> </properties>
<profile> <profiles>
<id>rpm</id> <profile>
<activation> <id>rpm</id>
<activeByDefault>false</activeByDefault> <activation>
</activation> <activeByDefault>false</activeByDefault>
<build> </activation>
<plugins> <build>
<plugin> <plugins>
<artifactId>maven-dependency-plugin</artifactId> <plugin>
<executions> <artifactId>maven-dependency-plugin</artifactId>
<execution> <executions>
<id>unpack-shared-resources</id> <execution>
<goals> <id>unpack-shared-resources</id>
<goal>unpack-dependencies</goal> <goals>
</goals> <goal>unpack-dependencies</goal>
<phase>generate-resources</phase> </goals>
<configuration> <phase>generate-resources</phase>
<outputDirectory>${project.build.directory}/generated-resources</outputDirectory> <configuration>
<includeArtifactIds>nifi-resources</includeArtifactIds> <outputDirectory>${project.build.directory}/generated-resources</outputDirectory>
<includeGroupIds>org.apache.nifi</includeGroupIds> <includeArtifactIds>nifi-resources</includeArtifactIds>
<excludeTransitive>false</excludeTransitive> <includeGroupIds>org.apache.nifi</includeGroupIds>
</configuration> <excludeTransitive>false</excludeTransitive>
</execution> </configuration>
<execution> </execution>
<id>unpack-docs</id> <execution>
<goals> <id>unpack-docs</id>
<goal>unpack-dependencies</goal> <goals>
</goals> <goal>unpack-dependencies</goal>
<phase>generate-resources</phase> </goals>
<configuration> <phase>generate-resources</phase>
<outputDirectory>${project.build.directory}/generated-docs</outputDirectory> <configuration>
<includeArtifactIds>nifi-docs</includeArtifactIds> <outputDirectory>${project.build.directory}/generated-docs</outputDirectory>
<includeGroupIds>org.apache.nifi</includeGroupIds> <includeArtifactIds>nifi-docs</includeArtifactIds>
<excludeTransitive>false</excludeTransitive> <includeGroupIds>org.apache.nifi</includeGroupIds>
</configuration> <excludeTransitive>false</excludeTransitive>
</execution> </configuration>
</executions> </execution>
</plugin> </executions>
<plugin> </plugin>
<groupId>org.codehaus.mojo</groupId> <plugin>
<artifactId>rpm-maven-plugin</artifactId> <groupId>org.codehaus.mojo</groupId>
<configuration> <artifactId>rpm-maven-plugin</artifactId>
<summary>Apache NiFi (incubating)</summary> <configuration>
<description>Apache Nifi (incubating) is dataflow system based on the Flow-Based Programming concepts.</description> <summary>Apache NiFi (incubating)</summary>
<license>Apache License, Version 2.0 and others (see included LICENSE file)</license> <description>Apache Nifi (incubating) is dataflow system based on
<url>http://nifi.incubator.apache.org</url> the Flow-Based Programming concepts.</description>
<group>Utilities</group> <license>Apache License, Version 2.0 and others (see included
<prefix>/opt/nifi</prefix> LICENSE file)</license>
<defineStatements> <url>http://nifi.incubator.apache.org</url>
<defineStatement>_use_internal_dependency_generator 0</defineStatement> <group>Utilities</group>
</defineStatements> <prefix>/opt/nifi</prefix>
<defaultDirmode>750</defaultDirmode> <defineStatements>
<defaultFilemode>640</defaultFilemode> <defineStatement>_use_internal_dependency_generator 0</defineStatement>
<defaultUsername>root</defaultUsername> </defineStatements>
<defaultGroupname>root</defaultGroupname> <defaultDirmode>750</defaultDirmode>
</configuration> <defaultFilemode>640</defaultFilemode>
<executions> <defaultUsername>root</defaultUsername>
<execution> <defaultGroupname>root</defaultGroupname>
<id>build-bin-rpm</id> </configuration>
<goals> <executions>
<goal>attached-rpm</goal> <execution>
</goals> <id>build-bin-rpm</id>
<configuration> <goals>
<classifier>bin</classifier> <goal>attached-rpm</goal>
<provides> </goals>
<provide>nifi</provide> <configuration>
</provides> <classifier>bin</classifier>
<mappings> <provides>
<mapping> <provide>nifi</provide>
<directory>/opt/nifi/nifi-${project.version}</directory> </provides>
</mapping> <mappings>
<mapping> <mapping>
<directory>/opt/nifi/nifi-${project.version}</directory> <directory>/opt/nifi/nifi-${project.version}</directory>
<sources> </mapping>
<source> <mapping>
<location>./LICENSE</location> <directory>/opt/nifi/nifi-${project.version}</directory>
</source> <sources>
<source> <source>
<location>./NOTICE</location> <location>./LICENSE</location>
</source> </source>
<source> <source>
<location>../DISCLAIMER</location> <location>./NOTICE</location>
</source> </source>
<source> <source>
<location>./README.md</location> <location>../DISCLAIMER</location>
<destination>README</destination> </source>
</source> <source>
</sources> <location>./README.md</location>
</mapping> <destination>README</destination>
<mapping> </source>
<directory>/opt/nifi/nifi-${project.version}/bin</directory> </sources>
<filemode>750</filemode> </mapping>
<sources> <mapping>
<source> <directory>/opt/nifi/nifi-${project.version}/bin</directory>
<location>${project.build.directory}/generated-resources/bin/nifi.sh</location> <filemode>750</filemode>
<destination>nifi.sh</destination> <sources>
<filter>true</filter> <source>
</source> <location>${project.build.directory}/generated-resources/bin/nifi.sh</location>
</sources> <destination>nifi.sh</destination>
</mapping> <filter>true</filter>
<mapping> </source>
<directory>/opt/nifi/nifi-${project.version}/conf</directory> </sources>
<configuration>true</configuration> </mapping>
<sources> <mapping>
<source> <directory>/opt/nifi/nifi-${project.version}/conf</directory>
<location>${project.build.directory}/generated-resources/conf</location> <configuration>true</configuration>
<filter>true</filter> <sources>
</source> <source>
</sources> <location>${project.build.directory}/generated-resources/conf</location>
</mapping> <filter>true</filter>
<mapping> </source>
<directory>/opt/nifi/nifi-${project.version}/lib</directory> </sources>
<dependency> </mapping>
<excludes> <mapping>
<exclude>org.apache.nifi:nifi-bootstrap</exclude> <directory>/opt/nifi/nifi-${project.version}/lib</directory>
<exclude>org.apache.nifi:nifi-resources</exclude> <dependency>
<exclude>org.apache.nifi:nifi-docs</exclude> <excludes>
</excludes> <exclude>org.apache.nifi:nifi-bootstrap</exclude>
</dependency> <exclude>org.apache.nifi:nifi-resources</exclude>
</mapping> <exclude>org.apache.nifi:nifi-docs</exclude>
<mapping> </excludes>
<directory>/opt/nifi/nifi-${project.version}/lib/bootstrap</directory> </dependency>
<dependency> </mapping>
<includes> <mapping>
<include>org.apache.nifi:nifi-bootstrap</include> <directory>/opt/nifi/nifi-${project.version}/lib/bootstrap</directory>
</includes> <dependency>
</dependency> <includes>
</mapping> <include>org.apache.nifi:nifi-bootstrap</include>
<mapping> </includes>
<directory>/opt/nifi/nifi-${project.version}/docs</directory> </dependency>
<sources> </mapping>
<source> <mapping>
<location>${project.build.directory}/generated-docs</location> <directory>/opt/nifi/nifi-${project.version}/docs</directory>
</source> <sources>
</sources> <source>
</mapping> <location>${project.build.directory}/generated-docs</location>
</mappings> </source>
</configuration> </sources>
</execution> </mapping>
</executions> </mappings>
</plugin> </configuration>
</plugins> </execution>
</build> </executions>
</profile> </plugin>
</profiles> </plugins>
</build>
</profile>
</profiles>
</project> </project>

View File

@ -0,0 +1,3 @@
/target/
/target/
/target/

View File

@ -0,0 +1,125 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-commons</artifactId>
<version>0.1.0-incubating-SNAPSHOT</version>
</parent>
<artifactId>nifi-hl7-query-language</artifactId>
<packaging>jar</packaging>
<name>NiFi Health Level 7 (HL7) Query Language</name>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<plugin>
<groupId>org.antlr</groupId>
<artifactId>antlr3-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>antlr</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.rat</groupId>
<artifactId>apache-rat-plugin</artifactId>
<configuration>
<excludes>
<exclude>src/test/resources/hypoglycemia</exclude>
<exclude>src/test/resources/hyperglycemia</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.antlr</groupId>
<artifactId>antlr-runtime</artifactId>
<version>3.5.2</version>
</dependency>
<!-- HAPI to parse v2 messages -->
<dependency>
<groupId>ca.uhn.hapi</groupId>
<artifactId>hapi-base</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi</groupId>
<artifactId>hapi-structures-v21</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi</groupId>
<artifactId>hapi-structures-v22</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi</groupId>
<artifactId>hapi-structures-v23</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi</groupId>
<artifactId>hapi-structures-v231</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi</groupId>
<artifactId>hapi-structures-v24</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi</groupId>
<artifactId>hapi-structures-v25</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi</groupId>
<artifactId>hapi-structures-v251</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi</groupId>
<artifactId>hapi-structures-v26</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,173 @@
/*
* 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.
*/
lexer grammar HL7QueryLexer;
@header {
package org.apache.nifi.hl7.query.antlr;
import org.apache.nifi.hl7.query.exception.HL7QueryParsingException;
}
@rulecatch {
catch(final Exception e) {
throw new HL7QueryParsingException(e);
}
}
@members {
public void displayRecognitionError(String[] tokenNames, RecognitionException e) {
final StringBuilder sb = new StringBuilder();
if ( e.token == null ) {
sb.append("Unrecognized token ");
} else {
sb.append("Unexpected token '").append(e.token.getText()).append("' ");
}
sb.append("at line ").append(e.line);
if ( e.approximateLineInfo ) {
sb.append(" (approximately)");
}
sb.append(", column ").append(e.charPositionInLine);
sb.append(". Query: ").append(e.input.toString());
throw new HL7QueryParsingException(sb.toString());
}
public void recover(RecognitionException e) {
final StringBuilder sb = new StringBuilder();
if ( e.token == null ) {
sb.append("Unrecognized token ");
} else {
sb.append("Unexpected token '").append(e.token.getText()).append("' ");
}
sb.append("at line ").append(e.line);
if ( e.approximateLineInfo ) {
sb.append(" (approximately)");
}
sb.append(", column ").append(e.charPositionInLine);
sb.append(". Query: ").append(e.input.toString());
throw new HL7QueryParsingException(sb.toString());
}
}
// PUNCTUATION & SPECIAL CHARACTERS
WHITESPACE : (' '|'\t'|'\n'|'\r')+ { $channel = HIDDEN; };
COMMENT : '#' ( ~('\n') )* '\n' { $channel = HIDDEN; };
LPAREN : '(';
RPAREN : ')';
LBRACE : '{';
RBRACE : '}';
COLON : ':';
COMMA : ',';
DOT : '.';
SEMICOLON : ';';
// OPERATORS
EQUALS : '=';
NOT_EQUALS : '!=';
GT : '>';
GE : '>=';
LT : '<';
LE : '<=';
REGEX : 'MATCHES REGEX';
LIKE : 'LIKE';
IS_NULL : 'IS NULL';
NOT_NULL : 'NOT NULL';
// KEYWORDS
AND : 'AND';
OR : 'OR';
NOT : 'NOT';
TRUE : 'true';
FALSE : 'false';
SELECT : 'select' | 'SELECT';
DECLARE : 'declare' | 'DECLARE';
OPTIONAL : 'optional' | 'OPTIONAL';
REQUIRED : 'required' | 'REQUIRED';
AS : 'as' | 'AS';
WHERE : 'where' | 'WHERE';
MESSAGE : 'MESSAGE' | 'message';
SEGMENT : 'SEGMENT' | 'segment';
SEGMENT_NAME : LETTER ALPHA_NUMERIC ALPHA_NUMERIC;
NUMBER : ('0'..'9')+;
fragment LETTER : 'A'..'Z';
fragment ALPHA_NUMERIC : 'A'..'Z' | '0'..'9';
// STRINGS
STRING_LITERAL
@init{StringBuilder lBuf = new StringBuilder();}
:
(
'"'
(
escaped=ESC {lBuf.append(getText());} |
normal = ~( '"' | '\\' | '\n' | '\r' | '\t' ) { lBuf.appendCodePoint(normal);}
)*
'"'
)
{
setText(lBuf.toString());
}
|
(
'\''
(
escaped=ESC {lBuf.append(getText());} |
normal = ~( '\'' | '\\' | '\n' | '\r' | '\t' ) { lBuf.appendCodePoint(normal);}
)*
'\''
)
{
setText(lBuf.toString());
}
;
fragment
ESC
: '\\'
(
'"' { setText("\""); }
| '\'' { setText("\'"); }
| 'r' { setText("\r"); }
| 'n' { setText("\n"); }
| 't' { setText("\t"); }
| '\\' { setText("\\\\"); }
| nextChar = ~('"' | '\'' | 'r' | 'n' | 't' | '\\')
{
StringBuilder lBuf = new StringBuilder(); lBuf.append("\\\\").appendCodePoint(nextChar); setText(lBuf.toString());
}
)
;
IDENTIFIER : (
~('$' | '{' | '}' | '(' | ')' | '[' | ']' | ',' | ':' | ';' | '/' | '*' | '\'' | ' ' | '\t' | '\r' | '\n' | '0'..'9' | '.')
~('$' | '{' | '}' | '(' | ')' | '[' | ']' | ',' | ':' | ';' | '/' | '*' | '\'' | ' ' | '\t' | '\r' | '\n' | '.')*
);

View File

@ -0,0 +1,108 @@
/*
* 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.
*/
parser grammar HL7QueryParser;
options {
output=AST;
tokenVocab=HL7QueryLexer;
}
tokens {
QUERY;
DECLARATION;
}
@header {
package org.apache.nifi.hl7.query.antlr;
import org.apache.nifi.hl7.query.exception.HL7QueryParsingException;
}
@members {
public void displayRecognitionError(String[] tokenNames, RecognitionException e) {
final StringBuilder sb = new StringBuilder();
if ( e.token == null ) {
sb.append("Unrecognized token ");
} else {
sb.append("Unexpected token '").append(e.token.getText()).append("' ");
}
sb.append("at line ").append(e.line);
if ( e.approximateLineInfo ) {
sb.append(" (approximately)");
}
sb.append(", column ").append(e.charPositionInLine);
sb.append(". Query: ").append(e.input.toString());
throw new HL7QueryParsingException(sb.toString());
}
public void recover(final RecognitionException e) {
final StringBuilder sb = new StringBuilder();
if ( e.token == null ) {
sb.append("Unrecognized token ");
} else {
sb.append("Unexpected token '").append(e.token.getText()).append("' ");
}
sb.append("at line ").append(e.line);
if ( e.approximateLineInfo ) {
sb.append(" (approximately)");
}
sb.append(", column ").append(e.charPositionInLine);
sb.append(". Query: ").append(e.input.toString());
throw new HL7QueryParsingException(sb.toString());
}
}
declareClause : DECLARE^ declaration (COMMA! declaration)*;
requiredOrOptional : REQUIRED | OPTIONAL;
declaration : IDENTIFIER AS requiredOrOptional SEGMENT_NAME ->
^(DECLARATION IDENTIFIER requiredOrOptional SEGMENT_NAME);
selectClause : SELECT^ selectableClause;
selectableClause : selectable (COMMA! selectable)*;
selectable : (MESSAGE | ref | field)^ (AS! IDENTIFIER^)?;
whereClause : WHERE^ conditions;
conditions : condition ((AND^ | OR^) condition)*;
condition : NOT^ condition | LPAREN! conditions RPAREN! | evaluation;
evaluation : expression
(
unaryOperator^
| (binaryOperator^ expression)
);
expression : (LPAREN! expr RPAREN!) | expr;
expr : ref | field | STRING_LITERAL | NUMBER;
unaryOperator : IS_NULL | NOT_NULL;
binaryOperator : EQUALS | NOT_EQUALS | LT | GT | LE | GE;
ref : (SEGMENT_NAME | IDENTIFIER);
field : ref DOT^ NUMBER
(DOT^ NUMBER (DOT^ NUMBER (DOT^ NUMBER)?)?)?;
query : declareClause? selectClause whereClause? EOF ->
^(QUERY declareClause? selectClause whereClause?);

View File

@ -0,0 +1,37 @@
/*
* 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.hl7.hapi;
import java.util.Collections;
import java.util.List;
import org.apache.nifi.hl7.model.HL7Component;
import org.apache.nifi.hl7.model.HL7Field;
public class EmptyField implements HL7Field {
@Override
public String getValue() {
return null;
}
@Override
public List<HL7Component> getComponents() {
return Collections.emptyList();
}
}

View File

@ -0,0 +1,83 @@
/*
* 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.hl7.hapi;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.nifi.hl7.model.HL7Component;
import org.apache.nifi.hl7.model.HL7Field;
import ca.uhn.hl7v2.model.Composite;
import ca.uhn.hl7v2.model.ExtraComponents;
import ca.uhn.hl7v2.model.Primitive;
import ca.uhn.hl7v2.model.Type;
import ca.uhn.hl7v2.model.Varies;
import ca.uhn.hl7v2.parser.EncodingCharacters;
import ca.uhn.hl7v2.parser.PipeParser;
public class HapiField implements HL7Field, HL7Component {
private final String value;
private final List<HL7Component> components;
public HapiField(final Type type) {
this.value = PipeParser.encode(type, EncodingCharacters.defaultInstance());
final List<HL7Component> componentList = new ArrayList<>();
if ( type instanceof Composite ) {
final Composite composite = (Composite) type;
for ( final Type component : composite.getComponents() ) {
componentList.add(new HapiField(component));
}
}
final ExtraComponents extra = type.getExtraComponents();
if ( extra != null && extra.numComponents() > 0 ) {
final String singleFieldValue;
if ( type instanceof Primitive ) {
singleFieldValue = ((Primitive) type).getValue();
} else {
singleFieldValue = this.value;
}
componentList.add(new SingleValueField(singleFieldValue));
for (int i=0; i < extra.numComponents(); i++) {
final Varies varies = extra.getComponent(i);
componentList.add(new HapiField(varies));
}
}
this.components = Collections.unmodifiableList(componentList);
}
@Override
public String getValue() {
return value;
}
@Override
public List<HL7Component> getComponents() {
return components;
}
@Override
public String toString() {
return value;
}
}

View File

@ -0,0 +1,94 @@
/*
* 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.hl7.hapi;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.nifi.hl7.model.HL7Message;
import org.apache.nifi.hl7.model.HL7Segment;
import ca.uhn.hl7v2.HL7Exception;
import ca.uhn.hl7v2.model.Group;
import ca.uhn.hl7v2.model.Message;
import ca.uhn.hl7v2.model.Segment;
import ca.uhn.hl7v2.model.Structure;
public class HapiMessage implements HL7Message {
private final Message message;
private final List<HL7Segment> allSegments;
private final Map<String, List<HL7Segment>> segmentMap;
public HapiMessage(final Message message) throws HL7Exception {
this.message = message;
allSegments = new ArrayList<>();
populateSegments(message, allSegments);
segmentMap = new HashMap<>();
for ( final HL7Segment segment : allSegments ) {
final String segmentName = segment.getName();
List<HL7Segment> segmentList = segmentMap.get(segmentName);
if ( segmentList == null ) {
segmentList = new ArrayList<>();
segmentMap.put(segmentName, segmentList);
}
segmentList.add(segment);
}
}
private void populateSegments(final Group group, final List<HL7Segment> segments) throws HL7Exception {
for ( final String structureName : group.getNames() ) {
final Structure[] structures = group.getAll(structureName);
if ( group.isGroup(structureName) ) {
for ( final Structure structure : structures ) {
populateSegments((Group) structure, segments);
}
} else {
for ( final Structure structure : structures ) {
final Segment segment = (Segment) structure;
final HapiSegment hapiSegment = new HapiSegment(segment);
segments.add(hapiSegment);
}
}
}
}
@Override
public List<HL7Segment> getSegments() {
return Collections.unmodifiableList(allSegments);
}
@Override
public List<HL7Segment> getSegments(final String segmentType) {
final List<HL7Segment> segments = segmentMap.get(segmentType);
if ( segments == null ) {
return Collections.emptyList();
}
return Collections.unmodifiableList(segments);
}
@Override
public String toString() {
return message.toString();
}
}

View File

@ -0,0 +1,69 @@
/*
* 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.hl7.hapi;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.nifi.hl7.model.HL7Field;
import org.apache.nifi.hl7.model.HL7Segment;
import ca.uhn.hl7v2.HL7Exception;
import ca.uhn.hl7v2.model.Segment;
import ca.uhn.hl7v2.model.Type;
public class HapiSegment implements HL7Segment {
private final Segment segment;
private final List<HL7Field> fields;
public HapiSegment(final Segment segment) throws HL7Exception {
this.segment = segment;
final List<HL7Field> fieldList = new ArrayList<>();
for (int i=1; i <= segment.numFields(); i++) {
final Type[] types = segment.getField(i);
if ( types == null || types.length == 0 ) {
fieldList.add(new EmptyField());
continue;
}
for ( final Type type : types ) {
fieldList.add(new HapiField(type));
}
}
this.fields = Collections.unmodifiableList(fieldList);
}
@Override
public String getName() {
return segment.getName();
}
@Override
public List<HL7Field> getFields() {
return fields;
}
@Override
public String toString() {
return segment.toString();
}
}

View File

@ -0,0 +1,42 @@
/*
* 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.hl7.hapi;
import java.util.Collections;
import java.util.List;
import org.apache.nifi.hl7.model.HL7Component;
import org.apache.nifi.hl7.model.HL7Field;
public class SingleValueField implements HL7Field {
private final String value;
public SingleValueField(final String value) {
this.value = value;
}
@Override
public String getValue() {
return value;
}
@Override
public List<HL7Component> getComponents() {
return Collections.emptyList();
}
}

View File

@ -0,0 +1,27 @@
/*
* 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.hl7.io;
import java.io.IOException;
import org.apache.nifi.hl7.model.HL7Message;
public interface HL7Reader {
HL7Message nextMessage() throws IOException;
}

View File

@ -0,0 +1,40 @@
/*
* 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.hl7.io.exception;
import java.io.IOException;
public class InvalidHL7Exception extends IOException {
private static final long serialVersionUID = -5675416667224562441L;
public InvalidHL7Exception() {
super();
}
public InvalidHL7Exception(String message, Throwable cause) {
super(message, cause);
}
public InvalidHL7Exception(String message) {
super(message);
}
public InvalidHL7Exception(Throwable cause) {
super(cause);
}
}

View File

@ -0,0 +1,24 @@
/*
* 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.hl7.model;
import java.util.List;
public interface HL7Component {
String getValue();
List<HL7Component> getComponents();
}

View File

@ -0,0 +1,21 @@
/*
* 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.hl7.model;
public interface HL7Field extends HL7Component {
}

View File

@ -0,0 +1,27 @@
/*
* 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.hl7.model;
import java.util.List;
public interface HL7Message {
List<HL7Segment> getSegments();
List<HL7Segment> getSegments(String segmentType);
}

View File

@ -0,0 +1,27 @@
/*
* 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.hl7.model;
import java.util.List;
public interface HL7Segment {
String getName();
List<HL7Field> getFields();
}

View File

@ -0,0 +1,29 @@
/*
* 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.hl7.query;
import org.apache.nifi.hl7.model.HL7Message;
public interface Declaration {
String getAlias();
boolean isRequired();
Object getDeclaredValue(HL7Message message);
}

View File

@ -0,0 +1,412 @@
/*
* 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.hl7.query;
import static org.apache.nifi.hl7.query.antlr.HL7QueryParser.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.antlr.runtime.ANTLRStringStream;
import org.antlr.runtime.CharStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.tree.Tree;
import org.apache.nifi.hl7.model.HL7Message;
import org.apache.nifi.hl7.query.evaluator.BooleanEvaluator;
import org.apache.nifi.hl7.query.evaluator.Evaluator;
import org.apache.nifi.hl7.query.evaluator.IntegerEvaluator;
import org.apache.nifi.hl7.query.evaluator.comparison.EqualsEvaluator;
import org.apache.nifi.hl7.query.evaluator.comparison.GreaterThanEvaluator;
import org.apache.nifi.hl7.query.evaluator.comparison.GreaterThanOrEqualEvaluator;
import org.apache.nifi.hl7.query.evaluator.comparison.IsNullEvaluator;
import org.apache.nifi.hl7.query.evaluator.comparison.LessThanEvaluator;
import org.apache.nifi.hl7.query.evaluator.comparison.LessThanOrEqualEvaluator;
import org.apache.nifi.hl7.query.evaluator.comparison.NotEqualsEvaluator;
import org.apache.nifi.hl7.query.evaluator.comparison.NotEvaluator;
import org.apache.nifi.hl7.query.evaluator.comparison.NotNullEvaluator;
import org.apache.nifi.hl7.query.evaluator.literal.IntegerLiteralEvaluator;
import org.apache.nifi.hl7.query.evaluator.literal.StringLiteralEvaluator;
import org.apache.nifi.hl7.query.evaluator.logic.AndEvaluator;
import org.apache.nifi.hl7.query.evaluator.logic.OrEvaluator;
import org.apache.nifi.hl7.query.evaluator.message.DeclaredReferenceEvaluator;
import org.apache.nifi.hl7.query.evaluator.message.DotEvaluator;
import org.apache.nifi.hl7.query.evaluator.message.MessageEvaluator;
import org.apache.nifi.hl7.query.evaluator.message.SegmentEvaluator;
import org.apache.nifi.hl7.query.exception.HL7QueryParsingException;
import org.apache.nifi.hl7.query.result.MissedResult;
import org.apache.nifi.hl7.query.result.StandardQueryResult;
import org.apache.nifi.hl7.query.antlr.HL7QueryLexer;
import org.apache.nifi.hl7.query.antlr.HL7QueryParser;
public class HL7Query {
private final Tree tree;
private final String query;
private final Set<Declaration> declarations = new HashSet<>();
private final List<Selection> selections;
private final BooleanEvaluator whereEvaluator;
private HL7Query(final Tree tree, final String query) {
this.tree = tree;
this.query = query;
List<Selection> select = null;
BooleanEvaluator where = null;
for (int i=0; i < tree.getChildCount(); i++) {
final Tree child = tree.getChild(i);
switch (child.getType()) {
case DECLARE:
processDeclare(child);
break;
case SELECT:
select = processSelect(child);
break;
case WHERE:
where = processWhere(child);
break;
default:
throw new HL7QueryParsingException("Found unexpected clause at root level: " + tree.getText());
}
}
this.whereEvaluator = where;
this.selections = select;
}
private void processDeclare(final Tree declare) {
for (int i=0; i < declare.getChildCount(); i++) {
final Tree declarationTree = declare.getChild(i);
final String identifier = declarationTree.getChild(0).getText();
final Tree requiredOrOptionalTree = declarationTree.getChild(1);
final boolean required = requiredOrOptionalTree.getType() == REQUIRED;
final String segmentName = declarationTree.getChild(2).getText();
final Declaration declaration = new Declaration() {
@Override
public String getAlias() {
return identifier;
}
@Override
public boolean isRequired() {
return required;
}
@Override
public Object getDeclaredValue(final HL7Message message) {
if ( message == null ) {
return null;
}
return message.getSegments(segmentName);
}
};
declarations.add(declaration);
}
}
private List<Selection> processSelect(final Tree select) {
final List<Selection> selections = new ArrayList<>();
for (int i=0; i < select.getChildCount(); i++) {
final Tree selectable = select.getChild(i);
final String alias = getSelectedName(selectable);
final Evaluator<?> selectionEvaluator = buildReferenceEvaluator(selectable);
final Selection selection = new Selection(selectionEvaluator, alias);
selections.add(selection);
}
return selections;
}
private String getSelectedName(final Tree selectable) {
if ( selectable.getChildCount() == 0 ) {
return selectable.getText();
} else if (selectable.getType() == DOT ) {
return getSelectedName(selectable.getChild(0)) + "." + getSelectedName(selectable.getChild(1));
} else {
return selectable.getChild(selectable.getChildCount() - 1).getText();
}
}
private BooleanEvaluator processWhere(final Tree where) {
return buildBooleanEvaluator(where.getChild(0));
}
private Evaluator<?> buildReferenceEvaluator(final Tree tree) {
switch (tree.getType()) {
case MESSAGE:
return new MessageEvaluator();
case SEGMENT_NAME:
return new SegmentEvaluator(new StringLiteralEvaluator(tree.getText()));
case IDENTIFIER:
return new DeclaredReferenceEvaluator(new StringLiteralEvaluator(tree.getText()));
case DOT:
final Tree firstChild = tree.getChild(0);
final Tree secondChild = tree.getChild(1);
return new DotEvaluator(buildReferenceEvaluator(firstChild), buildIntegerEvaluator(secondChild));
case STRING_LITERAL:
return new StringLiteralEvaluator(tree.getText());
case NUMBER:
return new IntegerLiteralEvaluator(Integer.parseInt(tree.getText()));
default:
throw new HL7QueryParsingException("Failed to build evaluator for " + tree.getText());
}
}
private IntegerEvaluator buildIntegerEvaluator(final Tree tree) {
switch (tree.getType()) {
case NUMBER:
return new IntegerLiteralEvaluator(Integer.parseInt(tree.getText()));
default:
throw new HL7QueryParsingException("Failed to build Integer Evaluator for " + tree.getText());
}
}
private BooleanEvaluator buildBooleanEvaluator(final Tree tree) {
// TODO: add Date comparisons
// LT/GT/GE/GE should allow for dates based on Field's Type
// BETWEEN
// DATE('2015/01/01')
// DATE('2015/01/01 12:00:00')
// DATE('24 HOURS AGO')
// DATE('YESTERDAY')
switch (tree.getType()) {
case EQUALS:
return new EqualsEvaluator(buildReferenceEvaluator(tree.getChild(0)), buildReferenceEvaluator(tree.getChild(1)));
case NOT_EQUALS:
return new NotEqualsEvaluator(buildReferenceEvaluator(tree.getChild(0)), buildReferenceEvaluator(tree.getChild(1)));
case GT:
return new GreaterThanEvaluator(buildReferenceEvaluator(tree.getChild(0)), buildReferenceEvaluator(tree.getChild(1)));
case LT:
return new LessThanEvaluator(buildReferenceEvaluator(tree.getChild(0)), buildReferenceEvaluator(tree.getChild(1)));
case GE:
return new GreaterThanOrEqualEvaluator(buildReferenceEvaluator(tree.getChild(0)), buildReferenceEvaluator(tree.getChild(1)));
case LE:
return new LessThanOrEqualEvaluator(buildReferenceEvaluator(tree.getChild(0)), buildReferenceEvaluator(tree.getChild(1)));
case NOT:
return new NotEvaluator(buildBooleanEvaluator(tree.getChild(0)));
case AND:
return new AndEvaluator(buildBooleanEvaluator(tree.getChild(0)), buildBooleanEvaluator(tree.getChild(1)));
case OR:
return new OrEvaluator(buildBooleanEvaluator(tree.getChild(0)), buildBooleanEvaluator(tree.getChild(1)));
case IS_NULL:
return new IsNullEvaluator(buildReferenceEvaluator(tree.getChild(0)));
case NOT_NULL:
return new NotNullEvaluator(buildReferenceEvaluator(tree.getChild(0)));
default:
throw new HL7QueryParsingException("Cannot build boolean evaluator for '" + tree.getText() + "'");
}
}
Tree getTree() {
return tree;
}
public String getQuery() {
return query;
}
@Override
public String toString() {
return "HL7Query[" + query + "]";
}
public static HL7Query compile(final String query) {
try {
final CommonTokenStream lexerTokenStream = createTokenStream(query);
final HL7QueryParser parser = new HL7QueryParser(lexerTokenStream);
final Tree tree = (Tree) parser.query().getTree();
return new HL7Query(tree, query);
} catch (final HL7QueryParsingException e) {
throw e;
} catch (final Exception e) {
throw new HL7QueryParsingException(e);
}
}
private static CommonTokenStream createTokenStream(final String expression) throws HL7QueryParsingException {
final CharStream input = new ANTLRStringStream(expression);
final HL7QueryLexer lexer = new HL7QueryLexer(input);
return new CommonTokenStream(lexer);
}
public List<Class<?>> getReturnTypes() {
final List<Class<?>> returnTypes = new ArrayList<>();
for ( final Selection selection : selections ) {
returnTypes.add( selection.getEvaluator().getType() );
}
return returnTypes;
}
@SuppressWarnings("unchecked")
public QueryResult evaluate(final HL7Message message) {
int totalIterations = 1;
final LinkedHashMap<String, List<Object>> possibleValueMap = new LinkedHashMap<>();
for ( final Declaration declaration : declarations ) {
final Object value = declaration.getDeclaredValue(message);
if ( value == null && declaration.isRequired() ) {
return new MissedResult(selections);
}
final List<Object> possibleValues;
if ( value instanceof List ) {
possibleValues = (List<Object>) value;
} else if ( value instanceof Collection ) {
possibleValues = new ArrayList<Object>((Collection<Object>) value);
} else {
possibleValues = new ArrayList<>(1);
possibleValues.add(value);
}
if ( possibleValues.isEmpty() ) {
return new MissedResult(selections);
}
possibleValueMap.put(declaration.getAlias(), possibleValues);
totalIterations *= possibleValues.size();
}
final Set<Map<String, Object>> resultSet = new HashSet<>();
for (int i=0; i < totalIterations; i++) {
final Map<String, Object> aliasValues = assignAliases(possibleValueMap, i);
aliasValues.put(Evaluator.MESSAGE_KEY, message);
if (whereEvaluator == null || Boolean.TRUE.equals(whereEvaluator.evaluate(aliasValues))) {
final Map<String, Object> resultMap = new HashMap<>();
for ( final Selection selection : selections ) {
final Object value = selection.getEvaluator().evaluate(aliasValues);
resultMap.put(selection.getName(), value);
}
resultSet.add(resultMap);
}
}
// for ( final Declaration declaration : declarations ) {
// final Object value = declaration.getDeclaredValue(message);
// if ( value == null && declaration.isRequired() ) {
// return new MissedResult(selections);
// }
// objectMap.put(declaration.getAlias(), value);
// }
//
// if (whereEvaluator == null || Boolean.TRUE.equals(whereEvaluator.evaluate(objectMap))) {
// for ( final Selection selection : selections ) {
// final Object value = selection.getEvaluator().evaluate(objectMap);
// resultMap.put(selection.getName(), value);
// }
// } else {
// return new MissedResult(selections);
// }
return new StandardQueryResult(selections, resultSet);
}
// assigns one of the possible values to each alias, based on which iteration this is.
// require LinkedHashMap just to be very clear and explicit that the order of the Map MUST be guaranteed
// between multiple invocations of this method.
// package protected for testing visibility
// static Map<String, Object> assignAliases(final LinkedHashMap<String, List<Object>> possibleValues, final int iteration) {
// final Map<String, Object> aliasMap = new HashMap<>();
//
// int aliasIndex = possibleValues.size() - 1;
// for ( final Map.Entry<String, List<Object>> entry : possibleValues.entrySet() ) {
// final String alias = entry.getKey();
// final List<Object> validValues = entry.getValue();
//
// final int validValueIndex;
// if (aliasIndex > 0) {
// validValueIndex = iteration / aliasIndex;
// } else {
// validValueIndex = iteration;
// }
//
// final Object obj = validValues.get(validValueIndex % validValues.size());
// aliasMap.put(alias, obj);
//
// aliasIndex--;
// }
//
// return aliasMap;
// }
//
static Map<String, Object> assignAliases(final LinkedHashMap<String, List<Object>> possibleValues, final int iteration) {
final Map<String, Object> aliasMap = new HashMap<>();
int divisor = 1;
for ( final Map.Entry<String, List<Object>> entry : possibleValues.entrySet() ) {
final String alias = entry.getKey();
final List<Object> validValues = entry.getValue();
final int idx = (iteration / divisor) % validValues.size();
final Object obj = validValues.get(idx);
aliasMap.put(alias, obj);
divisor *= validValues.size();
}
return aliasMap;
}
public String toTreeString() {
final StringBuilder sb = new StringBuilder();
toTreeString(tree, sb, 0);
return sb.toString();
}
private void toTreeString(final Tree tree, final StringBuilder sb, final int indentLevel) {
final String nodeName = tree.getText();
for (int i=0; i < indentLevel; i++) {
sb.append(" ");
}
sb.append(nodeName);
sb.append("\n");
for (int i=0; i < tree.getChildCount(); i++) {
final Tree child = tree.getChild(i);
toTreeString(child, sb, indentLevel + 2);
}
}
}

View File

@ -0,0 +1,29 @@
/*
* 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.hl7.query;
import java.util.List;
public interface QueryResult {
boolean isMatch();
List<String> getLabels();
int getHitCount();
ResultHit nextHit();
}

View File

@ -0,0 +1,25 @@
/*
* 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.hl7.query;
import java.util.Map;
public interface ResultHit {
Object getValue(String label);
Map<String, Object> getSelectedValues();
}

View File

@ -0,0 +1,37 @@
/*
* 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.hl7.query;
import org.apache.nifi.hl7.query.evaluator.Evaluator;
public class Selection {
private final Evaluator<?> evaluator;
private final String name;
public Selection(final Evaluator<?> evaluator, final String name) {
this.evaluator = evaluator;
this.name = name;
}
public String getName() {
return name;
}
public Evaluator<?> getEvaluator() {
return evaluator;
}
}

View File

@ -0,0 +1,24 @@
/*
* 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.hl7.query.evaluator;
public abstract class BooleanEvaluator implements Evaluator<Boolean> {
public Class<? extends Boolean> getType() {
return Boolean.class;
}
}

View File

@ -0,0 +1,27 @@
/*
* 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.hl7.query.evaluator;
import java.util.Map;
public interface Evaluator<T> {
public static final String MESSAGE_KEY = "message";
T evaluate(Map<String, Object> objectMap);
Class<? extends T> getType();
}

View File

@ -0,0 +1,26 @@
/*
* 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.hl7.query.evaluator;
public abstract class IntegerEvaluator implements Evaluator<Integer> {
public Class<? extends Integer> getType() {
return Integer.class;
}
}

View File

@ -0,0 +1,25 @@
/*
* 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.hl7.query.evaluator;
public abstract class StringEvaluator implements Evaluator<String> {
public Class<? extends String> getType() {
return String.class;
}
}

View File

@ -0,0 +1,106 @@
/*
* 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.hl7.query.evaluator.comparison;
import java.util.Collection;
import java.util.Map;
import org.apache.nifi.hl7.model.HL7Field;
import org.apache.nifi.hl7.query.evaluator.BooleanEvaluator;
import org.apache.nifi.hl7.query.evaluator.Evaluator;
public abstract class AbstractComparisonEvaluator extends BooleanEvaluator {
private final Evaluator<?> lhs;
private final Evaluator<?> rhs;
public AbstractComparisonEvaluator(final Evaluator<?> lhs, final Evaluator<?> rhs) {
this.lhs = lhs;
this.rhs = rhs;
}
public final Boolean evaluate(final Map<String, Object> objectMap) {
final Object lhsValue = lhs.evaluate(objectMap);
if ( lhsValue == null ) {
return false;
}
final Object rhsValue = rhs.evaluate(objectMap);
if ( rhsValue == null ) {
return false;
}
return compareRaw(lhsValue, rhsValue);
}
private Boolean compareRaw(Object lhsValue, Object rhsValue) {
if ( lhsValue == null || rhsValue == null ) {
return false;
}
if ( lhsValue instanceof HL7Field ) {
lhsValue = ((HL7Field) lhsValue).getValue();
}
if ( rhsValue instanceof HL7Field ) {
rhsValue = ((HL7Field) rhsValue).getValue();
}
if ( lhsValue == null || rhsValue == null ) {
return false;
}
// both are collections, and compare(lhsValue, rhsValue) is false.
// this would be the case, for instance, if we compared field 1 of one segment to
// a field in another segment, and both fields had components.
if ( lhsValue instanceof Collection && rhsValue instanceof Collection ) {
return false;
}
// if one side is a collection but the other is not, check if any element in that
// collection compares to the other element in a way that satisfies the condition.
// this would happen, for instance, if we check Segment1.Field5 = 'X' and field 5 repeats
// with a value "A~B~C~X~Y~Z"; in this case we do want to consider Field 5 = X as true.
if ( lhsValue instanceof Collection ) {
for ( final Object lhsObject : (Collection<?>) lhsValue ) {
if ( compareRaw(lhsObject, rhsValue) ) {
return true;
}
}
return false;
}
if ( rhsValue instanceof Collection ) {
for ( final Object rhsObject : (Collection<?>) rhsValue ) {
if ( compareRaw(rhsObject, lhsValue) ) {
return true;
}
}
return false;
}
if ( lhsValue != null && rhsValue != null && compare(lhsValue, rhsValue) ) {
return true;
}
return false;
}
protected abstract boolean compare(Object lhs, Object rhs);
}

View File

@ -0,0 +1,67 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nifi.hl7.query.evaluator.comparison;
import java.util.regex.Pattern;
import org.apache.nifi.hl7.query.evaluator.Evaluator;
public abstract class AbstractNumericComparison extends AbstractComparisonEvaluator {
private static final Pattern NUMERIC_PATTERN = Pattern.compile("\\d+(\\.\\d+)?");
public AbstractNumericComparison(final Evaluator<?> lhs, final Evaluator<?> rhs) {
super(lhs, rhs);
}
@Override
protected final boolean compare(final Object lhs, final Object rhs) {
final Double lhsDouble = toDouble(lhs);
if ( lhsDouble == null ) {
return false;
}
final Double rhsDouble = toDouble(rhs);
if ( rhsDouble == null ) {
return false;
}
return compareNumbers(lhsDouble, rhsDouble);
}
private Double toDouble(final Object value) {
if ( value == null ) {
return null;
}
if ( value instanceof Double ) {
return (Double) value;
}
if ( value instanceof Number ) {
return ((Number) value).doubleValue();
}
if ( value instanceof String ) {
if ( NUMERIC_PATTERN.matcher((String) value).matches() ) {
return Double.parseDouble((String) value);
}
}
return null;
}
protected abstract boolean compareNumbers(final Double lhs, final Double rhs);
}

View File

@ -0,0 +1,32 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nifi.hl7.query.evaluator.comparison;
import org.apache.nifi.hl7.query.evaluator.Evaluator;
public class EqualsEvaluator extends AbstractComparisonEvaluator {
public EqualsEvaluator(final Evaluator<?> lhs, final Evaluator<?> rhs) {
super(lhs, rhs);
}
@Override
protected boolean compare(final Object lhs, final Object rhs) {
return lhs != null && rhs != null && ((lhs == rhs) || (lhs.equals(rhs)) || lhs.toString().equals(rhs.toString()));
}
}

View File

@ -0,0 +1,34 @@
/*
* 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.hl7.query.evaluator.comparison;
import org.apache.nifi.hl7.query.evaluator.Evaluator;
public class GreaterThanEvaluator extends AbstractNumericComparison {
public GreaterThanEvaluator(final Evaluator<?> lhs, final Evaluator<?> rhs) {
super(lhs, rhs);
}
@Override
protected boolean compareNumbers(final Double lhs, final Double rhs) {
return lhs > rhs;
}
}

View File

@ -0,0 +1,34 @@
/*
* 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.hl7.query.evaluator.comparison;
import org.apache.nifi.hl7.query.evaluator.Evaluator;
public class GreaterThanOrEqualEvaluator extends AbstractNumericComparison {
public GreaterThanOrEqualEvaluator(final Evaluator<?> lhs, final Evaluator<?> rhs) {
super(lhs, rhs);
}
@Override
protected boolean compareNumbers(final Double lhs, final Double rhs) {
return lhs >= rhs;
}
}

View File

@ -0,0 +1,69 @@
/*
* 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.hl7.query.evaluator.comparison;
import java.util.Collection;
import java.util.Map;
import org.apache.nifi.hl7.model.HL7Component;
import org.apache.nifi.hl7.query.evaluator.BooleanEvaluator;
import org.apache.nifi.hl7.query.evaluator.Evaluator;
public class IsNullEvaluator extends BooleanEvaluator {
private final Evaluator<?> subjectEvaluator;
public IsNullEvaluator(final Evaluator<?> subjectEvaluator) {
this.subjectEvaluator = subjectEvaluator;
}
@Override
public Boolean evaluate(final Map<String, Object> objectMap) {
Object subjectValue = subjectEvaluator.evaluate(objectMap);
if ( subjectValue == null ) {
return true;
}
return isNull(subjectValue);
}
private boolean isNull(Object subjectValue) {
if ( subjectValue == null ) {
return true;
}
if ( subjectValue instanceof HL7Component ) {
subjectValue = ((HL7Component) subjectValue).getValue();
}
if ( subjectValue instanceof Collection ) {
final Collection<?> collection = (Collection<?>) subjectValue;
if ( collection.isEmpty() ) {
return true;
}
for ( final Object obj : collection ) {
if ( !isNull(obj) ) {
return false;
}
}
return true;
}
return subjectValue == null;
}
}

View File

@ -0,0 +1,31 @@
/*
* 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.hl7.query.evaluator.comparison;
import org.apache.nifi.hl7.query.evaluator.Evaluator;
public class LessThanEvaluator extends AbstractNumericComparison {
public LessThanEvaluator(final Evaluator<?> lhs, final Evaluator<?> rhs) {
super(lhs, rhs);
}
@Override
protected boolean compareNumbers(final Double lhs, final Double rhs) {
return lhs < rhs;
}
}

View File

@ -0,0 +1,31 @@
/*
* 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.hl7.query.evaluator.comparison;
import org.apache.nifi.hl7.query.evaluator.Evaluator;
public class LessThanOrEqualEvaluator extends AbstractNumericComparison {
public LessThanOrEqualEvaluator(final Evaluator<?> lhs, final Evaluator<?> rhs) {
super(lhs, rhs);
}
@Override
protected boolean compareNumbers(final Double lhs, final Double rhs) {
return lhs <= rhs;
}
}

View File

@ -0,0 +1,32 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nifi.hl7.query.evaluator.comparison;
import org.apache.nifi.hl7.query.evaluator.Evaluator;
public class NotEqualsEvaluator extends AbstractComparisonEvaluator {
public NotEqualsEvaluator(final Evaluator<?> lhs, final Evaluator<?> rhs) {
super(lhs, rhs);
}
@Override
protected boolean compare(final Object lhs, final Object rhs) {
return lhs != null && rhs != null && lhs != rhs && !lhs.equals(rhs) && !lhs.toString().equals(rhs.toString());
}
}

View File

@ -0,0 +1,36 @@
/*
* 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.hl7.query.evaluator.comparison;
import java.util.Map;
import org.apache.nifi.hl7.query.evaluator.BooleanEvaluator;
public class NotEvaluator extends BooleanEvaluator {
private final BooleanEvaluator subjectEvaluator;
public NotEvaluator(final BooleanEvaluator subjectEvaluator) {
this.subjectEvaluator = subjectEvaluator;
}
@Override
public Boolean evaluate(final Map<String, Object> objectMap) {
final Boolean subjectValue = subjectEvaluator.evaluate(objectMap);
return (subjectValue == null || Boolean.TRUE.equals(subjectValue));
}
}

View File

@ -0,0 +1,65 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nifi.hl7.query.evaluator.comparison;
import java.util.Collection;
import java.util.Map;
import org.apache.nifi.hl7.model.HL7Component;
import org.apache.nifi.hl7.query.evaluator.BooleanEvaluator;
import org.apache.nifi.hl7.query.evaluator.Evaluator;
public class NotNullEvaluator extends BooleanEvaluator {
private final Evaluator<?> subjectEvaluator;
public NotNullEvaluator(final Evaluator<?> subjectEvaluator) {
this.subjectEvaluator = subjectEvaluator;
}
@Override
public Boolean evaluate(final Map<String, Object> objectMap) {
Object subjectValue = subjectEvaluator.evaluate(objectMap);
if ( subjectValue == null ) {
return false;
}
return isNotNull(subjectValue);
}
private boolean isNotNull(Object subjectValue) {
if ( subjectValue instanceof HL7Component ) {
subjectValue = ((HL7Component) subjectValue).getValue();
}
if ( subjectValue instanceof Collection ) {
final Collection<?> collection = (Collection<?>) subjectValue;
if ( collection.isEmpty() ) {
return false;
}
for ( final Object obj : collection ) {
if ( isNotNull(obj) ) {
return true;
}
}
return false;
}
return subjectValue != null;
}
}

View File

@ -0,0 +1,36 @@
/*
* 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.hl7.query.evaluator.literal;
import java.util.Map;
import org.apache.nifi.hl7.query.evaluator.IntegerEvaluator;
public class IntegerLiteralEvaluator extends IntegerEvaluator {
private final Integer value;
public IntegerLiteralEvaluator(final Integer value) {
this.value = value;
}
@Override
public Integer evaluate(final Map<String, Object> objectMap) {
return value;
}
}

View File

@ -0,0 +1,35 @@
/*
* 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.hl7.query.evaluator.literal;
import java.util.Map;
import org.apache.nifi.hl7.query.evaluator.StringEvaluator;
public class StringLiteralEvaluator extends StringEvaluator {
private final String value;
public StringLiteralEvaluator(final String value) {
this.value = value;
}
@Override
public String evaluate(final Map<String, Object> objectMap) {
return value;
}
}

View File

@ -0,0 +1,43 @@
/*
* 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.hl7.query.evaluator.logic;
import java.util.Map;
import org.apache.nifi.hl7.query.evaluator.BooleanEvaluator;
public class AndEvaluator extends BooleanEvaluator {
private final BooleanEvaluator lhs;
private final BooleanEvaluator rhs;
public AndEvaluator(final BooleanEvaluator lhs, final BooleanEvaluator rhs) {
this.lhs = lhs;
this.rhs = rhs;
}
@Override
public Boolean evaluate(final Map<String, Object> objectMap) {
final Boolean lhsValue = lhs.evaluate(objectMap);
if ( lhsValue == null || Boolean.FALSE.equals(lhsValue) ) {
return false;
}
final Boolean rhsValue = rhs.evaluate(objectMap);
return (rhsValue != null && Boolean.TRUE.equals(rhsValue));
}
}

View File

@ -0,0 +1,43 @@
/*
* 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.hl7.query.evaluator.logic;
import java.util.Map;
import org.apache.nifi.hl7.query.evaluator.BooleanEvaluator;
public class OrEvaluator extends BooleanEvaluator {
private final BooleanEvaluator lhs;
private final BooleanEvaluator rhs;
public OrEvaluator(final BooleanEvaluator lhs, final BooleanEvaluator rhs) {
this.lhs = lhs;
this.rhs = rhs;
}
@Override
public Boolean evaluate(final Map<String, Object> objectMap) {
final Boolean lhsValue = lhs.evaluate(objectMap);
if ( lhsValue != null && Boolean.TRUE.equals(lhsValue) ) {
return true;
}
final Boolean rhsValue = rhs.evaluate(objectMap);
return (rhsValue != null && Boolean.TRUE.equals(rhsValue));
}
}

View File

@ -0,0 +1,42 @@
/*
* 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.hl7.query.evaluator.message;
import java.util.Map;
import org.apache.nifi.hl7.query.evaluator.Evaluator;
import org.apache.nifi.hl7.query.evaluator.StringEvaluator;
public class DeclaredReferenceEvaluator implements Evaluator<Object> {
private final StringEvaluator referenceNameEvaluator;
public DeclaredReferenceEvaluator(final StringEvaluator referenceNameEvaluator) {
this.referenceNameEvaluator = referenceNameEvaluator;
}
@Override
public Object evaluate(final Map<String, Object> objectMap) {
final String referenceName = referenceNameEvaluator.evaluate(objectMap);
return objectMap.get(referenceName);
}
@Override
public Class<? extends Object> getType() {
return Object.class;
}
}

View File

@ -0,0 +1,88 @@
/*
* 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.hl7.query.evaluator.message;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.apache.nifi.hl7.model.HL7Component;
import org.apache.nifi.hl7.model.HL7Message;
import org.apache.nifi.hl7.model.HL7Segment;
import org.apache.nifi.hl7.query.evaluator.Evaluator;
import org.apache.nifi.hl7.query.evaluator.IntegerEvaluator;
public class DotEvaluator implements Evaluator<Object> {
private final Evaluator<?> lhs;
private final IntegerEvaluator rhs;
public DotEvaluator(final Evaluator<?> lhs, final IntegerEvaluator rhs) {
this.lhs = lhs;
this.rhs = rhs;
}
@Override
public Object evaluate(final Map<String, Object> objectMap) {
final Object lhsValue = this.lhs.evaluate(objectMap);
final Integer rhsValue = this.rhs.evaluate(objectMap);
if ( lhsValue == null || rhsValue == null ) {
return null;
}
final List<Object> results = new ArrayList<>();
if ( lhsValue instanceof Collection ) {
final Collection<?> lhsCollection = (Collection<?>) lhsValue;
for ( final Object obj : lhsCollection ) {
final Object val = getValue(obj, rhsValue);
results.add(val);
}
} else {
final Object val = getValue(lhsValue, rhsValue);
return val;
}
return results;
}
private Object getValue(final Object lhsValue, final int rhsValue) {
final List<?> list;
if ( lhsValue instanceof HL7Message ) {
list = ((HL7Message) lhsValue).getSegments();
} else if ( lhsValue instanceof HL7Segment ) {
list = ((HL7Segment) lhsValue).getFields();
} else if ( lhsValue instanceof HL7Component ) {
list = ((HL7Component) lhsValue).getComponents();
} else {
return null;
}
if ( rhsValue > list.size() ) {
return null;
}
// convert from 0-based to 1-based
return list.get(rhsValue - 1);
}
@Override
public Class<? extends Object> getType() {
return Object.class;
}
}

View File

@ -0,0 +1,67 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nifi.hl7.query.evaluator.message;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.apache.nifi.hl7.model.HL7Field;
import org.apache.nifi.hl7.model.HL7Segment;
import org.apache.nifi.hl7.query.evaluator.Evaluator;
import org.apache.nifi.hl7.query.evaluator.IntegerEvaluator;
@SuppressWarnings("rawtypes")
public class FieldEvaluator implements Evaluator<List> {
private final SegmentEvaluator segmentEvaluator;
private final IntegerEvaluator indexEvaluator;
public FieldEvaluator(final SegmentEvaluator segmentEvaluator, final IntegerEvaluator indexEvaluator) {
this.segmentEvaluator = segmentEvaluator;
this.indexEvaluator = indexEvaluator;
}
public List<HL7Field> evaluate(final Map<String, Object> objectMap) {
final List<HL7Segment> segments = segmentEvaluator.evaluate(objectMap);
if ( segments == null ) {
return Collections.emptyList();
}
final Integer index = indexEvaluator.evaluate(objectMap);
if ( index == null ) {
return Collections.emptyList();
}
final List<HL7Field> fields = new ArrayList<>();
for ( final HL7Segment segment : segments ) {
final List<HL7Field> segmentFields = segment.getFields();
if ( segmentFields.size() <= index ) {
continue;
}
fields.add(segmentFields.get(index));
}
return fields;
}
public Class<? extends List> getType() {
return List.class;
}
}

View File

@ -0,0 +1,34 @@
/*
* 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.hl7.query.evaluator.message;
import java.util.Map;
import org.apache.nifi.hl7.model.HL7Message;
import org.apache.nifi.hl7.query.evaluator.Evaluator;
public class MessageEvaluator implements Evaluator<HL7Message> {
public HL7Message evaluate(final Map<String, Object> objectMap) {
return (HL7Message) objectMap.get(Evaluator.MESSAGE_KEY);
}
public Class<? extends HL7Message> getType() {
return HL7Message.class;
}
}

View File

@ -0,0 +1,51 @@
/*
* 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.hl7.query.evaluator.message;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.apache.nifi.hl7.model.HL7Message;
import org.apache.nifi.hl7.model.HL7Segment;
import org.apache.nifi.hl7.query.evaluator.Evaluator;
import org.apache.nifi.hl7.query.evaluator.StringEvaluator;
@SuppressWarnings("rawtypes")
public class SegmentEvaluator implements Evaluator<List> {
private final StringEvaluator segmentTypeEvaluator;
public SegmentEvaluator(final StringEvaluator segmentTypeEvaluator) {
this.segmentTypeEvaluator = segmentTypeEvaluator;
}
public List<HL7Segment> evaluate(final Map<String, Object> objectMap) {
final String segmentType = segmentTypeEvaluator.evaluate(objectMap);
if ( segmentType == null ) {
return Collections.emptyList();
}
final HL7Message message = (HL7Message) objectMap.get(Evaluator.MESSAGE_KEY);
final List<HL7Segment> segments = message.getSegments(segmentType);
return (segments == null) ? Collections.<HL7Segment>emptyList() : segments;
}
public Class<? extends List> getType() {
return List.class;
}
}

View File

@ -0,0 +1,37 @@
/*
* 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.hl7.query.exception;
public class HL7QueryParsingException extends RuntimeException {
private static final long serialVersionUID = 1L;
public HL7QueryParsingException() {
super();
}
public HL7QueryParsingException(final Throwable cause) {
super(cause);
}
public HL7QueryParsingException(final String message) {
super(message);
}
public HL7QueryParsingException(final String message, final Throwable cause) {
super(message, cause);
}
}

View File

@ -0,0 +1,56 @@
/*
* 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.hl7.query.result;
import java.util.ArrayList;
import java.util.List;
import org.apache.nifi.hl7.query.QueryResult;
import org.apache.nifi.hl7.query.ResultHit;
import org.apache.nifi.hl7.query.Selection;
public class MissedResult implements QueryResult {
private final List<Selection> selections;
public MissedResult(final List<Selection> selections) {
this.selections = selections;
}
@Override
public List<String> getLabels() {
final List<String> labels = new ArrayList<>();
for ( final Selection selection : selections ) {
labels.add(selection.getName());
}
return labels;
}
@Override
public boolean isMatch() {
return false;
}
@Override
public ResultHit nextHit() {
return null;
}
@Override
public int getHitCount() {
return 0;
}
}

View File

@ -0,0 +1,69 @@
/*
* 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.hl7.query.result;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.nifi.hl7.query.QueryResult;
import org.apache.nifi.hl7.query.ResultHit;
import org.apache.nifi.hl7.query.Selection;
public class StandardQueryResult implements QueryResult {
private final List<Selection> selections;
private final Set<Map<String, Object>> hits;
private final Iterator<Map<String, Object>> hitIterator;
public StandardQueryResult(final List<Selection> selections, final Set<Map<String, Object>> hits) {
this.selections = selections;
this.hits = hits;
hitIterator = hits.iterator();
}
@Override
public boolean isMatch() {
return !hits.isEmpty();
}
@Override
public List<String> getLabels() {
final List<String> labels = new ArrayList<>();
for ( final Selection selection : selections ) {
labels.add(selection.getName());
}
return labels;
}
@Override
public int getHitCount() {
return hits.size();
}
@Override
public ResultHit nextHit() {
if ( hitIterator.hasNext() ) {
return new StandardResultHit(hitIterator.next());
} else {
return null;
}
}
}

View File

@ -0,0 +1,41 @@
/*
* 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.hl7.query.result;
import java.util.Collections;
import java.util.Map;
import org.apache.nifi.hl7.query.ResultHit;
public class StandardResultHit implements ResultHit {
private final Map<String, Object> values;
public StandardResultHit(final Map<String, Object> values) {
this.values = values;
}
@Override
public Object getValue(final String label) {
return values.get(label);
}
@Override
public Map<String, Object> getSelectedValues() {
return Collections.unmodifiableMap(values);
}
}

View File

@ -0,0 +1,310 @@
/*
* 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.hl7.query;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.nifi.hl7.hapi.HapiMessage;
import org.apache.nifi.hl7.model.HL7Field;
import org.apache.nifi.hl7.model.HL7Message;
import org.junit.Test;
import ca.uhn.hl7v2.DefaultHapiContext;
import ca.uhn.hl7v2.HL7Exception;
import ca.uhn.hl7v2.HapiContext;
import ca.uhn.hl7v2.model.Message;
import ca.uhn.hl7v2.parser.PipeParser;
import ca.uhn.hl7v2.validation.impl.ValidationContextFactory;
@SuppressWarnings("resource")
public class TestHL7Query {
@Test
public void testAssignAliases() {
final LinkedHashMap<String, List<Object>> possibleValueMap = new LinkedHashMap<>();
final List<Object> valuesA = new ArrayList<>();
valuesA.add("a");
valuesA.add("b");
valuesA.add("c");
final List<Object> valuesB = new ArrayList<>();
valuesB.add("d");
final List<Object> valuesC = new ArrayList<>();
valuesC.add("e");
valuesC.add("f");
final List<Object> valuesD = new ArrayList<>();
valuesD.add("g");
valuesD.add("h");
possibleValueMap.put("A", valuesA);
possibleValueMap.put("B", valuesB);
possibleValueMap.put("C", valuesC);
possibleValueMap.put("D", valuesD);
for (int i=0; i < valuesA.size() * valuesB.size() * valuesC.size() * valuesD.size(); i++) {
System.out.println(i + " : " + HL7Query.assignAliases(possibleValueMap, i));
}
verifyAssignments(HL7Query.assignAliases(possibleValueMap, 0), "a", "d", "e", "g");
verifyAssignments(HL7Query.assignAliases(possibleValueMap, 1), "b", "d", "e", "g");
verifyAssignments(HL7Query.assignAliases(possibleValueMap, 2), "c", "d", "e", "g");
verifyAssignments(HL7Query.assignAliases(possibleValueMap, 3), "a", "d", "f", "g");
verifyAssignments(HL7Query.assignAliases(possibleValueMap, 4), "b", "d", "f", "g");
verifyAssignments(HL7Query.assignAliases(possibleValueMap, 5), "c", "d", "f", "g");
verifyAssignments(HL7Query.assignAliases(possibleValueMap, 6), "a", "d", "e", "h");
verifyAssignments(HL7Query.assignAliases(possibleValueMap, 7), "b", "d", "e", "h");
verifyAssignments(HL7Query.assignAliases(possibleValueMap, 8), "c", "d", "e", "h");
verifyAssignments(HL7Query.assignAliases(possibleValueMap, 9), "a", "d", "f", "h");
verifyAssignments(HL7Query.assignAliases(possibleValueMap, 10), "b", "d", "f", "h");
verifyAssignments(HL7Query.assignAliases(possibleValueMap, 11), "c", "d", "f", "h");
}
private void verifyAssignments(final Map<String, Object> map, final String a, final String b, final String c, final String d) {
assertEquals(a, map.get("A"));
assertEquals(b, map.get("B"));
assertEquals(c, map.get("C"));
assertEquals(d, map.get("D"));
}
@Test
public void testSelectMessage() throws HL7Exception, IOException {
final HL7Query query = HL7Query.compile("SELECT MESSAGE");
final HL7Message msg = createMessage(new File("src/test/resources/hypoglycemia"));
final QueryResult result = query.evaluate(msg);
assertTrue(result.isMatch());
final List<String> labels = result.getLabels();
assertEquals(1, labels.size());
assertEquals("MESSAGE", labels.get(0));
assertEquals(1, result.getHitCount());
assertEquals(msg, result.nextHit().getValue("MESSAGE"));
}
@Test
@SuppressWarnings({ "unchecked", "rawtypes" })
public void testSelectField() throws HL7Exception, IOException {
final HL7Query query = HL7Query.compile("SELECT PID.5");
final HL7Message msg = createMessage(new File("src/test/resources/hypoglycemia"));
final QueryResult result = query.evaluate(msg);
assertTrue(result.isMatch());
final List<String> labels = result.getLabels();
assertEquals(1, labels.size());
assertEquals(1, result.getHitCount());
final Object names = result.nextHit().getValue("PID.5");
assertTrue(names instanceof List);
final List<Object> nameList = (List) names;
assertEquals(1, nameList.size());
final HL7Field nameField = (HL7Field) nameList.get(0);
assertEquals("SMITH^JOHN", nameField.getValue());
}
@Test
public void testSelectAbnormalTestResult() throws HL7Exception, IOException {
final String query = "DECLARE result AS REQUIRED OBX SELECT result WHERE result.7 != 'N' AND result.1 = 1";
final HL7Query hl7Query = HL7Query.compile(query);
final QueryResult result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
assertTrue( result.isMatch() );
}
@Test
public void testFieldEqualsString() throws HL7Exception, IOException {
HL7Query hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.7 = 'L'");
QueryResult result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
assertTrue( result.isMatch() );
hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.7 = 'H'");
result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
assertFalse( result.isMatch() );
}
@Test
public void testLessThan() throws HL7Exception, IOException {
HL7Query hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.4 < 600");
QueryResult result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
assertTrue( result.isMatch() );
hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.4 < 59");
result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
assertFalse( result.isMatch() );
}
@Test
public void testCompareTwoFields() throws HL7Exception, IOException {
HL7Query hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.4 < result.6.2");
QueryResult result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
assertTrue( result.isMatch() );
hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE NOT(result.4 > result.6.3)");
result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
assertFalse( result.isMatch() );
}
@Test
public void testLessThanOrEqual() throws HL7Exception, IOException {
HL7Query hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.4 <= 59");
QueryResult result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
assertTrue( result.isMatch() );
hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.4 <= 600");
result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
assertTrue( result.isMatch() );
hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.4 <= 58");
result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
assertFalse( result.isMatch() );
}
@Test
public void testGreaterThanOrEqual() throws HL7Exception, IOException {
HL7Query hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.4 >= 59");
QueryResult result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
assertTrue( result.isMatch() );
hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.4 >= 6");
result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
assertTrue( result.isMatch() );
hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.4 >= 580");
result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
assertFalse( result.isMatch() );
}
@Test
public void testGreaterThan() throws HL7Exception, IOException {
HL7Query hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.4 > 58");
QueryResult result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
assertTrue( result.isMatch() );
hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.4 > 6");
result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
assertTrue( result.isMatch() );
hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.4 > 580");
result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
assertFalse( result.isMatch() );
}
@Test
public void testDistinctValuesReturned() throws HL7Exception, IOException {
HL7Query hl7Query = HL7Query.compile("DECLARE result1 AS REQUIRED OBX, result2 AS REQUIRED OBX SELECT MESSAGE WHERE result1.7 = 'L' OR result2.7 != 'H'");
QueryResult result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
assertTrue( result.isMatch() );
assertEquals(1, result.getHitCount());
}
@Test
public void testAndWithParens() throws HL7Exception, IOException {
HL7Query hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.7 = 'L' AND result.3.1 = 'GLU'");
QueryResult result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
assertTrue( result.isMatch() );
hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.7 = 'L' AND result.3.1 = 'GLU'");
result = hl7Query.evaluate(createMessage(new File("src/test/resources/hyperglycemia")));
assertFalse( result.isMatch() );
hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.7 = 'H' AND result.3.1 = 'GLU'");
result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
assertFalse( result.isMatch() );
hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.7 = 'H' AND result.3.1 = 'GLU'");
result = hl7Query.evaluate(createMessage(new File("src/test/resources/hyperglycemia")));
assertTrue( result.isMatch() );
hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE (result.7 = 'H') AND (result.3.1 = 'GLU')");
result = hl7Query.evaluate(createMessage(new File("src/test/resources/hyperglycemia")));
assertTrue( result.isMatch() );
hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE ((result.7 = 'H') AND (result.3.1 = 'GLU'))");
result = hl7Query.evaluate(createMessage(new File("src/test/resources/hyperglycemia")));
assertTrue( result.isMatch() );
hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE (( ((result.7 = 'H')) AND ( ((result.3.1 = 'GLU')) )))");
result = hl7Query.evaluate(createMessage(new File("src/test/resources/hyperglycemia")));
assertTrue( result.isMatch() );
}
@Test
public void testIsNull() throws HL7Exception, IOException {
HL7Query hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.999 IS NULL");
QueryResult result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
assertTrue( result.isMatch() );
hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.1 IS NULL");
result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
assertFalse( result.isMatch() );
hl7Query = HL7Query.compile("SELECT MESSAGE WHERE ZZZ IS NULL");
result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
assertTrue( result.isMatch() );
hl7Query = HL7Query.compile("SELECT MESSAGE WHERE OBX IS NULL");
result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
assertFalse( result.isMatch() );
}
@Test
public void testNotNull() throws HL7Exception, IOException {
HL7Query hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.999 NOT NULL");
QueryResult result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
assertFalse( result.isMatch() );
hl7Query = HL7Query.compile("DECLARE result AS REQUIRED OBX SELECT MESSAGE WHERE result.1 NOT NULL");
result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
assertTrue( result.isMatch() );
hl7Query = HL7Query.compile("SELECT MESSAGE WHERE ZZZ NOT NULL");
result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
assertFalse( result.isMatch() );
hl7Query = HL7Query.compile("SELECT MESSAGE WHERE OBX NOT NULL");
result = hl7Query.evaluate(createMessage(new File("src/test/resources/hypoglycemia")));
assertTrue( result.isMatch() );
}
private HL7Message createMessage(final File file) throws HL7Exception, IOException {
final byte[] bytes = Files.readAllBytes(file.toPath());
final String msgText = new String(bytes, "UTF-8");
final HapiContext hapiContext = new DefaultHapiContext();
hapiContext.setValidationContext(ValidationContextFactory.noValidation());
final PipeParser parser = hapiContext.getPipeParser();
final Message message = parser.parse(msgText);
return new HapiMessage(message);
}
}

View File

@ -0,0 +1,5 @@
MSH|^~\&|XXXXXX||HealthOrg01||||ORU^R01|Q1111111111111111111|P|2.3|
PID|||000000001||SMITH^JOHN||19700101|M||||||||||999999999999|123456789|
PD1||||1234567890^LAST^FIRST^M^^^^^NPI|
OBR|1|341856649^HNAM_ORDERID|000000000000000000|648088^Basic Metabolic Panel|||20150101000100|||||||||1620^Johnson^John^R||||||20150101000100|||M|||||||||||20150101000100|
OBX|1|NM|GLU^Glucose Lvl|159|mg/dL|65-99^65^99|H|||F|||20150101000100|

View File

@ -0,0 +1,5 @@
MSH|^~\&|XXXXXX||HealthOrg01||||ORU^R01|Q1111111111111111111|P|2.3|
PID|||000000001||SMITH^JOHN||19700101|M||||||||||999999999999|123456789|
PD1||||1234567890^LAST^FIRST^M^^^^^NPI|
OBR|1|341856649^HNAM_ORDERID|000000000000000000|648088^Basic Metabolic Panel|||20150101000100|||||||||1620^Johnson^John^R||||||20150101000100|||M|||||||||||20150101000100|
OBX|1|NM|GLU^Glucose Lvl|59|mg/dL|65-99^65^99|L|||F|||20150101000100|

View File

@ -88,6 +88,7 @@ import org.apache.nifi.web.api.dto.ControllerDTO;
import org.apache.nifi.web.api.dto.PortDTO; import org.apache.nifi.web.api.dto.PortDTO;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.slf4j.helpers.MessageFormatter;
public class EndpointConnectionPool { public class EndpointConnectionPool {
public static final long PEER_REFRESH_PERIOD = 60000L; public static final long PEER_REFRESH_PERIOD = 60000L;
@ -202,6 +203,28 @@ public class EndpointConnectionPool {
}, 5, 5, TimeUnit.SECONDS); }, 5, 5, TimeUnit.SECONDS);
} }
void warn(final String msg, final Object... args) {
logger.warn(msg, args);
if ( eventReporter != null ) {
eventReporter.reportEvent(Severity.WARNING, "Site-to-Site", MessageFormatter.arrayFormat(msg, args).getMessage());
}
}
void warn(final String msg, final Throwable t) {
logger.warn(msg, t);
if ( eventReporter != null ) {
eventReporter.reportEvent(Severity.WARNING, "Site-to-Site", msg + ": " + t.toString());
}
}
void error(final String msg, final Object... args) {
logger.error(msg, args);
if ( eventReporter != null ) {
eventReporter.reportEvent(Severity.ERROR, "Site-to-Site", MessageFormatter.arrayFormat(msg, args).getMessage());
}
}
private String getPortIdentifier(final TransferDirection transferDirection) throws IOException { private String getPortIdentifier(final TransferDirection transferDirection) throws IOException {
if ( remoteDestination.getIdentifier() != null ) { if ( remoteDestination.getIdentifier() != null ) {
return remoteDestination.getIdentifier(); return remoteDestination.getIdentifier();
@ -271,6 +294,7 @@ public class EndpointConnectionPool {
logger.debug("{} No Connection available for Port {}; creating new Connection", this, portId); logger.debug("{} No Connection available for Port {}; creating new Connection", this, portId);
protocol = new SocketClientProtocol(); protocol = new SocketClientProtocol();
protocol.setDestination(new IdEnrichedRemoteDestination(remoteDestination, portId)); protocol.setDestination(new IdEnrichedRemoteDestination(remoteDestination, portId));
protocol.setEventReporter(eventReporter);
final long penalizationMillis = remoteDestination.getYieldPeriod(TimeUnit.MILLISECONDS); final long penalizationMillis = remoteDestination.getYieldPeriod(TimeUnit.MILLISECONDS);
try { try {
@ -312,8 +336,15 @@ public class EndpointConnectionPool {
// handle error cases // handle error cases
if ( protocol.isDestinationFull() ) { if ( protocol.isDestinationFull() ) {
logger.warn("{} {} indicates that port's destination is full; penalizing peer", this, peer); logger.warn("{} {} indicates that port {}'s destination is full; penalizing peer",
this, peer, config.getPortName() == null ? config.getPortIdentifier() : config.getPortName());
penalize(peer, penalizationMillis); penalize(peer, penalizationMillis);
try {
peer.close();
} catch (final IOException ioe) {
}
continue; continue;
} else if ( protocol.isPortInvalid() ) { } else if ( protocol.isPortInvalid() ) {
penalize(peer, penalizationMillis); penalize(peer, penalizationMillis);
@ -336,7 +367,7 @@ public class EndpointConnectionPool {
cleanup(protocol, peer); cleanup(protocol, peer);
final String message = String.format("%s failed to communicate with %s due to %s", this, peer == null ? clusterUrl : peer, e.toString()); final String message = String.format("%s failed to communicate with %s due to %s", this, peer == null ? clusterUrl : peer, e.toString());
logger.error(message); error(message);
if ( logger.isDebugEnabled() ) { if ( logger.isDebugEnabled() ) {
logger.error("", e); logger.error("", e);
} }
@ -359,6 +390,15 @@ public class EndpointConnectionPool {
} }
} }
} while ( connection == null || codec == null || commsSession == null || protocol == null ); } while ( connection == null || codec == null || commsSession == null || protocol == null );
} catch (final Throwable t) {
if ( commsSession != null ) {
try {
commsSession.close();
} catch (final IOException ioe) {
}
}
throw t;
} finally { } finally {
if ( !addBack.isEmpty() ) { if ( !addBack.isEmpty() ) {
connectionQueue.addAll(addBack); connectionQueue.addAll(addBack);
@ -449,7 +489,7 @@ public class EndpointConnectionPool {
peerList = createPeerStatusList(direction); peerList = createPeerStatusList(direction);
} catch (final Exception e) { } catch (final Exception e) {
final String message = String.format("%s Failed to update list of peers due to %s", this, e.toString()); final String message = String.format("%s Failed to update list of peers due to %s", this, e.toString());
logger.warn(message); warn(message);
if ( logger.isDebugEnabled() ) { if ( logger.isDebugEnabled() ) {
logger.warn("", e); logger.warn("", e);
} }
@ -489,7 +529,7 @@ public class EndpointConnectionPool {
} }
private boolean isPenalized(final PeerStatus peerStatus) { private boolean isPenalized(final PeerStatus peerStatus) {
final Long expirationEnd = peerTimeoutExpirations.get(peerStatus); final Long expirationEnd = peerTimeoutExpirations.get(peerStatus.getPeerDescription());
return (expirationEnd == null ? false : expirationEnd > System.currentTimeMillis() ); return (expirationEnd == null ? false : expirationEnd > System.currentTimeMillis() );
} }
@ -573,7 +613,7 @@ public class EndpointConnectionPool {
clientProtocol.shutdown(peer); clientProtocol.shutdown(peer);
} catch (final IOException e) { } catch (final IOException e) {
final String message = String.format("%s Failed to shutdown protocol when updating list of peers due to %s", this, e.toString()); final String message = String.format("%s Failed to shutdown protocol when updating list of peers due to %s", this, e.toString());
logger.warn(message); warn(message);
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.warn("", e); logger.warn("", e);
} }
@ -583,7 +623,7 @@ public class EndpointConnectionPool {
peer.close(); peer.close();
} catch (final IOException e) { } catch (final IOException e) {
final String message = String.format("%s Failed to close resources when updating list of peers due to %s", this, e.toString()); final String message = String.format("%s Failed to close resources when updating list of peers due to %s", this, e.toString());
logger.warn(message); warn(message);
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.warn("", e); logger.warn("", e);
} }
@ -608,7 +648,8 @@ public class EndpointConnectionPool {
} }
} catch (final IOException e) { } catch (final IOException e) {
logger.error("Failed to persist list of Peers due to {}; if restarted and peer's NCM is down, may be unable to transfer data until communications with NCM are restored", e.toString(), e); error("Failed to persist list of Peers due to {}; if restarted and peer's NCM is down, may be unable to transfer data until communications with NCM are restored", e.toString());
logger.error("", e);
} }
} }
@ -804,7 +845,7 @@ public class EndpointConnectionPool {
peerStatusCache = new PeerStatusCache(statuses); peerStatusCache = new PeerStatusCache(statuses);
logger.info("{} Successfully refreshed Peer Status; remote instance consists of {} peers", this, statuses.size()); logger.info("{} Successfully refreshed Peer Status; remote instance consists of {} peers", this, statuses.size());
} catch (Exception e) { } catch (Exception e) {
logger.warn("{} Unable to refresh Remote Group's peers due to {}", this, e); warn("{} Unable to refresh Remote Group's peers due to {}", this, e);
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.warn("", e); logger.warn("", e);
} }

View File

@ -84,6 +84,7 @@ public class SocketClient implements SiteToSiteClient {
logger.debug("Unable to resolve port [{}] to an identifier", portName); logger.debug("Unable to resolve port [{}] to an identifier", portName);
} else { } else {
logger.debug("Resolved port [{}] to identifier [{}]", portName, portId); logger.debug("Resolved port [{}] to identifier [{}]", portName, portId);
this.portIdentifier = portId;
} }
return portId; return portId;
@ -136,7 +137,7 @@ public class SocketClient implements SiteToSiteClient {
connectionState.getPeer(), connectionState.getCodec(), direction); connectionState.getPeer(), connectionState.getCodec(), direction);
} catch (final Throwable t) { } catch (final Throwable t) {
pool.terminate(connectionState); pool.terminate(connectionState);
throw t; throw new IOException("Unable to create Transaction to communicate with " + connectionState.getPeer(), t);
} }
// Wrap the transaction in a new one that will return the EndpointConnectionState back to the pool whenever // Wrap the transaction in a new one that will return the EndpointConnectionState back to the pool whenever

View File

@ -27,6 +27,7 @@ import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.apache.nifi.events.EventReporter;
import org.apache.nifi.flowfile.FlowFile; import org.apache.nifi.flowfile.FlowFile;
import org.apache.nifi.flowfile.attributes.CoreAttributes; import org.apache.nifi.flowfile.attributes.CoreAttributes;
import org.apache.nifi.processor.ProcessContext; import org.apache.nifi.processor.ProcessContext;
@ -75,6 +76,7 @@ public class SocketClientProtocol implements ClientProtocol {
private int batchCount; private int batchCount;
private long batchSize; private long batchSize;
private long batchMillis; private long batchMillis;
private EventReporter eventReporter;
private static final long BATCH_SEND_NANOS = TimeUnit.SECONDS.toNanos(5L); // send batches of up to 5 seconds private static final long BATCH_SEND_NANOS = TimeUnit.SECONDS.toNanos(5L); // send batches of up to 5 seconds
@ -93,6 +95,10 @@ public class SocketClientProtocol implements ClientProtocol {
this.batchMillis = millis; this.batchMillis = millis;
} }
public void setEventReporter(final EventReporter eventReporter) {
this.eventReporter = eventReporter;
}
public void setDestination(final RemoteDestination destination) { public void setDestination(final RemoteDestination destination) {
this.destination = destination; this.destination = destination;
this.useCompression = destination.isUseCompression(); this.useCompression = destination.isUseCompression();
@ -272,7 +278,7 @@ public class SocketClientProtocol implements ClientProtocol {
} }
return new SocketClientTransaction(versionNegotiator.getVersion(), destination.getIdentifier(), peer, codec, return new SocketClientTransaction(versionNegotiator.getVersion(), destination.getIdentifier(), peer, codec,
direction, useCompression, (int) destination.getYieldPeriod(TimeUnit.MILLISECONDS)); direction, useCompression, (int) destination.getYieldPeriod(TimeUnit.MILLISECONDS), eventReporter);
} }

View File

@ -27,6 +27,7 @@ import java.util.zip.CRC32;
import java.util.zip.CheckedInputStream; import java.util.zip.CheckedInputStream;
import java.util.zip.CheckedOutputStream; import java.util.zip.CheckedOutputStream;
import org.apache.nifi.events.EventReporter;
import org.apache.nifi.remote.Communicant; import org.apache.nifi.remote.Communicant;
import org.apache.nifi.remote.Peer; import org.apache.nifi.remote.Peer;
import org.apache.nifi.remote.Transaction; import org.apache.nifi.remote.Transaction;
@ -39,6 +40,7 @@ import org.apache.nifi.remote.io.CompressionOutputStream;
import org.apache.nifi.remote.protocol.DataPacket; import org.apache.nifi.remote.protocol.DataPacket;
import org.apache.nifi.remote.protocol.RequestType; import org.apache.nifi.remote.protocol.RequestType;
import org.apache.nifi.remote.util.StandardDataPacket; import org.apache.nifi.remote.util.StandardDataPacket;
import org.apache.nifi.reporting.Severity;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -56,6 +58,7 @@ public class SocketClientTransaction implements Transaction {
private final Peer peer; private final Peer peer;
private final int penaltyMillis; private final int penaltyMillis;
private final String destinationId; private final String destinationId;
private final EventReporter eventReporter;
private boolean dataAvailable = false; private boolean dataAvailable = false;
private int transfers = 0; private int transfers = 0;
@ -63,7 +66,7 @@ public class SocketClientTransaction implements Transaction {
private TransactionState state; private TransactionState state;
SocketClientTransaction(final int protocolVersion, final String destinationId, final Peer peer, final FlowFileCodec codec, SocketClientTransaction(final int protocolVersion, final String destinationId, final Peer peer, final FlowFileCodec codec,
final TransferDirection direction, final boolean useCompression, final int penaltyMillis) throws IOException { final TransferDirection direction, final boolean useCompression, final int penaltyMillis, final EventReporter eventReporter) throws IOException {
this.protocolVersion = protocolVersion; this.protocolVersion = protocolVersion;
this.destinationId = destinationId; this.destinationId = destinationId;
this.peer = peer; this.peer = peer;
@ -74,6 +77,7 @@ public class SocketClientTransaction implements Transaction {
this.compress = useCompression; this.compress = useCompression;
this.state = TransactionState.TRANSACTION_STARTED; this.state = TransactionState.TRANSACTION_STARTED;
this.penaltyMillis = penaltyMillis; this.penaltyMillis = penaltyMillis;
this.eventReporter = eventReporter;
initialize(); initialize();
} }
@ -116,11 +120,11 @@ public class SocketClientTransaction implements Transaction {
try { try {
try { try {
if ( state != TransactionState.DATA_EXCHANGED && state != TransactionState.TRANSACTION_STARTED) { if ( state != TransactionState.DATA_EXCHANGED && state != TransactionState.TRANSACTION_STARTED) {
throw new IllegalStateException("Cannot receive data because Transaction State is " + state); throw new IllegalStateException("Cannot receive data from " + peer + " because Transaction State is " + state);
} }
if ( direction == TransferDirection.SEND ) { if ( direction == TransferDirection.SEND ) {
throw new IllegalStateException("Attempting to receive data but started a SEND Transaction"); throw new IllegalStateException("Attempting to receive data from " + peer + " but started a SEND Transaction");
} }
// if we already know there's no data, just return null // if we already know there's no data, just return null
@ -142,7 +146,7 @@ public class SocketClientTransaction implements Transaction {
this.dataAvailable = false; this.dataAvailable = false;
break; break;
default: default:
throw new ProtocolException("Got unexpected response when asking for data: " + dataAvailableCode); throw new ProtocolException("Got unexpected response from " + peer + " when asking for data: " + dataAvailableCode);
} }
} }
@ -184,11 +188,11 @@ public class SocketClientTransaction implements Transaction {
try { try {
try { try {
if ( state != TransactionState.DATA_EXCHANGED && state != TransactionState.TRANSACTION_STARTED) { if ( state != TransactionState.DATA_EXCHANGED && state != TransactionState.TRANSACTION_STARTED) {
throw new IllegalStateException("Cannot send data because Transaction State is " + state); throw new IllegalStateException("Cannot send data to " + peer + " because Transaction State is " + state);
} }
if ( direction == TransferDirection.RECEIVE ) { if ( direction == TransferDirection.RECEIVE ) {
throw new IllegalStateException("Attempting to send data but started a RECEIVE Transaction"); throw new IllegalStateException("Attempting to send data to " + peer + " but started a RECEIVE Transaction");
} }
if ( transfers > 0 ) { if ( transfers > 0 ) {
@ -242,7 +246,7 @@ public class SocketClientTransaction implements Transaction {
try { try {
try { try {
if ( state != TransactionState.TRANSACTION_CONFIRMED ) { if ( state != TransactionState.TRANSACTION_CONFIRMED ) {
throw new IllegalStateException("Cannot complete transaction because state is " + state + throw new IllegalStateException("Cannot complete transaction with " + peer + " because state is " + state +
"; Transaction can only be completed when state is " + TransactionState.TRANSACTION_CONFIRMED); "; Transaction can only be completed when state is " + TransactionState.TRANSACTION_CONFIRMED);
} }
@ -272,7 +276,7 @@ public class SocketClientTransaction implements Transaction {
peer.penalize(destinationId, penaltyMillis); peer.penalize(destinationId, penaltyMillis);
backoff = true; backoff = true;
} else if ( transactionResponse.getCode() != ResponseCode.TRANSACTION_FINISHED ) { } else if ( transactionResponse.getCode() != ResponseCode.TRANSACTION_FINISHED ) {
throw new ProtocolException("After sending data, expected TRANSACTION_FINISHED response but got " + transactionResponse); throw new ProtocolException("After sending data to " + peer + ", expected TRANSACTION_FINISHED response but got " + transactionResponse);
} }
state = TransactionState.TRANSACTION_COMPLETED; state = TransactionState.TRANSACTION_COMPLETED;
@ -324,7 +328,10 @@ public class SocketClientTransaction implements Transaction {
try { try {
confirmTransactionResponse = Response.read(dis); confirmTransactionResponse = Response.read(dis);
} catch (final IOException ioe) { } catch (final IOException ioe) {
logger.error("Failed to receive response code from {} when expected confirmation of transaction", peer); logger.error("Failed to receive response code from {} when expecting confirmation of transaction", peer);
if ( eventReporter != null ) {
eventReporter.reportEvent(Severity.ERROR, "Site-to-Site", "Failed to receive response code from " + peer + " when expecting confirmation of transaction");
}
throw ioe; throw ioe;
} }

View File

@ -0,0 +1,42 @@
/*
* 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.util;
public class EscapeUtils {
/**
* Escapes the specified html by replacing &amp;, &lt;, &gt;, &quot;, &#39;, &#x2f;
* with their corresponding html entity. If html is null, null is returned.
*
* @param html
* @return
*/
public static String escapeHtml(String html) {
if (html == null) {
return null;
}
html = html.replace("&", "&amp;");
html = html.replace("<", "&lt;");
html = html.replace(">", "&gt;");
html = html.replace("\"", "&quot;");
html = html.replace("'", "&#39;");
html = html.replace("/", "&#x2f;");
return html;
}
}

View File

@ -36,5 +36,6 @@
<module>nifi-processor-utilities</module> <module>nifi-processor-utilities</module>
<module>nifi-write-ahead-log</module> <module>nifi-write-ahead-log</module>
<module>nifi-site-to-site-client</module> <module>nifi-site-to-site-client</module>
<module>nifi-hl7-query-language</module>
</modules> </modules>
</project> </project>

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-aws-bundle</artifactId>
<version>0.1.0-incubating-SNAPSHOT</version>
</parent>
<artifactId>nifi-aws-nar</artifactId>
<packaging>nar</packaging>
<dependencies>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-aws-processors</artifactId>
<version>0.1.0-incubating-SNAPSHOT</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-aws-bundle</artifactId>
<version>0.1.0-incubating-SNAPSHOT</version>
</parent>
<artifactId>nifi-aws-processors</artifactId>
<packaging>jar</packaging>
<dependencies>
<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>com.amazonaws</groupId>
<artifactId>aws-java-sdk</artifactId>
</dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-mock</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,195 @@
/*
* 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.aws;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.apache.nifi.annotation.lifecycle.OnScheduled;
import org.apache.nifi.components.AllowableValue;
import org.apache.nifi.components.PropertyDescriptor;
import org.apache.nifi.components.ValidationContext;
import org.apache.nifi.components.ValidationResult;
import org.apache.nifi.processor.AbstractProcessor;
import org.apache.nifi.processor.ProcessContext;
import org.apache.nifi.processor.Relationship;
import org.apache.nifi.processor.exception.ProcessException;
import org.apache.nifi.processor.util.StandardValidators;
import com.amazonaws.AmazonWebServiceClient;
import com.amazonaws.ClientConfiguration;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AnonymousAWSCredentials;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.auth.PropertiesCredentials;
import com.amazonaws.regions.Region;
import com.amazonaws.regions.Regions;
public abstract class AbstractAWSProcessor<ClientType extends AmazonWebServiceClient> extends AbstractProcessor {
public static final Relationship REL_SUCCESS = new Relationship.Builder().name("success")
.description("FlowFiles are routed to success after being successfully copied to Amazon S3").build();
public static final Relationship REL_FAILURE = new Relationship.Builder().name("failure")
.description("FlowFiles are routed to failure if unable to be copied to Amazon S3").build();
public static final Set<Relationship> relationships = Collections.unmodifiableSet(
new HashSet<>(Arrays.asList(REL_SUCCESS, REL_FAILURE)));
public static final PropertyDescriptor CREDENTAILS_FILE = new PropertyDescriptor.Builder()
.name("Credentials File")
.expressionLanguageSupported(false)
.required(false)
.addValidator(StandardValidators.FILE_EXISTS_VALIDATOR)
.build();
public static final PropertyDescriptor ACCESS_KEY = new PropertyDescriptor.Builder()
.name("Access Key")
.expressionLanguageSupported(false)
.required(false)
.addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
.sensitive(true)
.build();
public static final PropertyDescriptor SECRET_KEY = new PropertyDescriptor.Builder()
.name("Secret Key")
.expressionLanguageSupported(false)
.required(false)
.addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
.sensitive(true)
.build();
public static final PropertyDescriptor REGION = new PropertyDescriptor.Builder()
.name("Region")
.required(true)
.allowableValues(getAvailableRegions())
.defaultValue(createAllowableValue(Regions.DEFAULT_REGION).getValue())
.build();
public static final PropertyDescriptor TIMEOUT = new PropertyDescriptor.Builder()
.name("Communications Timeout")
.required(true)
.addValidator(StandardValidators.TIME_PERIOD_VALIDATOR)
.defaultValue("30 secs")
.build();
private volatile ClientType client;
private static AllowableValue createAllowableValue(final Regions regions) {
return new AllowableValue(regions.getName(), regions.getName(), regions.getName());
}
private static AllowableValue[] getAvailableRegions() {
final List<AllowableValue> values = new ArrayList<>();
for ( final Regions regions : Regions.values() ) {
values.add(createAllowableValue(regions));
}
return (AllowableValue[]) values.toArray(new AllowableValue[values.size()]);
}
@Override
public Set<Relationship> getRelationships() {
return relationships;
}
@Override
protected Collection<ValidationResult> customValidate(final ValidationContext validationContext) {
final List<ValidationResult> problems = new ArrayList<>(super.customValidate(validationContext));
final boolean accessKeySet = validationContext.getProperty(ACCESS_KEY).isSet();
final boolean secretKeySet = validationContext.getProperty(SECRET_KEY).isSet();
if ( (accessKeySet && !secretKeySet) || (secretKeySet && !accessKeySet) ) {
problems.add(new ValidationResult.Builder().input("Access Key").valid(false).explanation("If setting Secret Key or Access Key, must set both").build());
}
final boolean credentialsFileSet = validationContext.getProperty(CREDENTAILS_FILE).isSet();
if ( (secretKeySet || accessKeySet) && credentialsFileSet ) {
problems.add(new ValidationResult.Builder().input("Access Key").valid(false).explanation("Cannot set both Credentials File and Secret Key/Access Key").build());
}
return problems;
}
protected ClientConfiguration createConfiguration(final ProcessContext context) {
final ClientConfiguration config = new ClientConfiguration();
config.setMaxConnections(context.getMaxConcurrentTasks());
config.setMaxErrorRetry(0);
config.setUserAgent("NiFi");
final int commsTimeout = context.getProperty(TIMEOUT).asTimePeriod(TimeUnit.MILLISECONDS).intValue();
config.setConnectionTimeout(commsTimeout);
config.setSocketTimeout(commsTimeout);
return config;
}
@OnScheduled
public void onScheduled(final ProcessContext context) {
final ClientType awsClient = createClient(context, getCredentials(context), createConfiguration(context));
this.client = awsClient;
// if the processor supports REGION, get the configured region.
if ( getSupportedPropertyDescriptors().contains(REGION) ) {
final String region = context.getProperty(REGION).getValue();
if ( region != null ) {
client.setRegion(Region.getRegion(Regions.fromName(region)));
}
}
}
protected abstract ClientType createClient(final ProcessContext context, final AWSCredentials credentials, final ClientConfiguration config);
protected ClientType getClient() {
return client;
}
protected AWSCredentials getCredentials(final ProcessContext context) {
final String accessKey = context.getProperty(ACCESS_KEY).getValue();
final String secretKey = context.getProperty(SECRET_KEY).getValue();
final String credentialsFile = context.getProperty(CREDENTAILS_FILE).getValue();
if ( credentialsFile != null ) {
try {
return new PropertiesCredentials(new File(credentialsFile));
} catch (final IOException ioe) {
throw new ProcessException("Could not read Credentials File", ioe);
}
}
if ( accessKey != null && secretKey != null ) {
return new BasicAWSCredentials(accessKey, secretKey);
}
return new AnonymousAWSCredentials();
}
protected boolean isEmpty(final String value) {
return value == null || value.trim().equals("");
}
}

View File

@ -0,0 +1,171 @@
/*
* 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.aws.s3;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.nifi.components.PropertyDescriptor;
import org.apache.nifi.flowfile.FlowFile;
import org.apache.nifi.processor.ProcessContext;
import org.apache.nifi.processor.util.StandardValidators;
import org.apache.nifi.processors.aws.AbstractAWSProcessor;
import com.amazonaws.ClientConfiguration;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.AccessControlList;
import com.amazonaws.services.s3.model.CanonicalGrantee;
import com.amazonaws.services.s3.model.EmailAddressGrantee;
import com.amazonaws.services.s3.model.Grantee;
import com.amazonaws.services.s3.model.Owner;
import com.amazonaws.services.s3.model.Permission;
public abstract class AbstractS3Processor extends AbstractAWSProcessor<AmazonS3Client> {
public static final PropertyDescriptor FULL_CONTROL_USER_LIST = new PropertyDescriptor.Builder()
.name("FullControl User List")
.required(false)
.expressionLanguageSupported(true)
.addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
.description("A comma-separated list of Amazon User ID's or E-mail addresses that specifies who should have Full Control for an object")
.defaultValue("${s3.permissions.full.users}")
.build();
public static final PropertyDescriptor READ_USER_LIST = new PropertyDescriptor.Builder()
.name("Read Permission User List")
.required(false)
.expressionLanguageSupported(true)
.addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
.description("A comma-separated list of Amazon User ID's or E-mail addresses that specifies who should have Read Access for an object")
.defaultValue("${s3.permissions.read.users}")
.build();
public static final PropertyDescriptor WRITE_USER_LIST = new PropertyDescriptor.Builder()
.name("Write Permission User List")
.required(false)
.expressionLanguageSupported(true)
.addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
.description("A comma-separated list of Amazon User ID's or E-mail addresses that specifies who should have Write Access for an object")
.defaultValue("${s3.permissions.write.users}")
.build();
public static final PropertyDescriptor READ_ACL_LIST = new PropertyDescriptor.Builder()
.name("Read ACL User List")
.required(false)
.expressionLanguageSupported(true)
.addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
.description("A comma-separated list of Amazon User ID's or E-mail addresses that specifies who should have permissions to read the Access Control List for an object")
.defaultValue("${s3.permissions.readacl.users}")
.build();
public static final PropertyDescriptor WRITE_ACL_LIST = new PropertyDescriptor.Builder()
.name("Write ACL User List")
.required(false)
.expressionLanguageSupported(true)
.addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
.description("A comma-separated list of Amazon User ID's or E-mail addresses that specifies who should have permissions to change the Access Control List for an object")
.defaultValue("${s3.permissions.writeacl.users}")
.build();
public static final PropertyDescriptor OWNER = new PropertyDescriptor.Builder()
.name("Owner")
.required(false)
.expressionLanguageSupported(true)
.addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
.description("The Amazon ID to use for the object's owner")
.defaultValue("${s3.owner}")
.build();
public static final PropertyDescriptor BUCKET = new PropertyDescriptor.Builder()
.name("Bucket")
.expressionLanguageSupported(true)
.required(true)
.addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
.build();
public static final PropertyDescriptor KEY = new PropertyDescriptor.Builder()
.name("Object Key")
.required(true)
.addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
.expressionLanguageSupported(true)
.defaultValue("${filename}")
.build();
@Override
protected AmazonS3Client createClient(final ProcessContext context, final AWSCredentials credentials, final ClientConfiguration config) {
return new AmazonS3Client(credentials, config);
}
protected Grantee createGrantee(final String value) {
if ( isEmpty(value) ) {
return null;
}
if ( value.contains("@") ) {
return new EmailAddressGrantee(value);
} else {
return new CanonicalGrantee(value);
}
}
protected final List<Grantee> createGrantees(final String value) {
if ( isEmpty(value) ) {
return Collections.emptyList();
}
final List<Grantee> grantees = new ArrayList<>();
final String[] vals = value.split(",");
for ( final String val : vals ) {
final String identifier = val.trim();
final Grantee grantee = createGrantee(identifier);
if ( grantee != null ) {
grantees.add(grantee);
}
}
return grantees;
}
protected final AccessControlList createACL(final ProcessContext context, final FlowFile flowFile) {
final AccessControlList acl = new AccessControlList();
final String ownerId = context.getProperty(OWNER).evaluateAttributeExpressions(flowFile).getValue();
if ( !isEmpty(ownerId) ) {
final Owner owner = new Owner();
owner.setId(ownerId);
acl.setOwner(owner);
}
for ( final Grantee grantee : createGrantees(context.getProperty(FULL_CONTROL_USER_LIST).evaluateAttributeExpressions(flowFile).getValue())) {
acl.grantPermission(grantee, Permission.FullControl);
}
for ( final Grantee grantee : createGrantees(context.getProperty(READ_USER_LIST).evaluateAttributeExpressions(flowFile).getValue())) {
acl.grantPermission(grantee, Permission.Read);
}
for ( final Grantee grantee : createGrantees(context.getProperty(WRITE_USER_LIST).evaluateAttributeExpressions(flowFile).getValue())) {
acl.grantPermission(grantee, Permission.Write);
}
for ( final Grantee grantee : createGrantees(context.getProperty(READ_ACL_LIST).evaluateAttributeExpressions(flowFile).getValue())) {
acl.grantPermission(grantee, Permission.ReadAcp);
}
for ( final Grantee grantee : createGrantees(context.getProperty(WRITE_ACL_LIST).evaluateAttributeExpressions(flowFile).getValue())) {
acl.grantPermission(grantee, Permission.WriteAcp);
}
return acl;
}
}

View File

@ -0,0 +1,157 @@
/*
* 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.aws.s3;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.apache.nifi.annotation.behavior.SupportsBatching;
import org.apache.nifi.annotation.behavior.WritesAttribute;
import org.apache.nifi.annotation.behavior.WritesAttributes;
import org.apache.nifi.annotation.documentation.CapabilityDescription;
import org.apache.nifi.annotation.documentation.SeeAlso;
import org.apache.nifi.annotation.documentation.Tags;
import org.apache.nifi.components.PropertyDescriptor;
import org.apache.nifi.flowfile.FlowFile;
import org.apache.nifi.flowfile.attributes.CoreAttributes;
import org.apache.nifi.processor.ProcessContext;
import org.apache.nifi.processor.ProcessSession;
import org.apache.nifi.processor.util.StandardValidators;
import com.amazonaws.AmazonClientException;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.GetObjectRequest;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.S3Object;
@SupportsBatching
@SeeAlso({PutS3Object.class})
@Tags({"Amazon", "S3", "AWS", "Get", "Fetch"})
@CapabilityDescription("Retrieves the contents of an S3 Object and writes it to the content of a FlowFile")
@WritesAttributes({
@WritesAttribute(attribute="s3.bucket", description="The name of the S3 bucket"),
@WritesAttribute(attribute="path", description="The path of the file"),
@WritesAttribute(attribute="absolute.path", description="The path of the file"),
@WritesAttribute(attribute="filename", description="The name of the file"),
@WritesAttribute(attribute="hash.value", description="The MD5 sum of the file"),
@WritesAttribute(attribute="hash.algorithm", description="MD5"),
@WritesAttribute(attribute="mime.type", description="If S3 provides the content type/MIME type, this attribute will hold that file"),
@WritesAttribute(attribute="s3.etag", description="The ETag that can be used to see if the file has changed"),
@WritesAttribute(attribute="s3.expirationTime", description="If the file has an expiration date, this attribute will be set, containing the milliseconds since epoch in UTC time"),
@WritesAttribute(attribute="s3.expirationTimeRuleId", description="The ID of the rule that dictates this object's expiration time"),
@WritesAttribute(attribute="s3.version", description="The version of the S3 object"),
})
public class FetchS3Object extends AbstractS3Processor {
public static final PropertyDescriptor VERSION_ID = new PropertyDescriptor.Builder()
.name("Version")
.description("The Version of the Object to download")
.addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
.expressionLanguageSupported(true)
.required(false)
.build();
public static final List<PropertyDescriptor> properties = Collections.unmodifiableList(
Arrays.asList(BUCKET, KEY, REGION, ACCESS_KEY, SECRET_KEY, CREDENTAILS_FILE, TIMEOUT, VERSION_ID) );
@Override
protected List<PropertyDescriptor> getSupportedPropertyDescriptors() {
return properties;
}
@Override
public void onTrigger(final ProcessContext context, final ProcessSession session) {
FlowFile flowFile = session.get();
if ( flowFile == null ) {
return;
}
final long startNanos = System.nanoTime();
final String bucket = context.getProperty(BUCKET).evaluateAttributeExpressions(flowFile).getValue();
final String key = context.getProperty(KEY).evaluateAttributeExpressions(flowFile).getValue();
final String versionId = context.getProperty(VERSION_ID).evaluateAttributeExpressions(flowFile).getValue();
final AmazonS3 client = getClient();
final GetObjectRequest request;
if ( versionId == null ) {
request = new GetObjectRequest(bucket, key);
} else {
request = new GetObjectRequest(bucket, key, versionId);
}
final Map<String, String> attributes = new HashMap<>();
try (final S3Object s3Object = client.getObject(request)) {
flowFile = session.importFrom(s3Object.getObjectContent(), flowFile);
attributes.put("s3.bucket", s3Object.getBucketName());
final ObjectMetadata metadata = s3Object.getObjectMetadata();
if ( metadata.getContentDisposition() != null ) {
final String fullyQualified = metadata.getContentDisposition();
final int lastSlash = fullyQualified.lastIndexOf("/");
if ( lastSlash > -1 && lastSlash < fullyQualified.length() - 1 ) {
attributes.put(CoreAttributes.PATH.key(), fullyQualified.substring(0, lastSlash));
attributes.put(CoreAttributes.ABSOLUTE_PATH.key(), fullyQualified);
attributes.put(CoreAttributes.FILENAME.key(), fullyQualified.substring(lastSlash + 1));
} else {
attributes.put(CoreAttributes.FILENAME.key(), metadata.getContentDisposition());
}
}
if (metadata.getContentMD5() != null ) {
attributes.put("hash.value", metadata.getContentMD5());
attributes.put("hash.algorithm", "MD5");
}
if ( metadata.getContentType() != null ) {
attributes.put(CoreAttributes.MIME_TYPE.key(), metadata.getContentType());
}
if ( metadata.getETag() != null ) {
attributes.put("s3.etag", metadata.getETag());
}
if ( metadata.getExpirationTime() != null ) {
attributes.put("s3.expirationTime", String.valueOf(metadata.getExpirationTime().getTime()));
}
if ( metadata.getExpirationTimeRuleId() != null ) {
attributes.put("s3.expirationTimeRuleId", metadata.getExpirationTimeRuleId());
}
if ( metadata.getUserMetadata() != null ) {
attributes.putAll(metadata.getUserMetadata());
}
if ( metadata.getVersionId() != null ) {
attributes.put("s3.version", metadata.getVersionId());
}
} catch (final IOException | AmazonClientException ioe) {
getLogger().error("Failed to retrieve S3 Object for {}; routing to failure", new Object[] {flowFile, ioe});
session.transfer(flowFile, REL_FAILURE);
return;
}
if ( !attributes.isEmpty() ) {
flowFile = session.putAllAttributes(flowFile, attributes);
}
session.transfer(flowFile, REL_SUCCESS);
final long transferMillis = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanos);
getLogger().info("Successfully retrieved S3 Object for {} in {} millis; routing to success", new Object[] {flowFile, transferMillis});
session.getProvenanceReporter().receive(flowFile, "http://" + bucket + ".amazonaws.com/" + key, transferMillis);
}
}

View File

@ -0,0 +1,181 @@
/*
* 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.aws.s3;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.apache.nifi.annotation.behavior.DynamicProperty;
import org.apache.nifi.annotation.behavior.ReadsAttribute;
import org.apache.nifi.annotation.behavior.SupportsBatching;
import org.apache.nifi.annotation.behavior.WritesAttribute;
import org.apache.nifi.annotation.behavior.WritesAttributes;
import org.apache.nifi.annotation.documentation.CapabilityDescription;
import org.apache.nifi.annotation.documentation.SeeAlso;
import org.apache.nifi.annotation.documentation.Tags;
import org.apache.nifi.components.PropertyDescriptor;
import org.apache.nifi.flowfile.FlowFile;
import org.apache.nifi.flowfile.attributes.CoreAttributes;
import org.apache.nifi.processor.ProcessContext;
import org.apache.nifi.processor.ProcessSession;
import org.apache.nifi.processor.exception.ProcessException;
import org.apache.nifi.processor.io.InputStreamCallback;
import org.apache.nifi.processor.util.StandardValidators;
import com.amazonaws.AmazonClientException;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.AccessControlList;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.amazonaws.services.s3.model.PutObjectResult;
import com.amazonaws.services.s3.model.StorageClass;
@SupportsBatching
@SeeAlso({FetchS3Object.class})
@Tags({"Amazon", "S3", "AWS", "Archive", "Put"})
@CapabilityDescription("Puts FlowFiles to an Amazon S3 Bucket")
@DynamicProperty(name="The name of a User-Defined Metadata field to add to the S3 Object",
value="The value of a User-Defined Metadata field to add to the S3 Object",
description="Allows user-defined metadata to be added to the S3 object as key/value pairs",
supportsExpressionLanguage=true)
@ReadsAttribute(attribute="filename", description="Uses the FlowFile's filename as the filename for the S3 object")
@WritesAttributes({
@WritesAttribute(attribute="s3.version", description="The version of the S3 Object that was put to S3"),
@WritesAttribute(attribute="s3.etag", description="The ETag of the S3 Object"),
@WritesAttribute(attribute="s3.expiration", description="A human-readable form of the expiration date of the S3 object, if one is set")
})
public class PutS3Object extends AbstractS3Processor {
public static final PropertyDescriptor EXPIRATION_RULE_ID = new PropertyDescriptor.Builder()
.name("Expiration Time Rule")
.required(false)
.expressionLanguageSupported(true)
.addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
.build();
public static final PropertyDescriptor STORAGE_CLASS = new PropertyDescriptor.Builder()
.name("Storage Class")
.required(true)
.allowableValues(StorageClass.Standard.name(), StorageClass.ReducedRedundancy.name())
.defaultValue(StorageClass.Standard.name())
.build();
public static final List<PropertyDescriptor> properties = Collections.unmodifiableList(
Arrays.asList(KEY, BUCKET, ACCESS_KEY, SECRET_KEY, CREDENTAILS_FILE, STORAGE_CLASS, REGION, TIMEOUT, EXPIRATION_RULE_ID,
FULL_CONTROL_USER_LIST, READ_USER_LIST, WRITE_USER_LIST, READ_ACL_LIST, WRITE_ACL_LIST, OWNER) );
@Override
protected List<PropertyDescriptor> getSupportedPropertyDescriptors() {
return properties;
}
@Override
protected PropertyDescriptor getSupportedDynamicPropertyDescriptor(final String propertyDescriptorName) {
return new PropertyDescriptor.Builder()
.name(propertyDescriptorName)
.addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
.expressionLanguageSupported(true)
.dynamic(true)
.build();
}
public void onTrigger(final ProcessContext context, final ProcessSession session) {
FlowFile flowFile = session.get();
if ( flowFile == null ) {
return;
}
final long startNanos = System.nanoTime();
final String bucket = context.getProperty(BUCKET).evaluateAttributeExpressions(flowFile).getValue();
final String key = context.getProperty(KEY).evaluateAttributeExpressions(flowFile).getValue();
final AmazonS3 s3 = getClient();
final FlowFile ff = flowFile;
final Map<String, String> attributes = new HashMap<>();
try {
session.read(flowFile, new InputStreamCallback() {
@Override
public void process(final InputStream rawIn) throws IOException {
try (final InputStream in = new BufferedInputStream(rawIn)) {
final ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentDisposition(ff.getAttribute(CoreAttributes.FILENAME.key()));
objectMetadata.setContentLength(ff.getSize());
final String expirationRule = context.getProperty(EXPIRATION_RULE_ID).evaluateAttributeExpressions(ff).getValue();
if ( expirationRule != null ) {
objectMetadata.setExpirationTimeRuleId(expirationRule);
}
final Map<String, String> userMetadata = new HashMap<>();
for ( final Map.Entry<PropertyDescriptor, String> entry : context.getProperties().entrySet() ) {
if ( entry.getKey().isDynamic() ) {
final String value = context.getProperty(entry.getKey()).evaluateAttributeExpressions(ff).getValue();
userMetadata.put(entry.getKey().getName(), value);
}
}
if ( !userMetadata.isEmpty() ) {
objectMetadata.setUserMetadata(userMetadata);
}
final PutObjectRequest request = new PutObjectRequest(bucket, key, in, objectMetadata);
request.setStorageClass(StorageClass.valueOf(context.getProperty(STORAGE_CLASS).getValue()));
final AccessControlList acl = createACL(context, ff);
if ( acl != null ) {
request.setAccessControlList(acl);
}
final PutObjectResult result = s3.putObject(request);
if ( result.getVersionId() != null ) {
attributes.put("s3.version", result.getVersionId());
}
attributes.put("s3.etag", result.getETag());
final Date expiration = result.getExpirationTime();
if ( expiration != null ) {
attributes.put("s3.expiration", expiration.toString());
}
}
}
});
if ( !attributes.isEmpty() ) {
flowFile = session.putAllAttributes(flowFile, attributes);
}
session.transfer(flowFile, REL_SUCCESS);
final String url = "http://" + bucket + ".s3.amazonaws.com/" + key;
final long millis = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanos);
session.getProvenanceReporter().send(flowFile, url, millis);
getLogger().info("Successfully put {} to Amazon S3 in {} milliseconds", new Object[] {ff, millis});
} catch (final ProcessException | AmazonClientException pe) {
getLogger().error("Failed to put {} to Amazon S3 due to {}", new Object[] {flowFile, pe});
session.transfer(flowFile, REL_FAILURE);
}
}
}

View File

@ -0,0 +1,60 @@
/*
* 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.aws.sns;
import org.apache.nifi.components.AllowableValue;
import org.apache.nifi.components.PropertyDescriptor;
import org.apache.nifi.processor.ProcessContext;
import org.apache.nifi.processor.util.StandardValidators;
import org.apache.nifi.processors.aws.AbstractAWSProcessor;
import com.amazonaws.ClientConfiguration;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.services.sns.AmazonSNSClient;
public abstract class AbstractSNSProcessor extends AbstractAWSProcessor<AmazonSNSClient> {
protected static final AllowableValue ARN_TYPE_TOPIC =
new AllowableValue("Topic ARN", "Topic ARN", "The ARN is the name of a topic");
protected static final AllowableValue ARN_TYPE_TARGET =
new AllowableValue("Target ARN", "Target ARN", "The ARN is the name of a particular Target, used to notify a specific subscriber");
public static final PropertyDescriptor ARN = new PropertyDescriptor.Builder()
.name("Amazon Resource Name (ARN)")
.description("The name of the resource to which notifications should be published")
.expressionLanguageSupported(true)
.required(true)
.addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
.build();
public static final PropertyDescriptor ARN_TYPE = new PropertyDescriptor.Builder()
.name("ARN Type")
.description("The type of Amazon Resource Name that is being used.")
.expressionLanguageSupported(false)
.required(true)
.allowableValues(ARN_TYPE_TOPIC, ARN_TYPE_TARGET)
.defaultValue(ARN_TYPE_TOPIC.getValue())
.build();
@Override
protected AmazonSNSClient createClient(final ProcessContext context, final AWSCredentials credentials, final ClientConfiguration config) {
return new AmazonSNSClient(credentials, config);
}
}

View File

@ -0,0 +1,155 @@
/*
* 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.aws.sns;
import java.io.ByteArrayOutputStream;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.apache.nifi.annotation.behavior.SupportsBatching;
import org.apache.nifi.annotation.documentation.CapabilityDescription;
import org.apache.nifi.annotation.documentation.SeeAlso;
import org.apache.nifi.annotation.documentation.Tags;
import org.apache.nifi.components.PropertyDescriptor;
import org.apache.nifi.flowfile.FlowFile;
import org.apache.nifi.processor.ProcessContext;
import org.apache.nifi.processor.ProcessSession;
import org.apache.nifi.processor.util.StandardValidators;
import org.apache.nifi.processors.aws.sqs.GetSQS;
import org.apache.nifi.processors.aws.sqs.PutSQS;
import com.amazonaws.services.sns.AmazonSNSClient;
import com.amazonaws.services.sns.model.MessageAttributeValue;
import com.amazonaws.services.sns.model.PublishRequest;
@SupportsBatching
@SeeAlso({GetSQS.class, PutSQS.class})
@Tags({"amazon", "aws", "sns", "topic", "put", "publish", "pubsub"})
@CapabilityDescription("Sends the content of a FlowFile as a notification to the Amazon Simple Notification Service")
public class PutSNS extends AbstractSNSProcessor {
public static final PropertyDescriptor CHARACTER_ENCODING = new PropertyDescriptor.Builder()
.name("Character Set")
.description("The character set in which the FlowFile's content is encoded")
.defaultValue("UTF-8")
.expressionLanguageSupported(true)
.addValidator(StandardValidators.CHARACTER_SET_VALIDATOR)
.required(true)
.build();
public static final PropertyDescriptor USE_JSON_STRUCTURE = new PropertyDescriptor.Builder()
.name("Use JSON Structure")
.description("If true, the contents of the FlowFile must be JSON with a top-level element named 'default'. Additional elements can be used to send different messages to different protocols. See the Amazon SNS Documentation for more information.")
.defaultValue("false")
.allowableValues("true", "false")
.required(true)
.build();
public static final PropertyDescriptor SUBJECT = new PropertyDescriptor.Builder()
.name("E-mail Subject")
.description("The optional subject to use for any subscribers that are subscribed via E-mail")
.expressionLanguageSupported(true)
.required(false)
.addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
.build();
public static final List<PropertyDescriptor> properties = Collections.unmodifiableList(
Arrays.asList(ARN, ARN_TYPE, SUBJECT, REGION, ACCESS_KEY, SECRET_KEY, CREDENTAILS_FILE, TIMEOUT,
USE_JSON_STRUCTURE, CHARACTER_ENCODING) );
public static final int MAX_SIZE = 256 * 1024;
@Override
protected List<PropertyDescriptor> getSupportedPropertyDescriptors() {
return properties;
}
@Override
protected PropertyDescriptor getSupportedDynamicPropertyDescriptor(final String propertyDescriptorName) {
return new PropertyDescriptor.Builder()
.name(propertyDescriptorName)
.addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
.expressionLanguageSupported(true)
.required(false)
.dynamic(true)
.build();
}
@Override
public void onTrigger(final ProcessContext context, final ProcessSession session) {
FlowFile flowFile = session.get();
if ( flowFile == null ) {
return;
}
if ( flowFile.getSize() > MAX_SIZE ) {
getLogger().error("Cannot publish {} to SNS because its size exceeds Amazon SNS's limit of 256KB; routing to failure", new Object[] {flowFile});
session.transfer(flowFile, REL_FAILURE);
return;
}
final Charset charset = Charset.forName(context.getProperty(CHARACTER_ENCODING).evaluateAttributeExpressions(flowFile).getValue());
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
session.exportTo(flowFile, baos);
final String message = new String(baos.toByteArray(), charset);
final AmazonSNSClient client = getClient();
final PublishRequest request = new PublishRequest();
request.setMessage(message);
if ( context.getProperty(USE_JSON_STRUCTURE).asBoolean() ) {
request.setMessageStructure("json");
}
final String arn = context.getProperty(ARN).evaluateAttributeExpressions(flowFile).getValue();
final String arnType = context.getProperty(ARN_TYPE).getValue();
if ( arnType.equalsIgnoreCase(ARN_TYPE_TOPIC.getValue()) ) {
request.setTopicArn(arn);
} else {
request.setTargetArn(arn);
}
final String subject = context.getProperty(SUBJECT).evaluateAttributeExpressions(flowFile).getValue();
if ( subject != null ) {
request.setSubject(subject);
}
for ( final Map.Entry<PropertyDescriptor, String> entry : context.getProperties().entrySet() ) {
if ( entry.getKey().isDynamic() && !isEmpty(entry.getValue()) ) {
final MessageAttributeValue value = new MessageAttributeValue();
value.setStringValue(context.getProperty(entry.getKey()).evaluateAttributeExpressions(flowFile).getValue());
value.setDataType("String");
request.addMessageAttributesEntry(entry.getKey().getName(), value);
}
}
try {
client.publish(request);
session.transfer(flowFile, REL_SUCCESS);
session.getProvenanceReporter().send(flowFile, arn);
getLogger().info("Successfully published notification for {}", new Object[] {flowFile});
} catch (final Exception e) {
getLogger().error("Failed to publish Amazon SNS message for {} due to {}", new Object[] {flowFile, e});
session.transfer(flowFile, REL_FAILURE);
return;
}
}
}

View File

@ -0,0 +1,51 @@
/*
* 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.aws.sqs;
import org.apache.nifi.components.PropertyDescriptor;
import org.apache.nifi.processor.ProcessContext;
import org.apache.nifi.processor.util.StandardValidators;
import org.apache.nifi.processors.aws.AbstractAWSProcessor;
import com.amazonaws.ClientConfiguration;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.services.sqs.AmazonSQSClient;
public abstract class AbstractSQSProcessor extends AbstractAWSProcessor<AmazonSQSClient> {
public static final PropertyDescriptor BATCH_SIZE = new PropertyDescriptor.Builder()
.name("Batch Size")
.description("The maximum number of messages to send in a single network request")
.required(true)
.addValidator(StandardValidators.POSITIVE_INTEGER_VALIDATOR)
.defaultValue("25")
.build();
public static final PropertyDescriptor QUEUE_URL = new PropertyDescriptor.Builder()
.name("Queue URL")
.description("The URL of the queue to act upon")
.addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
.expressionLanguageSupported(true)
.required(true)
.build();
@Override
protected AmazonSQSClient createClient(final ProcessContext context, final AWSCredentials credentials, final ClientConfiguration config) {
return new AmazonSQSClient(credentials, config);
}
}

View File

@ -0,0 +1,95 @@
/*
* 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.aws.sqs;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.apache.nifi.annotation.behavior.SupportsBatching;
import org.apache.nifi.annotation.documentation.CapabilityDescription;
import org.apache.nifi.annotation.documentation.SeeAlso;
import org.apache.nifi.annotation.documentation.Tags;
import org.apache.nifi.components.PropertyDescriptor;
import org.apache.nifi.flowfile.FlowFile;
import org.apache.nifi.processor.ProcessContext;
import org.apache.nifi.processor.ProcessSession;
import org.apache.nifi.processor.util.StandardValidators;
import com.amazonaws.services.sqs.AmazonSQSClient;
import com.amazonaws.services.sqs.model.DeleteMessageBatchRequest;
import com.amazonaws.services.sqs.model.DeleteMessageBatchRequestEntry;
@SupportsBatching
@SeeAlso({GetSQS.class, PutSQS.class})
@Tags({"Amazon", "AWS", "SQS", "Queue", "Delete"})
@CapabilityDescription("Deletes a message from an Amazon Simple Queuing Service Queue")
public class DeleteSQS extends AbstractSQSProcessor {
public static final PropertyDescriptor RECEIPT_HANDLE = new PropertyDescriptor.Builder()
.name("Receipt Handle")
.description("The identifier that specifies the receipt of the message")
.expressionLanguageSupported(true)
.required(true)
.addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
.defaultValue("${sqs.receipt.handle}")
.build();
public static final List<PropertyDescriptor> properties = Collections.unmodifiableList(
Arrays.asList(ACCESS_KEY, SECRET_KEY, REGION, QUEUE_URL, TIMEOUT) );
@Override
protected List<PropertyDescriptor> getSupportedPropertyDescriptors() {
return properties;
}
@Override
public void onTrigger(final ProcessContext context, final ProcessSession session) {
List<FlowFile> flowFiles = session.get(1);
if ( flowFiles.isEmpty() ) {
return;
}
final FlowFile firstFlowFile = flowFiles.get(0);
final String queueUrl = context.getProperty(QUEUE_URL).evaluateAttributeExpressions(firstFlowFile).getValue();
final AmazonSQSClient client = getClient();
final DeleteMessageBatchRequest request = new DeleteMessageBatchRequest();
request.setQueueUrl(queueUrl);
final List<DeleteMessageBatchRequestEntry> entries = new ArrayList<>(flowFiles.size());
for ( final FlowFile flowFile : flowFiles ) {
final DeleteMessageBatchRequestEntry entry = new DeleteMessageBatchRequestEntry();
entry.setReceiptHandle(context.getProperty(RECEIPT_HANDLE).evaluateAttributeExpressions(flowFile).getValue());
entries.add(entry);
}
request.setEntries(entries);
try {
client.deleteMessageBatch(request);
getLogger().info("Successfully deleted {} objects from SQS", new Object[] {flowFiles.size()});
session.transfer(flowFiles, REL_SUCCESS);
} catch (final Exception e) {
getLogger().error("Failed to delete {} objects from SQS due to {}", new Object[] {flowFiles.size(), e});
session.transfer(flowFiles, REL_FAILURE);
}
}
}

View File

@ -0,0 +1,204 @@
/*
* 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.aws.sqs;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.apache.nifi.annotation.behavior.SupportsBatching;
import org.apache.nifi.annotation.behavior.WritesAttribute;
import org.apache.nifi.annotation.behavior.WritesAttributes;
import org.apache.nifi.annotation.documentation.CapabilityDescription;
import org.apache.nifi.annotation.documentation.SeeAlso;
import org.apache.nifi.annotation.documentation.Tags;
import org.apache.nifi.components.PropertyDescriptor;
import org.apache.nifi.flowfile.FlowFile;
import org.apache.nifi.processor.ProcessContext;
import org.apache.nifi.processor.ProcessSession;
import org.apache.nifi.processor.Relationship;
import org.apache.nifi.processor.io.OutputStreamCallback;
import org.apache.nifi.processor.util.StandardValidators;
import com.amazonaws.services.sqs.AmazonSQSClient;
import com.amazonaws.services.sqs.model.DeleteMessageBatchRequest;
import com.amazonaws.services.sqs.model.DeleteMessageBatchRequestEntry;
import com.amazonaws.services.sqs.model.Message;
import com.amazonaws.services.sqs.model.MessageAttributeValue;
import com.amazonaws.services.sqs.model.ReceiveMessageRequest;
import com.amazonaws.services.sqs.model.ReceiveMessageResult;
@SupportsBatching
@Tags({ "Amazon", "AWS", "SQS", "Queue", "Get", "Fetch", "Poll"})
@SeeAlso({PutSQS.class, DeleteSQS.class})
@CapabilityDescription("Fetches messages from an Amazon Simple Queuing Service Queue")
@WritesAttributes({
@WritesAttribute(attribute="hash.value", description="The MD5 sum of the message"),
@WritesAttribute(attribute="hash.algorithm", description="MD5"),
@WritesAttribute(attribute="sqs.message.id", description="The unique identifier of the SQS message"),
@WritesAttribute(attribute="sqs.receipt.handle", description="The SQS Receipt Handle that is to be used to delete the message from the queue")
})
public class GetSQS extends AbstractSQSProcessor {
public static final PropertyDescriptor CHARSET = new PropertyDescriptor.Builder()
.name("Character Set")
.description("The Character Set that should be used to encode the textual content of the SQS message")
.required(true)
.defaultValue("UTF-8")
.allowableValues(Charset.availableCharsets().keySet().toArray(new String[0]))
.build();
public static final PropertyDescriptor AUTO_DELETE = new PropertyDescriptor.Builder()
.name("Auto Delete Messages")
.description("Specifies whether the messages should be automatically deleted by the processors once they have been received.")
.required(true)
.allowableValues("true", "false")
.defaultValue("true")
.build();
public static final PropertyDescriptor VISIBILITY_TIMEOUT = new PropertyDescriptor.Builder()
.name("Visibility Timeout")
.description("The amount of time after a message is received but not deleted that the message is hidden from other consumers")
.expressionLanguageSupported(false)
.required(true)
.defaultValue("15 mins")
.addValidator(StandardValidators.TIME_PERIOD_VALIDATOR)
.build();
public static final PropertyDescriptor BATCH_SIZE = new PropertyDescriptor.Builder()
.name("Batch Size")
.description("The maximum number of messages to send in a single network request")
.required(true)
.addValidator(StandardValidators.createLongValidator(1L, 10L, true))
.defaultValue("10")
.build();
public static final PropertyDescriptor STATIC_QUEUE_URL = new PropertyDescriptor.Builder()
.fromPropertyDescriptor(QUEUE_URL)
.expressionLanguageSupported(false)
.build();
public static final List<PropertyDescriptor> properties = Collections.unmodifiableList(
Arrays.asList(STATIC_QUEUE_URL, AUTO_DELETE, ACCESS_KEY, SECRET_KEY, CREDENTAILS_FILE, REGION, BATCH_SIZE, TIMEOUT, CHARSET, VISIBILITY_TIMEOUT) );
@Override
protected List<PropertyDescriptor> getSupportedPropertyDescriptors() {
return properties;
}
@Override
public Set<Relationship> getRelationships() {
return Collections.singleton(REL_SUCCESS);
}
@Override
public void onTrigger(final ProcessContext context, final ProcessSession session) {
final String queueUrl = context.getProperty(STATIC_QUEUE_URL).getValue();
final AmazonSQSClient client = getClient();
final ReceiveMessageRequest request = new ReceiveMessageRequest();
request.setAttributeNames(Collections.singleton("All"));
request.setMaxNumberOfMessages(context.getProperty(BATCH_SIZE).asInteger());
request.setVisibilityTimeout(context.getProperty(VISIBILITY_TIMEOUT).asTimePeriod(TimeUnit.SECONDS).intValue());
request.setQueueUrl(queueUrl);
final Charset charset = Charset.forName(context.getProperty(CHARSET).getValue());
final ReceiveMessageResult result;
try {
result = client.receiveMessage(request);
} catch (final Exception e) {
getLogger().error("Failed to receive messages from Amazon SQS due to {}", new Object[] {e});
context.yield();
return;
}
final List<Message> messages = result.getMessages();
if ( messages.isEmpty() ) {
context.yield();
return;
}
final boolean autoDelete = context.getProperty(AUTO_DELETE).asBoolean();
for ( final Message message : messages ) {
FlowFile flowFile = session.create();
final Map<String, String> attributes = new HashMap<>();
for ( final Map.Entry<String, String> entry : message.getAttributes().entrySet() ) {
attributes.put("sqs." + entry.getKey(), entry.getValue());
}
for ( final Map.Entry<String, MessageAttributeValue> entry : message.getMessageAttributes().entrySet() ) {
attributes.put("sqs." + entry.getKey(), entry.getValue().getStringValue());
}
attributes.put("hash.value", message.getMD5OfBody());
attributes.put("hash.algorithm", "md5");
attributes.put("sqs.message.id", message.getMessageId());
attributes.put("sqs.receipt.handle", message.getReceiptHandle());
flowFile = session.putAllAttributes(flowFile, attributes);
flowFile = session.write(flowFile, new OutputStreamCallback() {
@Override
public void process(final OutputStream out) throws IOException {
out.write(message.getBody().getBytes(charset));
}
});
session.transfer(flowFile, REL_SUCCESS);
session.getProvenanceReporter().receive(flowFile, queueUrl);
getLogger().info("Successfully received {} from Amazon SQS", new Object[] {flowFile});
}
if ( autoDelete ) {
// If we want to auto-delete messages, we must fist commit the session to ensure that the data
// is persisted in NiFi's repositories.
session.commit();
final DeleteMessageBatchRequest deleteRequest = new DeleteMessageBatchRequest();
deleteRequest.setQueueUrl(queueUrl);
final List<DeleteMessageBatchRequestEntry> deleteRequestEntries = new ArrayList<>();
for ( final Message message : messages ) {
final DeleteMessageBatchRequestEntry entry = new DeleteMessageBatchRequestEntry();
entry.setId(message.getMessageId());
entry.setReceiptHandle(message.getReceiptHandle());
deleteRequestEntries.add(entry);
}
deleteRequest.setEntries(deleteRequestEntries);
try {
client.deleteMessageBatch(deleteRequest);
} catch (final Exception e) {
getLogger().error("Received {} messages from Amazon SQS but failed to delete the messages; these messages may be duplicated. Reason for deletion failure: {}", new Object[] {messages.size(), e});
}
}
}
}

View File

@ -0,0 +1,147 @@
/*
* 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.aws.sqs;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
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 org.apache.nifi.annotation.behavior.DynamicProperty;
import org.apache.nifi.annotation.behavior.SupportsBatching;
import org.apache.nifi.annotation.documentation.CapabilityDescription;
import org.apache.nifi.annotation.documentation.SeeAlso;
import org.apache.nifi.annotation.documentation.Tags;
import org.apache.nifi.annotation.lifecycle.OnScheduled;
import org.apache.nifi.components.PropertyDescriptor;
import org.apache.nifi.flowfile.FlowFile;
import org.apache.nifi.processor.ProcessContext;
import org.apache.nifi.processor.ProcessSession;
import org.apache.nifi.processor.util.StandardValidators;
import com.amazonaws.services.sqs.AmazonSQSClient;
import com.amazonaws.services.sqs.model.MessageAttributeValue;
import com.amazonaws.services.sqs.model.SendMessageBatchRequest;
import com.amazonaws.services.sqs.model.SendMessageBatchRequestEntry;
@SupportsBatching
@Tags({"Amazon", "AWS", "SQS", "Queue", "Put", "Publish"})
@SeeAlso({GetSQS.class, DeleteSQS.class})
@CapabilityDescription("Publishes a message to an Amazon Simple Queuing Service Queue")
@DynamicProperty(name="The name of a Message Attribute to add to the message", value="The value of the Message Attribute",
description="Allows the user to add key/value pairs as Message Attributes by adding a property whose name will become the name of "
+ "the Message Attribute and value will become the value of the Message Attribute", supportsExpressionLanguage=true)
public class PutSQS extends AbstractSQSProcessor {
public static final PropertyDescriptor DELAY = new PropertyDescriptor.Builder()
.name("Delay")
.description("The amount of time to delay the message before it becomes available to consumers")
.required(true)
.addValidator(StandardValidators.TIME_PERIOD_VALIDATOR)
.defaultValue("0 secs")
.build();
public static final List<PropertyDescriptor> properties = Collections.unmodifiableList(
Arrays.asList(QUEUE_URL, ACCESS_KEY, SECRET_KEY, CREDENTAILS_FILE, REGION, DELAY, TIMEOUT) );
private volatile List<PropertyDescriptor> userDefinedProperties = Collections.emptyList();
@Override
protected List<PropertyDescriptor> getSupportedPropertyDescriptors() {
return properties;
}
@Override
protected PropertyDescriptor getSupportedDynamicPropertyDescriptor(final String propertyDescriptorName) {
return new PropertyDescriptor.Builder()
.name(propertyDescriptorName)
.expressionLanguageSupported(true)
.required(false)
.dynamic(true)
.addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
.build();
}
@OnScheduled
public void setup(final ProcessContext context) {
userDefinedProperties = new ArrayList<>();
for ( final PropertyDescriptor descriptor : context.getProperties().keySet() ) {
if ( descriptor.isDynamic() ) {
userDefinedProperties.add(descriptor);
}
}
}
@Override
public void onTrigger(final ProcessContext context, final ProcessSession session) {
FlowFile flowFile = session.get();
if ( flowFile == null ) {
return;
}
final long startNanos = System.nanoTime();
final AmazonSQSClient client = getClient();
final SendMessageBatchRequest request = new SendMessageBatchRequest();
final String queueUrl = context.getProperty(QUEUE_URL).evaluateAttributeExpressions(flowFile).getValue();
request.setQueueUrl(queueUrl);
final Set<SendMessageBatchRequestEntry> entries = new HashSet<>();
final SendMessageBatchRequestEntry entry = new SendMessageBatchRequestEntry();
entry.setId(flowFile.getAttribute("uuid"));
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
session.exportTo(flowFile, baos);
final String flowFileContent = baos.toString();
entry.setMessageBody(flowFileContent);
final Map<String, MessageAttributeValue> messageAttributes = new HashMap<>();
for ( final PropertyDescriptor descriptor : userDefinedProperties ) {
final MessageAttributeValue mav = new MessageAttributeValue();
mav.setDataType("String");
mav.setStringValue(context.getProperty(descriptor).evaluateAttributeExpressions(flowFile).getValue());
messageAttributes.put(descriptor.getName(), mav);
}
entry.setMessageAttributes(messageAttributes);
entry.setDelaySeconds(context.getProperty(DELAY).asTimePeriod(TimeUnit.SECONDS).intValue());
entries.add(entry);
request.setEntries(entries);
try {
client.sendMessageBatch(request);
} catch (final Exception e) {
getLogger().error("Failed to send messages to Amazon SQS due to {}; routing to failure", new Object[] {e});
session.transfer(flowFile, REL_FAILURE);
return;
}
getLogger().info("Successfully published message to Amazon SQS for {}", new Object[] {flowFile});
session.transfer(flowFile, REL_SUCCESS);
final long transmissionMillis = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanos);
session.getProvenanceReporter().send(flowFile, queueUrl, transmissionMillis);
}
}

View File

@ -0,0 +1,20 @@
# 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.aws.s3.FetchS3Object
org.apache.nifi.processors.aws.s3.PutS3Object
org.apache.nifi.processors.aws.sns.PutSNS
org.apache.nifi.processors.aws.sqs.GetSQS
org.apache.nifi.processors.aws.sqs.PutSQS
org.apache.nifi.processors.aws.sqs.DeleteSQS

View File

@ -0,0 +1,44 @@
package org.apache.nifi.processors.aws.s3;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.nifi.util.MockFlowFile;
import org.apache.nifi.util.TestRunner;
import org.apache.nifi.util.TestRunners;
import org.junit.Ignore;
import org.junit.Test;
@Ignore("For local testing only - interacts with S3 so the credentials file must be configured and all necessary buckets created")
public class TestFetchS3Object {
private final String CREDENTIALS_FILE = System.getProperty("user.home") + "/aws-credentials.properties";
@Test
public void testGet() throws IOException {
final TestRunner runner = TestRunners.newTestRunner(new FetchS3Object());
runner.setProperty(FetchS3Object.BUCKET, "anonymous-test-bucket-00000000");
runner.setProperty(FetchS3Object.CREDENTAILS_FILE, CREDENTIALS_FILE);
runner.setProperty(FetchS3Object.KEY, "folder/1.txt");
final Map<String, String> attrs = new HashMap<>();
attrs.put("start", "0");
runner.enqueue(new byte[0], attrs);
runner.run(1);
runner.assertAllFlowFilesTransferred(FetchS3Object.REL_SUCCESS, 1);
final List<MockFlowFile> ffs = runner.getFlowFilesForRelationship(FetchS3Object.REL_SUCCESS);
final MockFlowFile out = ffs.iterator().next();
final byte[] expectedBytes = Files.readAllBytes(Paths.get("src/test/resources/hello.txt"));
out.assertContentEquals(new String(expectedBytes));
for ( final Map.Entry<String, String> entry : out.getAttributes().entrySet() ) {
System.out.println(entry.getKey() + " : " + entry.getValue());
}
}
}

View File

@ -0,0 +1,87 @@
package org.apache.nifi.processors.aws.s3;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import org.apache.nifi.util.TestRunner;
import org.apache.nifi.util.TestRunners;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import com.amazonaws.services.s3.model.StorageClass;
@Ignore("For local testing only - interacts with S3 so the credentials file must be configured and all necessary buckets created")
public class TestPutS3Object {
private final String CREDENTIALS_FILE = System.getProperty("user.home") + "/aws-credentials.properties";
@Test
public void testSimplePut() throws IOException {
final TestRunner runner = TestRunners.newTestRunner(new PutS3Object());
runner.setProperty(PutS3Object.CREDENTAILS_FILE, CREDENTIALS_FILE);
runner.setProperty(PutS3Object.BUCKET, "test-bucket-00000000-0000-0000-0000-123456789012");
runner.setProperty(PutS3Object.EXPIRATION_RULE_ID, "Expire Quickly");
Assert.assertTrue( runner.setProperty("x-custom-prop", "hello").isValid() );
for (int i=0; i < 3; i++) {
final Map<String, String> attrs = new HashMap<>();
attrs.put("filename", String.valueOf(i) + ".txt");
runner.enqueue(Paths.get("src/test/resources/hello.txt"), attrs);
}
runner.run(3);
runner.assertAllFlowFilesTransferred(PutS3Object.REL_SUCCESS, 3);
}
@Test
public void testPutInFolder() throws IOException {
final TestRunner runner = TestRunners.newTestRunner(new PutS3Object());
runner.setProperty(PutS3Object.BUCKET, "test-bucket-00000000-0000-0000-0000-123456789012");
runner.setProperty(PutS3Object.CREDENTAILS_FILE, CREDENTIALS_FILE);
runner.setProperty(PutS3Object.EXPIRATION_RULE_ID, "Expire Quickly");
Assert.assertTrue( runner.setProperty("x-custom-prop", "hello").isValid() );
final Map<String, String> attrs = new HashMap<>();
attrs.put("filename", "folder/1.txt");
runner.enqueue(Paths.get("src/test/resources/hello.txt"), attrs);
runner.run();
runner.assertAllFlowFilesTransferred(PutS3Object.REL_SUCCESS, 1);
}
@Test
public void testStorageClass() throws IOException {
final TestRunner runner = TestRunners.newTestRunner(new PutS3Object());
runner.setProperty(PutS3Object.BUCKET, "test-bucket-00000000-0000-0000-0000-123456789012");
runner.setProperty(PutS3Object.CREDENTAILS_FILE, CREDENTIALS_FILE);
runner.setProperty(PutS3Object.STORAGE_CLASS, StorageClass.ReducedRedundancy.name());
Assert.assertTrue( runner.setProperty("x-custom-prop", "hello").isValid() );
final Map<String, String> attrs = new HashMap<>();
attrs.put("filename", "folder/2.txt");
runner.enqueue(Paths.get("src/test/resources/hello.txt"), attrs);
runner.run();
runner.assertAllFlowFilesTransferred(PutS3Object.REL_SUCCESS, 1);
}
@Test
public void testPermissions() throws IOException {
final TestRunner runner = TestRunners.newTestRunner(new PutS3Object());
runner.setProperty(PutS3Object.BUCKET, "test-bucket-00000000-0000-0000-0000-123456789012");
runner.setProperty(PutS3Object.CREDENTAILS_FILE, CREDENTIALS_FILE);
runner.setProperty(PutS3Object.FULL_CONTROL_USER_LIST, "28545acd76c35c7e91f8409b95fd1aa0c0914bfa1ac60975d9f48bc3c5e090b5");
final Map<String, String> attrs = new HashMap<>();
attrs.put("filename", "folder/4.txt");
runner.enqueue(Paths.get("src/test/resources/hello.txt"), attrs);
runner.run();
runner.assertAllFlowFilesTransferred(PutS3Object.REL_SUCCESS, 1);
}
}

View File

@ -0,0 +1,34 @@
package org.apache.nifi.processors.aws.sns;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import org.apache.nifi.util.TestRunner;
import org.apache.nifi.util.TestRunners;
import org.junit.Ignore;
import org.junit.Test;
@Ignore("For local testing only - interacts with S3 so the credentials file must be configured and all necessary buckets created")
public class TestPutSNS {
private final String CREDENTIALS_FILE = System.getProperty("user.home") + "/aws-credentials.properties";
@Test
public void testPublish() throws IOException {
final TestRunner runner = TestRunners.newTestRunner(new PutSNS());
runner.setProperty(PutSNS.CREDENTAILS_FILE, CREDENTIALS_FILE);
runner.setProperty(PutSNS.ARN, "arn:aws:sns:us-west-2:100515378163:test-topic-1");
assertTrue( runner.setProperty("DynamicProperty", "hello!").isValid() );
final Map<String, String> attrs = new HashMap<>();
attrs.put("filename", "1.txt");
runner.enqueue(Paths.get("src/test/resources/hello.txt"), attrs);
runner.run();
runner.assertAllFlowFilesTransferred(PutSNS.REL_SUCCESS, 1);
}
}

View File

@ -0,0 +1,32 @@
package org.apache.nifi.processors.aws.sqs;
import java.util.List;
import org.apache.nifi.processors.aws.sns.PutSNS;
import org.apache.nifi.util.MockFlowFile;
import org.apache.nifi.util.TestRunner;
import org.apache.nifi.util.TestRunners;
import org.junit.Ignore;
import org.junit.Test;
@Ignore("For local testing only - interacts with S3 so the credentials file must be configured and all necessary buckets created")
public class TestGetSQS {
private final String CREDENTIALS_FILE = System.getProperty("user.home") + "/aws-credentials.properties";
@Test
public void testSimpleGet() {
final TestRunner runner = TestRunners.newTestRunner(new GetSQS());
runner.setProperty(PutSNS.CREDENTAILS_FILE, CREDENTIALS_FILE);
runner.setProperty(GetSQS.TIMEOUT, "30 secs");
runner.setProperty(GetSQS.QUEUE_URL, "https://sqs.us-west-2.amazonaws.com/100515378163/test-queue-000000000");
runner.run(1);
final List<MockFlowFile> flowFiles = runner.getFlowFilesForRelationship(GetSQS.REL_SUCCESS);
for ( final MockFlowFile mff : flowFiles ) {
System.out.println(mff.getAttributes());
System.out.println(new String(mff.toByteArray()));
}
}
}

View File

@ -0,0 +1,35 @@
package org.apache.nifi.processors.aws.sqs;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import org.apache.nifi.processors.aws.sns.PutSNS;
import org.apache.nifi.util.TestRunner;
import org.apache.nifi.util.TestRunners;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
@Ignore("For local testing only - interacts with S3 so the credentials file must be configured and all necessary buckets created")
public class TestPutSQS {
private final String CREDENTIALS_FILE = System.getProperty("user.home") + "/aws-credentials.properties";
@Test
public void testSimplePut() throws IOException {
final TestRunner runner = TestRunners.newTestRunner(new PutSQS());
runner.setProperty(PutSNS.CREDENTAILS_FILE, CREDENTIALS_FILE);
runner.setProperty(PutSQS.TIMEOUT, "30 secs");
runner.setProperty(PutSQS.QUEUE_URL, "https://sqs.us-west-2.amazonaws.com/100515378163/test-queue-000000000");
Assert.assertTrue( runner.setProperty("x-custom-prop", "hello").isValid() );
final Map<String, String> attrs = new HashMap<>();
attrs.put("filename", "1.txt");
runner.enqueue(Paths.get("src/test/resources/hello.txt"), attrs);
runner.run(1);
runner.assertAllFlowFilesTransferred(PutSQS.REL_SUCCESS, 1);
}
}

View File

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-nar-bundles</artifactId>
<version>0.1.0-incubating-SNAPSHOT</version>
</parent>
<artifactId>nifi-aws-bundle</artifactId>
<packaging>pom</packaging>
<modules>
<module>nifi-aws-processors</module>
<module>nifi-aws-nar</module>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk</artifactId>
<version>1.9.24</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>

View File

@ -32,14 +32,6 @@
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>nifi-properties</artifactId> <artifactId>nifi-properties</artifactId>
</dependency> </dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>nifi-processor-utils</artifactId> <artifactId>nifi-processor-utils</artifactId>

View File

@ -26,7 +26,6 @@ import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter; import javax.xml.stream.XMLStreamWriter;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.annotation.behavior.DynamicProperties; import org.apache.nifi.annotation.behavior.DynamicProperties;
import org.apache.nifi.annotation.behavior.DynamicProperty; import org.apache.nifi.annotation.behavior.DynamicProperty;
import org.apache.nifi.annotation.documentation.CapabilityDescription; import org.apache.nifi.annotation.documentation.CapabilityDescription;
@ -212,13 +211,23 @@ public class HtmlDocumentationWriter implements DocumentationWriter {
xmlStreamWriter.writeEndElement(); xmlStreamWriter.writeEndElement();
xmlStreamWriter.writeStartElement("p"); xmlStreamWriter.writeStartElement("p");
if (tags != null) { if (tags != null) {
final String tagString = StringUtils.join(tags.value(), ", "); final String tagString = join(tags.value(), ", ");
xmlStreamWriter.writeCharacters(tagString); xmlStreamWriter.writeCharacters(tagString);
} else { } else {
xmlStreamWriter.writeCharacters("None."); xmlStreamWriter.writeCharacters("None.");
} }
xmlStreamWriter.writeEndElement(); xmlStreamWriter.writeEndElement();
}
static String join(final String[] toJoin, final String delimiter) {
final StringBuilder sb = new StringBuilder();
for (int i=0; i < toJoin.length; i++) {
sb.append(toJoin[i]);
if ( i < toJoin.length - 1 ) {
sb.append(delimiter);
}
}
return sb.toString();
} }
/** /**

View File

@ -23,7 +23,6 @@ import java.util.List;
import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter; import javax.xml.stream.XMLStreamWriter;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.annotation.behavior.DynamicRelationship; import org.apache.nifi.annotation.behavior.DynamicRelationship;
import org.apache.nifi.annotation.behavior.ReadsAttribute; import org.apache.nifi.annotation.behavior.ReadsAttribute;
import org.apache.nifi.annotation.behavior.ReadsAttributes; import org.apache.nifi.annotation.behavior.ReadsAttributes;
@ -67,6 +66,13 @@ public class HtmlProcessorDocumentationWriter extends HtmlDocumentationWriter {
handleWritesAttributes(xmlStreamWriter, processor); handleWritesAttributes(xmlStreamWriter, processor);
} }
private String defaultIfBlank(final String test, final String defaultValue) {
if ( test == null || test.trim().isEmpty() ) {
return defaultValue;
}
return test;
}
/** /**
* Writes out just the attributes that are being read in a table form. * Writes out just the attributes that are being read in a table form.
* *
@ -91,10 +97,10 @@ public class HtmlProcessorDocumentationWriter extends HtmlDocumentationWriter {
for (ReadsAttribute attribute : attributesRead) { for (ReadsAttribute attribute : attributesRead) {
xmlStreamWriter.writeStartElement("tr"); xmlStreamWriter.writeStartElement("tr");
writeSimpleElement(xmlStreamWriter, "td", writeSimpleElement(xmlStreamWriter, "td",
StringUtils.defaultIfBlank(attribute.attribute(), "Not Specified")); defaultIfBlank(attribute.attribute(), "Not Specified"));
// TODO allow for HTML characters here. // TODO allow for HTML characters here.
writeSimpleElement(xmlStreamWriter, "td", writeSimpleElement(xmlStreamWriter, "td",
StringUtils.defaultIfBlank(attribute.description(), "Not Specified")); defaultIfBlank(attribute.description(), "Not Specified"));
xmlStreamWriter.writeEndElement(); xmlStreamWriter.writeEndElement();
} }
@ -129,10 +135,10 @@ public class HtmlProcessorDocumentationWriter extends HtmlDocumentationWriter {
for (WritesAttribute attribute : attributesRead) { for (WritesAttribute attribute : attributesRead) {
xmlStreamWriter.writeStartElement("tr"); xmlStreamWriter.writeStartElement("tr");
writeSimpleElement(xmlStreamWriter, "td", writeSimpleElement(xmlStreamWriter, "td",
StringUtils.defaultIfBlank(attribute.attribute(), "Not Specified")); defaultIfBlank(attribute.attribute(), "Not Specified"));
// TODO allow for HTML characters here. // TODO allow for HTML characters here.
writeSimpleElement(xmlStreamWriter, "td", writeSimpleElement(xmlStreamWriter, "td",
StringUtils.defaultIfBlank(attribute.description(), "Not Specified")); defaultIfBlank(attribute.description(), "Not Specified"));
xmlStreamWriter.writeEndElement(); xmlStreamWriter.writeEndElement();
} }
xmlStreamWriter.writeEndElement(); xmlStreamWriter.writeEndElement();

View File

@ -30,9 +30,17 @@ import org.apache.nifi.reporting.ReportingTask;
import org.junit.Test; import org.junit.Test;
import static org.apache.nifi.documentation.html.XmlValidator.assertContains; import static org.apache.nifi.documentation.html.XmlValidator.assertContains;
import static org.junit.Assert.assertEquals;
public class HtmlDocumentationWriterTest { public class HtmlDocumentationWriterTest {
@Test
public void testJoin() {
assertEquals("a, b, c", HtmlDocumentationWriter.join(new String[] {"a", "b", "c"}, ", "));
assertEquals("a, b", HtmlDocumentationWriter.join(new String[] {"a", "b"}, ", "));
assertEquals("a", HtmlDocumentationWriter.join(new String[] {"a"}, ", "));
}
@Test @Test
public void testDocumentControllerService() throws InitializationException, IOException { public void testDocumentControllerService() throws InitializationException, IOException {

View File

@ -116,12 +116,23 @@ public class StandardRepositoryRecord implements RepositoryRecord {
public void setWorking(final FlowFileRecord flowFile, final String attributeKey, final String attributeValue) { public void setWorking(final FlowFileRecord flowFile, final String attributeKey, final String attributeValue) {
workingFlowFileRecord = flowFile; workingFlowFileRecord = flowFile;
updatedAttributes.put(attributeKey, attributeValue);
// If setting attribute to same value as original, don't add to updated attributes
final String currentValue = originalAttributes.get(attributeKey);
if ( currentValue == null || !currentValue.equals(attributeValue) ) {
updatedAttributes.put(attributeKey, attributeValue);
}
} }
public void setWorking(final FlowFileRecord flowFile, final Map<String, String> updatedAttribs) { public void setWorking(final FlowFileRecord flowFile, final Map<String, String> updatedAttribs) {
workingFlowFileRecord = flowFile; workingFlowFileRecord = flowFile;
updatedAttributes.putAll(updatedAttribs);
for ( final Map.Entry<String, String> entry : updatedAttribs.entrySet() ) {
final String currentValue = originalAttributes.get(entry.getKey());
if ( currentValue == null || !currentValue.equals(entry.getValue()) ) {
updatedAttributes.put(entry.getKey(), entry.getValue());
}
}
} }
@Override @Override

View File

@ -107,6 +107,6 @@ public class ContinuallyRunConnectableTask implements Callable<Boolean> {
return true; return true;
} }
return true; return false; // do not yield
} }
} }

View File

@ -171,6 +171,7 @@ public class StandardRemoteGroupPort extends RemoteGroupPort {
this.targetRunning.set(false); this.targetRunning.set(false);
final String message = String.format("%s failed to communicate with %s because the remote instance indicates that the port is not in a valid state", this, url); final String message = String.format("%s failed to communicate with %s because the remote instance indicates that the port is not in a valid state", this, url);
logger.error(message); logger.error(message);
session.rollback();
remoteGroup.getEventReporter().reportEvent(Severity.ERROR, CATEGORY, message); remoteGroup.getEventReporter().reportEvent(Severity.ERROR, CATEGORY, message);
return; return;
} catch (final UnknownPortException e) { } catch (final UnknownPortException e) {
@ -178,21 +179,24 @@ public class StandardRemoteGroupPort extends RemoteGroupPort {
this.targetExists.set(false); this.targetExists.set(false);
final String message = String.format("%s failed to communicate with %s because the remote instance indicates that the port no longer exists", this, url); final String message = String.format("%s failed to communicate with %s because the remote instance indicates that the port no longer exists", this, url);
logger.error(message); logger.error(message);
session.rollback();
remoteGroup.getEventReporter().reportEvent(Severity.ERROR, CATEGORY, message); remoteGroup.getEventReporter().reportEvent(Severity.ERROR, CATEGORY, message);
return; return;
} catch (final IOException e) { } catch (final IOException e) {
context.yield();
final String message = String.format("%s failed to communicate with %s due to %s", this, url, e.toString()); final String message = String.format("%s failed to communicate with %s due to %s", this, url, e.toString());
logger.error(message); logger.error(message);
if ( logger.isDebugEnabled() ) { if ( logger.isDebugEnabled() ) {
logger.error("", e); logger.error("", e);
} }
session.rollback();
remoteGroup.getEventReporter().reportEvent(Severity.ERROR, CATEGORY, message); remoteGroup.getEventReporter().reportEvent(Severity.ERROR, CATEGORY, message);
session.rollback();
return; return;
} }
if ( transaction == null ) { if ( transaction == null ) {
logger.debug("{} Unable to create transaction to communicate with; all peers must be penalized, so yielding context", this); logger.debug("{} Unable to create transaction to communicate with; all peers must be penalized, so yielding context", this);
session.rollback();
context.yield(); context.yield();
return; return;
} }

View File

@ -121,8 +121,14 @@ public class StandardNiFiContentAccess implements ContentAccess {
final String rawDirection = StringUtils.substringAfterLast(eventDetails, "/content/"); final String rawDirection = StringUtils.substringAfterLast(eventDetails, "/content/");
// get the content type // get the content type
final Long eventId = Long.parseLong(rawEventId); final Long eventId;
final ContentDirection direction = ContentDirection.valueOf(rawDirection.toUpperCase()); final ContentDirection direction;
try {
eventId = Long.parseLong(rawEventId);
direction = ContentDirection.valueOf(rawDirection.toUpperCase());
} catch (final IllegalArgumentException iae) {
throw new IllegalArgumentException("The specified data reference URI is not valid.");
}
return serviceFacade.getContent(eventId, request.getDataUri(), direction); return serviceFacade.getContent(eventId, request.getDataUri(), direction);
} }
} }

View File

@ -35,11 +35,7 @@ public class AdministrationExceptionMapper implements ExceptionMapper<Administra
@Override @Override
public Response toResponse(AdministrationException exception) { public Response toResponse(AdministrationException exception) {
// log the error // log the error
logger.info(String.format("%s. Returning %s response.", exception, Response.Status.INTERNAL_SERVER_ERROR)); logger.error(String.format("%s. Returning %s response.", exception, Response.Status.INTERNAL_SERVER_ERROR), exception);
if (logger.isDebugEnabled()) {
logger.debug(StringUtils.EMPTY, exception);
}
// generate the response // generate the response
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(exception.getMessage()).type("text/plain").build(); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(exception.getMessage()).type("text/plain").build();

View File

@ -35,11 +35,7 @@ public class ClusterExceptionMapper implements ExceptionMapper<ClusterException>
@Override @Override
public Response toResponse(ClusterException exception) { public Response toResponse(ClusterException exception) {
// log the error // log the error
logger.info(String.format("%s. Returning %s response.", exception, Response.Status.INTERNAL_SERVER_ERROR)); logger.error(String.format("%s. Returning %s response.", exception, Response.Status.INTERNAL_SERVER_ERROR), exception);
if (logger.isDebugEnabled()) {
logger.debug(StringUtils.EMPTY, exception);
}
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(exception.getMessage()).type("text/plain").build(); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(exception.getMessage()).type("text/plain").build();
} }

View File

@ -0,0 +1,48 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nifi.web.api.config;
import com.sun.jersey.api.NotFoundException;
import com.sun.jersey.api.Responses;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
import org.apache.nifi.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Maps not found exceptions into client responses.
*/
@Provider
public class NotFoundExceptionMapper implements ExceptionMapper<NotFoundException> {
private static final Logger logger = LoggerFactory.getLogger(NotFoundExceptionMapper.class);
@Override
public Response toResponse(NotFoundException exception) {
// log the error
logger.info(String.format("%s. Returning %s response.", exception, Response.Status.NOT_FOUND));
if (logger.isDebugEnabled()) {
logger.debug(StringUtils.EMPTY, exception);
}
return Responses.notFound().entity("The specified resource could not be found.").type("text/plain").build();
}
}

View File

@ -34,8 +34,7 @@ public class ThrowableMapper implements ExceptionMapper<Throwable> {
@Override @Override
public Response toResponse(Throwable exception) { public Response toResponse(Throwable exception) {
// log the error // log the error
logger.error(String.format("An unexpected error has occurred: %s. Returning %s response.", exception, Response.Status.INTERNAL_SERVER_ERROR)); logger.error(String.format("An unexpected error has occurred: %s. Returning %s response.", exception, Response.Status.INTERNAL_SERVER_ERROR), exception);
logger.error(StringUtils.EMPTY, exception);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity("An unexpected error has occurred. Please check the logs for additional details.").type("text/plain").build(); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity("An unexpected error has occurred. Please check the logs for additional details.").type("text/plain").build();
} }

View File

@ -270,6 +270,7 @@
<bean class="org.apache.nifi.web.api.config.NodeReconnectionExceptionMapper" scope="singleton"/> <bean class="org.apache.nifi.web.api.config.NodeReconnectionExceptionMapper" scope="singleton"/>
<bean class="org.apache.nifi.web.api.config.PrimaryRoleAssignmentExceptionMapper" scope="singleton"/> <bean class="org.apache.nifi.web.api.config.PrimaryRoleAssignmentExceptionMapper" scope="singleton"/>
<bean class="org.apache.nifi.web.api.config.ResourceNotFoundExceptionMapper" scope="singleton"/> <bean class="org.apache.nifi.web.api.config.ResourceNotFoundExceptionMapper" scope="singleton"/>
<bean class="org.apache.nifi.web.api.config.NotFoundExceptionMapper" scope="singleton"/>
<bean class="org.apache.nifi.web.api.config.UnknownNodeExceptionMapper" scope="singleton"/> <bean class="org.apache.nifi.web.api.config.UnknownNodeExceptionMapper" scope="singleton"/>
<bean class="org.apache.nifi.web.api.config.ValidationExceptionMapper" scope="singleton"/> <bean class="org.apache.nifi.web.api.config.ValidationExceptionMapper" scope="singleton"/>
<bean class="org.apache.nifi.web.api.config.WebApplicationExceptionMapper" scope="singleton"/> <bean class="org.apache.nifi.web.api.config.WebApplicationExceptionMapper" scope="singleton"/>

View File

@ -68,10 +68,21 @@ public class ContentViewerController extends HttpServlet {
final ServletContext servletContext = request.getServletContext(); final ServletContext servletContext = request.getServletContext();
final ContentAccess contentAccess = (ContentAccess) servletContext.getAttribute("nifi-content-access"); final ContentAccess contentAccess = (ContentAccess) servletContext.getAttribute("nifi-content-access");
final ContentRequestContext contentRequest = getContentRequest(request);
if (contentRequest.getDataUri() == null) {
request.setAttribute("title", "Error");
request.setAttribute("messages", "The data reference must be specified.");
// forward to the error page
final ServletContext viewerContext = servletContext.getContext("/nifi");
viewerContext.getRequestDispatcher("/message").forward(request, response);
return;
}
// get the content // get the content
final DownloadableContent downloadableContent; final DownloadableContent downloadableContent;
try { try {
downloadableContent = contentAccess.getContent(getContentRequest(request)); downloadableContent = contentAccess.getContent(contentRequest);
} catch (final ResourceNotFoundException rnfe) { } catch (final ResourceNotFoundException rnfe) {
request.setAttribute("title", "Error"); request.setAttribute("title", "Error");
request.setAttribute("messages", "Unable to find the specified content"); request.setAttribute("messages", "Unable to find the specified content");
@ -138,9 +149,6 @@ public class ContentViewerController extends HttpServlet {
final String mimeType = mediatype.toString(); final String mimeType = mediatype.toString();
// add attributes needed for the header // add attributes needed for the header
final StringBuffer requestUrl = request.getRequestURL();
request.setAttribute("requestUrl", requestUrl.toString());
request.setAttribute("dataRef", request.getParameter("ref"));
request.setAttribute("filename", downloadableContent.getFilename()); request.setAttribute("filename", downloadableContent.getFilename());
request.setAttribute("contentType", mimeType); request.setAttribute("contentType", mimeType);
@ -148,8 +156,6 @@ public class ContentViewerController extends HttpServlet {
request.getRequestDispatcher("/WEB-INF/jsp/header.jsp").include(request, response); request.getRequestDispatcher("/WEB-INF/jsp/header.jsp").include(request, response);
// remove the attributes needed for the header // remove the attributes needed for the header
request.removeAttribute("requestUrl");
request.removeAttribute("dataRef");
request.removeAttribute("filename"); request.removeAttribute("filename");
request.removeAttribute("contentType"); request.removeAttribute("contentType");

View File

@ -31,8 +31,8 @@
<script type="text/javascript"> <script type="text/javascript">
var $$ = $.noConflict(true); var $$ = $.noConflict(true);
$$(document).ready(function () { $$(document).ready(function () {
var url = '${requestUrl}'; var url = $$('#requestUrl').text();
var ref = '${param.ref}'; var ref = $$('#ref').text();
// create the parameters // create the parameters
var params = { var params = {
@ -40,14 +40,14 @@
}; };
// include the cluster node if appropriate // include the cluster node if appropriate
var clusterNodeId = '${param.clusterNodeId}'; var clusterNodeId = $$('#clusterNodeId').text();
if (clusterNodeId !== null && clusterNodeId !== '') { if (clusterNodeId !== '') {
params['clusterNodeId'] = clusterNodeId; params['clusterNodeId'] = clusterNodeId;
} }
// determine the appropriate mode to select initially // determine the appropriate mode to select initially
var initialMode = '${param.mode}'; var initialMode = $$('#mode').text();
if (initialMode === null && initialMode === '') { if (initialMode === '') {
initialMode = 'Original'; initialMode = 'Original';
} }
@ -85,8 +85,12 @@
</script> </script>
</head> </head>
<body class="message-pane"> <body class="message-pane">
<span id="ref" class="hidden"><%= org.apache.nifi.util.EscapeUtils.escapeHtml(request.getParameter("ref")) %></span>
<span id="clusterNodeId" class="hidden"><%= request.getParameter("clusterNodeId") == null ? "" : org.apache.nifi.util.EscapeUtils.escapeHtml(request.getParameter("clusterNodeId")) %></span>
<span id="mode" class="hidden"><%= request.getParameter("mode") == null ? "" : org.apache.nifi.util.EscapeUtils.escapeHtml(request.getParameter("mode")) %></span>
<span id="requestUrl" class="hidden"><%= org.apache.nifi.util.EscapeUtils.escapeHtml(request.getRequestURL().toString()) %></span>
<div id="view-as-label">View as</div> <div id="view-as-label">View as</div>
<div id="view-as" class="pointer button-normal"></div> <div id="view-as" class="pointer button-normal"></div>
<div id="content-filename"><span class="content-label">filename</span>${filename}</div> <div id="content-filename"><span class="content-label">filename</span><%= request.getAttribute("filename") == null ? "" : org.apache.nifi.util.EscapeUtils.escapeHtml(request.getAttribute("filename").toString()) %></div>
<div id="content-type"><span class="content-label">content type</span>${contentType}</div> <div id="content-type"><span class="content-label">content type</span><%= request.getAttribute("contentType") == null ? "" : org.apache.nifi.util.EscapeUtils.escapeHtml(request.getAttribute("contentType").toString()) %></div>
<div class="message-pane-message-box"> <div class="message-pane-message-box">

View File

@ -29,4 +29,4 @@
<input type="hidden" name="caption" value="" /> <input type="hidden" name="caption" value="" />
</form> </form>
</div> </div>
<div id="trancation-message">Showing up to 1.5kb</div> <div id="truncation-message">Showing up to 1.5 KB</div>

View File

@ -87,7 +87,7 @@
padding: 2px; padding: 2px;
} }
#trancation-message { #truncation-message {
position: absolute; position: absolute;
left: 100px; left: 100px;
bottom: 35px; bottom: 35px;

View File

@ -148,7 +148,7 @@ $(document).ready(function () {
$("table", div).addClass("hexviewerwindow_table"); $("table", div).addClass("hexviewerwindow_table");
$("table", div).append("<tr></tr>").addClass("hexviewerwindow"); $("table", div).append("<tr></tr>").addClass("hexviewerwindow");
$("table tr:last", div).append("<td>" + (decimal_offset ? ("00000000"+offset).slice(-8) : "0x" + dec_to_hex8(offset)) + "</td>"); $("table tr:last", div).append("<td>" + escapeHtml((decimal_offset ? ("00000000"+offset).slice(-8) : "0x" + dec_to_hex8(offset))) + "</td>");
$("table tr td:last", div).addClass("hexviewerwindow_offset"); $("table tr td:last", div).addClass("hexviewerwindow_offset");
var runlen = 0; var runlen = 0;
@ -162,7 +162,7 @@ $(document).ready(function () {
num += dec2_to_hex(line_data.charCodeAt(i+j)); num += dec2_to_hex(line_data.charCodeAt(i+j));
} }
$("table tr:last", div).append("<td>" + (hide_0x ? "" : "0x") + num + "</td>"); $("table tr:last", div).append("<td>" + escapeHtml((hide_0x ? "" : "0x") + num) + "</td>");
apply_highlights(offset+i); apply_highlights(offset+i);
} }

View File

@ -26,12 +26,18 @@
<dependency> <dependency>
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId> <artifactId>commons-lang3</artifactId>
<scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.nifi</groupId> <groupId>org.apache.nifi</groupId>
<artifactId>nifi-nar-utils</artifactId> <artifactId>nifi-nar-utils</artifactId>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>org.apache.nifi</groupId>
<artifactId>nifi-utils</artifactId>
<scope>provided</scope>
</dependency>
<dependency> <dependency>
<groupId>javax.servlet.jsp</groupId> <groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId> <artifactId>javax.servlet.jsp-api</artifactId>

View File

@ -30,7 +30,7 @@
<body id="documentation-body"> <body id="documentation-body">
<div id="banner-header" class="main-banner-header"></div> <div id="banner-header" class="main-banner-header"></div>
<div id="banner-footer" class="main-banner-footer"></div> <div id="banner-footer" class="main-banner-footer"></div>
<span id="initial-selection" style="display: none;">${param.select}</span> <span id="initial-selection" style="display: none;"><%= request.getParameter("select") == null ? "" : org.apache.nifi.util.EscapeUtils.escapeHtml(request.getParameter("select")) %></span>
<div id="documentation-header" class="documentation-header"> <div id="documentation-header" class="documentation-header">
<div id="nf-title">NiFi Documentation</div> <div id="nf-title">NiFi Documentation</div>
<div id="nf-version"></div> <div id="nf-version"></div>

View File

@ -14,6 +14,9 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
/* global top */
$(document).ready(function () { $(document).ready(function () {
var isUndefined = function (obj) { var isUndefined = function (obj) {
@ -139,7 +142,7 @@ $(document).ready(function () {
if (isDefinedAndNotNull(response.banners)) { if (isDefinedAndNotNull(response.banners)) {
if (isDefinedAndNotNull(response.banners.headerText) && response.banners.headerText !== '') { if (isDefinedAndNotNull(response.banners.headerText) && response.banners.headerText !== '') {
// update the header text // update the header text
var bannerHeader = $('#banner-header').html(response.banners.headerText).show(); var bannerHeader = $('#banner-header').text(response.banners.headerText).show();
// show the banner // show the banner
var updateTop = function (elementId) { var updateTop = function (elementId) {
@ -155,7 +158,7 @@ $(document).ready(function () {
if (isDefinedAndNotNull(response.banners.footerText) && response.banners.footerText !== '') { if (isDefinedAndNotNull(response.banners.footerText) && response.banners.footerText !== '') {
// update the footer text and show it // update the footer text and show it
var bannerFooter = $('#banner-footer').html(response.banners.footerText).show(); var bannerFooter = $('#banner-footer').text(response.banners.footerText).show();
var updateBottom = function (elementId) { var updateBottom = function (elementId) {
var element = $('#' + elementId); var element = $('#' + elementId);

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