From 5c14cf7b84baa989b1822051addb6d4a94f6c535 Mon Sep 17 00:00:00 2001 From: Gianluca Date: Mon, 16 Dec 2024 10:51:07 +0100 Subject: [PATCH] Add experimental support for KVP format and subscription to regex --- docs/javadoc/allclasses-index.html | 2 +- docs/javadoc/allpackages-index.html | 2 +- .../pub/KafkaConnectorMetadataAdapter.html | 2 +- .../kafka/adapters/pub/package-summary.html | 2 +- .../kafka/adapters/pub/package-tree.html | 2 +- docs/javadoc/help-doc.html | 2 +- docs/javadoc/index-all.html | 2 +- docs/javadoc/index.html | 2 +- docs/javadoc/overview-summary.html | 2 +- docs/javadoc/overview-tree.html | 2 +- examples/kvp-demo/README.md | 3 + examples/kvp-demo/adapters.xml | 46 ++++ examples/kvp-demo/docker | 1 + examples/kvp-demo/docker-compose.yml | 50 ++++ examples/kvp-demo/helpers.sh | 1 + examples/kvp-demo/log4j.properties | 24 ++ examples/kvp-demo/producer/Dockerfile | 23 ++ examples/kvp-demo/producer/README.md | 17 ++ .../producer/FeedSimulator$1.class | Bin 0 -> 1531 bytes .../FeedSimulator$ExternalFeedListener.class | Bin 0 -> 406 bytes .../producer/FeedSimulator$FIELD.class | Bin 0 -> 2058 bytes .../FeedSimulator$QuoteProducer.class | Bin 0 -> 3649 bytes .../quick_start/producer/FeedSimulator.class | Bin 0 -> 7645 bytes .../quick_start/producer/Producer$1.class | Bin 0 -> 2518 bytes .../quick_start/producer/Producer.class | Bin 0 -> 5205 bytes .../producer/bin/main/log4j.properties | 26 ++ examples/kvp-demo/producer/build.gradle | 73 +++++ examples/kvp-demo/producer/gradle.properties | 1 + .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43462 bytes .../gradle/wrapper/gradle-wrapper.properties | 7 + examples/kvp-demo/producer/gradlew | 249 +++++++++++++++++ examples/kvp-demo/producer/gradlew.bat | 92 +++++++ examples/kvp-demo/producer/settings.gradle | 1 + .../quick_start/producer/FeedSimulator.java | 248 +++++++++++++++++ .../quick_start/producer/Producer.java | 102 +++++++ .../src/main/resources/log4j.properties | 26 ++ examples/kvp-demo/producer/start-publisher.sh | 3 + examples/kvp-demo/screen_small.png | Bin 0 -> 10987 bytes examples/kvp-demo/start.sh | 1 + examples/kvp-demo/stop.sh | 1 + examples/kvp-demo/web/css/table.css | 110 ++++++++ examples/kvp-demo/web/images/banner.jpg | Bin 0 -> 97103 bytes examples/kvp-demo/web/images/logo.png | Bin 0 -> 14354 bytes examples/kvp-demo/web/index.html | 255 ++++++++++++++++++ kafka-connector-project/gradle.properties | 4 +- .../kafka/adapters/ConnectorConfigurator.java | 4 +- .../adapters/config/specs/ConfigTypes.java | 1 + .../adapters/consumers/offsets/Offsets.java | 2 +- .../processor/RecordConsumerSupport.java | 6 +- .../wrapper/ConsumerWrapperImpl.java | 47 ++-- .../selectors/kvp/KvpSelectorsSuppliers.java | 248 +++++++++++++++++ .../kafka/common/config/FieldConfigs.java | 8 +- .../kafka/common/mapping/RecordMapper.java | 9 +- .../mapping/selectors/DataExtractor.java | 17 +- .../selectors/DataExtractorSupport.java | 31 ++- .../adapters/ConnectorConfiguratorTest.java | 5 + .../adapters/config/ConnectorConfigTest.java | 1 + .../kvp/KvpNodeSelectorsSuppliersTest.java | 224 +++++++++++++++ .../mapping/selectors/kvp/KvpNodeTest.java | 146 ++++++++++ .../common/mapping/RecordRoutingTest.java | 31 +++ .../mapping/selectors/DataExtractorTest.java | 37 +++ .../kafka/test_utils/VersionUtils.java | 2 +- 62 files changed, 2149 insertions(+), 54 deletions(-) create mode 100644 examples/kvp-demo/README.md create mode 100644 examples/kvp-demo/adapters.xml create mode 120000 examples/kvp-demo/docker create mode 100644 examples/kvp-demo/docker-compose.yml create mode 120000 examples/kvp-demo/helpers.sh create mode 100644 examples/kvp-demo/log4j.properties create mode 100644 examples/kvp-demo/producer/Dockerfile create mode 100644 examples/kvp-demo/producer/README.md create mode 100644 examples/kvp-demo/producer/bin/main/com/lightstreamer/kafka/examples/quick_start/producer/FeedSimulator$1.class create mode 100644 examples/kvp-demo/producer/bin/main/com/lightstreamer/kafka/examples/quick_start/producer/FeedSimulator$ExternalFeedListener.class create mode 100644 examples/kvp-demo/producer/bin/main/com/lightstreamer/kafka/examples/quick_start/producer/FeedSimulator$FIELD.class create mode 100644 examples/kvp-demo/producer/bin/main/com/lightstreamer/kafka/examples/quick_start/producer/FeedSimulator$QuoteProducer.class create mode 100644 examples/kvp-demo/producer/bin/main/com/lightstreamer/kafka/examples/quick_start/producer/FeedSimulator.class create mode 100644 examples/kvp-demo/producer/bin/main/com/lightstreamer/kafka/examples/quick_start/producer/Producer$1.class create mode 100644 examples/kvp-demo/producer/bin/main/com/lightstreamer/kafka/examples/quick_start/producer/Producer.class create mode 100644 examples/kvp-demo/producer/bin/main/log4j.properties create mode 100644 examples/kvp-demo/producer/build.gradle create mode 100644 examples/kvp-demo/producer/gradle.properties create mode 100644 examples/kvp-demo/producer/gradle/wrapper/gradle-wrapper.jar create mode 100644 examples/kvp-demo/producer/gradle/wrapper/gradle-wrapper.properties create mode 100755 examples/kvp-demo/producer/gradlew create mode 100644 examples/kvp-demo/producer/gradlew.bat create mode 100644 examples/kvp-demo/producer/settings.gradle create mode 100644 examples/kvp-demo/producer/src/main/java/com/lightstreamer/kafka/examples/quick_start/producer/FeedSimulator.java create mode 100644 examples/kvp-demo/producer/src/main/java/com/lightstreamer/kafka/examples/quick_start/producer/Producer.java create mode 100644 examples/kvp-demo/producer/src/main/resources/log4j.properties create mode 100755 examples/kvp-demo/producer/start-publisher.sh create mode 100644 examples/kvp-demo/screen_small.png create mode 120000 examples/kvp-demo/start.sh create mode 120000 examples/kvp-demo/stop.sh create mode 100644 examples/kvp-demo/web/css/table.css create mode 100644 examples/kvp-demo/web/images/banner.jpg create mode 100644 examples/kvp-demo/web/images/logo.png create mode 100644 examples/kvp-demo/web/index.html create mode 100644 kafka-connector-project/kafka-connector/src/main/java/com/lightstreamer/kafka/adapters/mapping/selectors/kvp/KvpSelectorsSuppliers.java create mode 100644 kafka-connector-project/kafka-connector/src/test/java/com/lightstreamer/kafka/adapters/mapping/selectors/kvp/KvpNodeSelectorsSuppliersTest.java create mode 100644 kafka-connector-project/kafka-connector/src/test/java/com/lightstreamer/kafka/adapters/mapping/selectors/kvp/KvpNodeTest.java diff --git a/docs/javadoc/allclasses-index.html b/docs/javadoc/allclasses-index.html index 50ee01f9..3c0fcbed 100644 --- a/docs/javadoc/allclasses-index.html +++ b/docs/javadoc/allclasses-index.html @@ -2,7 +2,7 @@ -All Classes and Interfaces (kafka-connector 1.1.0 API) +All Classes and Interfaces (kafka-connector 1.1.1-kvp API) diff --git a/docs/javadoc/allpackages-index.html b/docs/javadoc/allpackages-index.html index d2dc80af..8aef5a6f 100644 --- a/docs/javadoc/allpackages-index.html +++ b/docs/javadoc/allpackages-index.html @@ -2,7 +2,7 @@ -All Packages (kafka-connector 1.1.0 API) +All Packages (kafka-connector 1.1.1-kvp API) diff --git a/docs/javadoc/com/lightstreamer/kafka/adapters/pub/KafkaConnectorMetadataAdapter.html b/docs/javadoc/com/lightstreamer/kafka/adapters/pub/KafkaConnectorMetadataAdapter.html index 37681ac6..92d4473f 100644 --- a/docs/javadoc/com/lightstreamer/kafka/adapters/pub/KafkaConnectorMetadataAdapter.html +++ b/docs/javadoc/com/lightstreamer/kafka/adapters/pub/KafkaConnectorMetadataAdapter.html @@ -2,7 +2,7 @@ -KafkaConnectorMetadataAdapter (kafka-connector 1.1.0 API) +KafkaConnectorMetadataAdapter (kafka-connector 1.1.1-kvp API) diff --git a/docs/javadoc/com/lightstreamer/kafka/adapters/pub/package-summary.html b/docs/javadoc/com/lightstreamer/kafka/adapters/pub/package-summary.html index c5497ba5..8d083d88 100644 --- a/docs/javadoc/com/lightstreamer/kafka/adapters/pub/package-summary.html +++ b/docs/javadoc/com/lightstreamer/kafka/adapters/pub/package-summary.html @@ -2,7 +2,7 @@ -com.lightstreamer.kafka.adapters.pub (kafka-connector 1.1.0 API) +com.lightstreamer.kafka.adapters.pub (kafka-connector 1.1.1-kvp API) diff --git a/docs/javadoc/com/lightstreamer/kafka/adapters/pub/package-tree.html b/docs/javadoc/com/lightstreamer/kafka/adapters/pub/package-tree.html index 1c27e16b..93e1590b 100644 --- a/docs/javadoc/com/lightstreamer/kafka/adapters/pub/package-tree.html +++ b/docs/javadoc/com/lightstreamer/kafka/adapters/pub/package-tree.html @@ -2,7 +2,7 @@ -com.lightstreamer.kafka.adapters.pub Class Hierarchy (kafka-connector 1.1.0 API) +com.lightstreamer.kafka.adapters.pub Class Hierarchy (kafka-connector 1.1.1-kvp API) diff --git a/docs/javadoc/help-doc.html b/docs/javadoc/help-doc.html index 306009db..cae682c0 100644 --- a/docs/javadoc/help-doc.html +++ b/docs/javadoc/help-doc.html @@ -2,7 +2,7 @@ -API Help (kafka-connector 1.1.0 API) +API Help (kafka-connector 1.1.1-kvp API) diff --git a/docs/javadoc/index-all.html b/docs/javadoc/index-all.html index 1d9cb650..c1f424ba 100644 --- a/docs/javadoc/index-all.html +++ b/docs/javadoc/index-all.html @@ -2,7 +2,7 @@ -Index (kafka-connector 1.1.0 API) +Index (kafka-connector 1.1.1-kvp API) diff --git a/docs/javadoc/index.html b/docs/javadoc/index.html index e0854ca3..ed947a9b 100644 --- a/docs/javadoc/index.html +++ b/docs/javadoc/index.html @@ -2,7 +2,7 @@ -Overview (kafka-connector 1.1.0 API) +Overview (kafka-connector 1.1.1-kvp API) diff --git a/docs/javadoc/overview-summary.html b/docs/javadoc/overview-summary.html index f1505b10..e4e8dade 100644 --- a/docs/javadoc/overview-summary.html +++ b/docs/javadoc/overview-summary.html @@ -2,7 +2,7 @@ -kafka-connector 1.1.0 API +kafka-connector 1.1.1-kvp API diff --git a/docs/javadoc/overview-tree.html b/docs/javadoc/overview-tree.html index 5a047e0f..8946a9cf 100644 --- a/docs/javadoc/overview-tree.html +++ b/docs/javadoc/overview-tree.html @@ -2,7 +2,7 @@ -Class Hierarchy (kafka-connector 1.1.0 API) +Class Hierarchy (kafka-connector 1.1.1-kvp API) diff --git a/examples/kvp-demo/README.md b/examples/kvp-demo/README.md new file mode 100644 index 00000000..430b4f7e --- /dev/null +++ b/examples/kvp-demo/README.md @@ -0,0 +1,3 @@ +# Quick Start + +This folder contains all the resources needed to launch the _Quick Start_ app. See the [Quick Start](../../README.md#quick-start-set-up-in-5-minutes) section for more details. diff --git a/examples/kvp-demo/adapters.xml b/examples/kvp-demo/adapters.xml new file mode 100644 index 00000000..64ae4e99 --- /dev/null +++ b/examples/kvp-demo/adapters.xml @@ -0,0 +1,46 @@ + + + + + + com.lightstreamer.kafka.adapters.pub.KafkaConnectorMetadataAdapter + log4j.properties + + + + com.lightstreamer.kafka.adapters.KafkaConnectorDataAdapter + + broker:29092 + + quick-start-group + + STRING + KVP + + quote-#{topic=TOPIC} + + item-template.quotes + + + #{VALUE.NAME} + #{VALUE.QCHARTTOT} + #{VALUE.TRow} + #{VALUE.TMSTMP} + #{VALUE.QCHART} + #{VALUE.VTOT} + #{VALUE.QTOT} + #{VALUE.QA} + #{VALUE.Q} + #{VALUE.PA} + #{VALUE.PCHART} + #{VALUE.NTRAD} + #{VALUE.PLIM_MIN} + #{VALUE.PLIM_MAX} + + + + diff --git a/examples/kvp-demo/docker b/examples/kvp-demo/docker new file mode 120000 index 00000000..880b1623 --- /dev/null +++ b/examples/kvp-demo/docker @@ -0,0 +1 @@ +../compose-templates/docker \ No newline at end of file diff --git a/examples/kvp-demo/docker-compose.yml b/examples/kvp-demo/docker-compose.yml new file mode 100644 index 00000000..1fe05964 --- /dev/null +++ b/examples/kvp-demo/docker-compose.yml @@ -0,0 +1,50 @@ +--- +name: quickstart-kafka-connector +services: + kafka-connector: + container_name: kafka-connector + image: lightstreamer-kafka-connector-${version} + depends_on: + - broker + - producer + ports: + - 8080:8080 + volumes: + - ./web:/lightstreamer/pages/QuickStart + - ./adapters.xml:/lightstreamer/adapters/lightstreamer-kafka-connector-${version}/adapters.xml + - ./log4j.properties:/lightstreamer/adapters/lightstreamer-kafka-connector-${version}/log4j.properties + + producer: + container_name: producer + depends_on: + - broker + build: + context: ./producer + args: + VERSION: ${version} + command: ["--bootstrap-servers", "broker:29092"] + + broker: + image: apache/kafka + hostname: broker + container_name: broker + ports: + - "9092:9092" + - "8082:8082" + environment: + KAFKA_NODE_ID: 1 + KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: 'CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT' + KAFKA_ADVERTISED_LISTENERS: 'PLAINTEXT://broker:29092,PLAINTEXT_HOST://localhost:9092' + KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 + KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0 + KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1 + KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1 + KAFKA_PROCESS_ROLES: 'broker,controller' + KAFKA_CONTROLLER_QUORUM_VOTERS: '1@broker:29093' + KAFKA_LISTENERS: 'PLAINTEXT://broker:29092,CONTROLLER://broker:29093,PLAINTEXT_HOST://0.0.0.0:9092' + KAFKA_INTER_BROKER_LISTENER_NAME: 'PLAINTEXT' + KAFKA_CONTROLLER_LISTENER_NAMES: 'CONTROLLER' + KAFKA_LOG_DIRS: '/tmp/kraft-combined-logs' + KAFKA_REST_HOST_NAME: rest-proxy + KAFKA_REST_BOOTSTRAP_SERVERS: 'broker:29092' + KAFKA_REST_LISTENERS: 'http://0.0.0.0:8082' diff --git a/examples/kvp-demo/helpers.sh b/examples/kvp-demo/helpers.sh new file mode 120000 index 00000000..d97f9467 --- /dev/null +++ b/examples/kvp-demo/helpers.sh @@ -0,0 +1 @@ +../compose-templates/helpers.sh \ No newline at end of file diff --git a/examples/kvp-demo/log4j.properties b/examples/kvp-demo/log4j.properties new file mode 100644 index 00000000..35e50b26 --- /dev/null +++ b/examples/kvp-demo/log4j.properties @@ -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. + +log4j.logger.com.lightstreamer.kafka.adapters.pub.KafkaConnectorMetadataAdapter +log4j.logger.org.apache.kafka=WARN, stdout +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=[%d] [%-20t] [%-10c{1}] %-5p %m%n +log4j.appender.stdout.Target=System.out + +# prices logger +log4j.logger.prices=INFO, stdout diff --git a/examples/kvp-demo/producer/Dockerfile b/examples/kvp-demo/producer/Dockerfile new file mode 100644 index 00000000..942baa0f --- /dev/null +++ b/examples/kvp-demo/producer/Dockerfile @@ -0,0 +1,23 @@ +# Use the offical Gradle Docker image as builder +FROM gradle:jdk17 AS builder + +# Set the working directory +WORKDIR /app + +# Copy the project resources +COPY src ./src +COPY build.gradle . +COPY gradlew . +COPY settings.gradle . +COPY gradle.properties . + +# Generate the jar file +RUN gradle build + +# Use the official Eclipse Temuring image +FROM eclipse-temurin:21-jdk + +# Copy the Producer jar from the previuos build stage +COPY --from=builder /app/build/libs/quickstart-producer-all.jar /usr/app/producer.jar + +ENTRYPOINT ["java", "-jar", "/usr/app/producer.jar"] diff --git a/examples/kvp-demo/producer/README.md b/examples/kvp-demo/producer/README.md new file mode 100644 index 00000000..c190055e --- /dev/null +++ b/examples/kvp-demo/producer/README.md @@ -0,0 +1,17 @@ +# Producer for the Quick Start App + +This folder contains the Gradle project of the Kafka native producer used for the _Quick Start_ App. See the [Quick Start](../../README.md#quick-start-set-up-in-5-minutes) section for more details. + +To build the producer: + +```sh +$ ./gradlew build +``` + +which generates the `quickstart-producer-all.jar` file under the `build/libs` folder. + +To run it: + +```sh +$ java -jar build/libs/quickstart-producer-all.jar --bootstrap-servers --topic stocks +``` diff --git a/examples/kvp-demo/producer/bin/main/com/lightstreamer/kafka/examples/quick_start/producer/FeedSimulator$1.class b/examples/kvp-demo/producer/bin/main/com/lightstreamer/kafka/examples/quick_start/producer/FeedSimulator$1.class new file mode 100644 index 0000000000000000000000000000000000000000..d8aaa8db0ba08495537374e436acacdc6080b520 GIT binary patch literal 1531 zcmb_cTTc@~6#k~PU8vO}S3yD4qAj--?@+|VriiAZNePM zG~vM?;Ggk9pN(gCn*b>x#?WSV&di?McfND>$DhOR0G{HRffz%{QjTfMox1k5C%7X# zv%x<$xG4^})3k+e?zN=V_~2{qX|w66TFWAxH6dyh>9lOFm6u&K5NGJ$<@?;Uxw~V& zsqP9(8%Q#|KaT*V11&t4+d$B!rSCkTQCfimBsBX?#bZXxs2<&^ch~1&m+^t0)wI4(!Ovt!&0t%RFsPLq`Oo2 zmz(@n9|kdOVCY0yD{X@Aq%nd~0>m(7Et|r8<{x`dB2q{e9x`9cC!4UAteA1Z04XR5sNYQpF>r)~J6ehH4N-N;0 zt87HK-*&3XCat}e(!^$}qFSCM)?`TSiI!Xl?5P7vu4OBq%KlpDx~dtNVJMyh&uPqI zo?6b~lHiQww!0G!@bo`dDi|!@jNe@-_7#4GygY4M>a@eJAMDN0Lc9qdhV64XMxJ>oh%+;pI)mWX%5)|Y=_&h80C={jG@R2v~2{cv->eM%VJ*yLhtI0Okf_6XyDh)OvAEi7ShD$O=wsadV$_iej+yw`F~$4#C%&4YH8)~s9BfnnF%HPdZ6 zZcT$`5Qocxo?bVI+ zPDvo#8I-nY>@Rm@n`K}-Eg-f928uKY7P|p~LTM+xCJ-v-viZGyws1a7KNW~(3cLC7 zayqx0F$8kY`uY)}RJCR|YMj|X%v~I<;W~EX za48!vhiUC65&;B5&=hT;QmW1d!}&mcpq<- zI;y7CIyU}d9E3ts_z{}-{LyMr+$VT3^xM|;|yN}873Iw8bg9%D#S3&kkmWtW|{8j zorQBu^Ll6H0@Hmx%yElMOL~MU#k8zPnI18vb;2 zO6N3yEd4Ljs{~Bbt2KjBBr$O}*iSs`Soo;=F_~*|^-@BS8hJT#=YayvJ96Li~T6A7AsH&*)>-hdjr( RDw5*q{eii11^Ya|_YVr?(@X#W literal 0 HcmV?d00001 diff --git a/examples/kvp-demo/producer/bin/main/com/lightstreamer/kafka/examples/quick_start/producer/FeedSimulator$QuoteProducer.class b/examples/kvp-demo/producer/bin/main/com/lightstreamer/kafka/examples/quick_start/producer/FeedSimulator$QuoteProducer.class new file mode 100644 index 0000000000000000000000000000000000000000..aebd2105f86750c695c53139910bc8ebdb9b6fd5 GIT binary patch literal 3649 zcmcImTX!4P75VVf+Wk<;KYQYj-_K~V%?Y- zIZ0c}rBK=erI!G0X@PR5A#HJplai7j(5J4|Kfz-k`_jwaM~W>aUTq&-d)YJRn|=26 z+xwh9|MSzU01o0aA36l43e|G7WG$R<9oJT7S=rG=bAHi`s?%neBHH5(P@p* z*UqvsD*_@;f{t4(swKiI6v(Q)_9ExnR%L$#a6Ur(99 zeG*?l539Xj;sA&Vy$@yMF(Z*TlIcV~orq<~m5$?aHgizIj{pmQO=6UVH?<1qjCekt zn9&{`mbeK4B942)as+az4Mi#<*IVFO8P~TR>a?oX85M?I?)oV*9hK6?$qk8 z%Ba)sQPXm{xir_0a1zrJ8BFp9v+g(+H3)Hc(`p8G@4N25qzB*qft0l6$N&MTPe_( z7{lv^7I3+7la-=6O{yu*vAS)miaTSL>WaMWy_OL@CCFpSoxtLhPp_;c7pP>FOyDJUycU zoWdC&xa4b@Yz#b!Z{l0j6BQzOvO+P)exsLJy?k5Z9bR^2wQZJoPvU)sycuIUMq^UpLy6xq z(w$8u)A@8V!{A2}=Uc$oG1{S)s6}beHyWh152!u2=sd2TKxb;#P9gh7@&9+XGSMzb ze1eNK&zMpVtPV!HAH=D5=1hmiNFZ0O+XXdl=_9o5HnfL{M7xufin4Y4@m%uiHl<10 zc2}a*Wt8JiR2`RkmsmzOvn~wqK~HZ3d}h-##D|f7(n+JAbOzG1gHJSicGK_82-5yu zTHQxD|MZ1Im$B&r2CiV+>?PcK0k?lla0j;2|9}=cMH^3Jheko$Z^O=}l4sGWB@PZ< z!k34Zv1{uxc3-XSy@I^2lPdxbV4~?#RIdT=&?mV2587vrq>EtSIju(O zcW+Y-&DV7|@4J5U^XqOtf(M(HAEkP5Wi}6y=@I&itspfz=s)}Vzy3COo<4YVPD2zP z@G_O;6)MMB^x@T(;5IkC>YW?`QDLdY17}Znl%M zzjbeJ1;?)U{k0Yo{j2%w$D_^sS#oeR z^l}^e%KH0%apV49vE=r9d$&K_rr-^NgSQ5Ew}Edl7!K}j1K(w^7(Ceqe!!qVxUCI5 z$Kc-JcnioWZ&dg&H5u>*b*adB8NHa3HN{gT#B-}4SzO+(QJUUkBjR}59>$!|S(lbOF72WMV R9qlLUVO$}ZKk4t`e*=5hl3f4* literal 0 HcmV?d00001 diff --git a/examples/kvp-demo/producer/bin/main/com/lightstreamer/kafka/examples/quick_start/producer/FeedSimulator.class b/examples/kvp-demo/producer/bin/main/com/lightstreamer/kafka/examples/quick_start/producer/FeedSimulator.class new file mode 100644 index 0000000000000000000000000000000000000000..e4b5cc8f0a9a6e2e2e944c37bf1dfdc76aca5e42 GIT binary patch literal 7645 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 UPf3c1cmMzZ literal 0 HcmV?d00001 diff --git a/examples/kvp-demo/producer/bin/main/com/lightstreamer/kafka/examples/quick_start/producer/Producer.class b/examples/kvp-demo/producer/bin/main/com/lightstreamer/kafka/examples/quick_start/producer/Producer.class new file mode 100644 index 0000000000000000000000000000000000000000..4ace58e74a6dd15870c6e39683e83dc7d68c35a8 GIT binary patch literal 5205 zcmb_giGLL38GgQGli4hTNQh`eA<{r|vVv5OMHC`YGzXH95U9|}?ng3YcV?NH4T-I_ zwbfRoww4}h4|^%}pcSi0BvsmKv4z^h-uL}a^w(eQ^UcmCyUD5~zh;x2nQz|j{oeO^ z@0|bdnHK@vfPZNy5!f2HQ_-Y3FzC6SBaM`FqC>`^AtNeBj8r-)-RO~w86P_6dWPdg z(~h0U#7VO&xX}<2xb(0wY($fWH4yFWKP=;(hH`<*>DApC%QE_tQbSl^@BB_|l`_#~ zrZP#xvz^)mHOfw<|Pj;_|fE$V4g4Ap|0%Ib;sBnn@|Ju(>(= z*nG%;zB(&_k*vv?K(Qowje4&*fE;u01lG3M&Op>i8}UII7?dhW%X6n~=u4I}Bvq^E zG6yWf%Q!s0cdq-l%*J{4%2u9IzQwdmZ>vCQU44&0sKrjm2uiU+$1+?cut?$EkxBJS zr(5}qGNBzel0AlFs^?%aSn{TfA&&+ zcBz0-Ov7~oR}`K=w1%?25y3mKLBsX4giszuAvR(Y(U&8P76P{}kfZQ~K$5A&Y3{N5 zeHDn|1`S&Z0T^ZQrF3k?jZ{%OjKa&ZvJ-<{^mLC9F|Wib>7v_oY)6a0Txgw8-Ol_(TsC6+w8(@JLo9R*r13e{<5u|PM@}S6LrQ@9{sLJ9=+hv#?z`HaY%#r$tb`LuC zQ59G^4D>Ve7>PtzCY^Sq>&m=?3yEXu-~UNAcD}X6*?OW%jTzt|snB>A35?DNyZ`TC*R)r;9uL2PTn5`p?BLWLt=>_pSDzLGL z>EAHi@=6$O;3A`ePSG@5Tvi6#ic$>INSb%~r6bCc!|bnP7)KdrSw|I!*3}nDrEj=D zwg97ew}!g}8s~7Vj`!ehmXMr8su-@H%gMeT(xKL+xJSqP@P3vPCajjE;W8|jkk@n_ z^%qI{0oJpRL5tuRKB{WU$5`(JCzz4j>BP!~KCa_(T%q9;0xk2aCI!7o1jq3yb?~PJ z?yQ?p`P}()y+A+*lMPSoK1#g~b2Ck)Y_=!2?ncXsOJBcv9 zDu4){!q=4nzwwq5WZT_Xo&>>)lHi+cPv+-Ox$IZFHiA=lTFG)+ATwWQ%CUTBD=n6UyX)r8=puHs7|CQm3q`Id`fkfEH=c1Ex3 zopDunCUSMhPuf#;hqcbL*}*dfyQ8UoJIRG38QY^zmpi_Z+523CFf(wNiipC~=)Fq| zyVKEhu0!2B=Tk1ahYqC-lWon?x?-IpJ?~{=Q)G&XFxjn$;3d4GJnDxt`l2b%RTSrS zT)>Z+!{vyKXB2wH4`Z7ZGMoWbB8teubkt>MoVZjiC<8qQ>KaurnCQ3X#L!0J@MwOP2&l6RZ{_&L-nX{Rwudt5 z{4fm?MPA?*Z0x2Pi;(B5n1gFJ3+2aMrYB>I%JEtlzhffW_XlH5b>=ze-VpGHCvljTu)Ku<>8UtIoBB`ypIhf<^xI zQY_wwN^am_Rg|zQAiPWwE+rxBGzqK8u>@Bl#Cy(+{JxqiQC^w7QPwby<)?j;8h)4i zi?tLE6u~FJO1zCv^s?M$ae>Urxc1s4R`reJ?QIRuqV62-RhQBi&@h1}Ue--u{o`2r z3^uFvGq?#D!_6fVxJ|T+jzZdw3GCV&YOD^8p}RVC4ja_n-V=zntEaI4)O5C^=@o>V z+fQI&Q}dGuH#Lvp&Ze{NO=E~FwvCmC$u&6!t9Js<^B564@V#TWx4p6Q!z8kL8!I35 zuMLwp*4H?Who8eElQ`Z-5I)&KZjYWO?48(yergwk%@42v_wke9n;JgdMjL=LY8Y#= z2C4<5L-;>xnqBoe z3Cc7G-fB4D|Msb;1OG;38!r_Vuak92mO{N?5f}Ihp5(XkiVGA*8MbK>PxUoco@^e+ z*EWX;>Lb;mNu26ys1A+eTVr^p_XHxFL&_{;r@0qYrIwW@?kssI zC(XY5E>{#7{6}3v!+1F-o$ulMfnwYFQi)kLiI@5&A?UR~7{|GWG5n}^+WQr@I- zp9QeisK*rlxC>+~7Dk4IxIRsKBHqdR9b3+fyL=ynHmIDe&|>O*VlvO+%z5;9Z$|DJ zb4dO}-R=MKr^6EKJiOrJdLnCJn>np?~vU-1sSFgPu;pthGwf}bG z(1db%xwr#x)r+`4AGu$j7~u2MpVs3VpLp|mx&;>`0p0vH6kF+D2CY0fVdQOZ@h;A` z{infNyvmFUiu*XG}RNMNwXrbec_*a3N=2zJ|Wh5z* z5rAX$JJR{#zP>KY**>xHTuw?|-Rg|o24V)74HcfVT;WtQHXlE+_4iPE8QE#DUm%x0 zEKr75ur~W%w#-My3Tj`hH6EuEW+8K-^5P62$7Sc5OK+22qj&Pd1;)1#4tKihi=~8C zHiQSst0cpri6%OeaR`PY>HH_;CPaRNty%WTm4{wDK8V6gCZlG@U3$~JQZ;HPvDJcT1V{ z?>H@13MJcCNe#5z+MecYNi@VT5|&UiN1D4ATT+%M+h4c$t;C#UAs3O_q=GxK0}8%8 z8J(_M9bayxN}69ex4dzM_P3oh@ZGREjVvn%%r7=xjkqxJP4kj}5tlf;QosR=%4L5y zWhgejO=vao5oX%mOHbhJ8V+SG&K5dABn6!WiKl{|oPkq(9z8l&Mm%(=qGcFzI=eLu zWc_oCLyf;hVlB@dnwY98?75B20=n$>u3b|NB28H0u-6Rpl((%KWEBOfElVWJx+5yg z#SGqwza7f}$z;n~g%4HDU{;V{gXIhft*q2=4zSezGK~nBgu9-Q*rZ#2f=Q}i2|qOp z!!y4p)4o=LVUNhlkp#JL{tfkhXNbB=Ox>M=n6soptJw-IDI|_$is2w}(XY>a=H52d z3zE$tjPUhWWS+5h=KVH&uqQS=$v3nRs&p$%11b%5qtF}S2#Pc`IiyBIF4%A!;AVoI zXU8-Rpv!DQNcF~(qQnyyMy=-AN~U>#&X1j5BLDP{?K!%h!;hfJI>$mdLSvktEr*89 zdJHvby^$xEX0^l9g$xW-d?J;L0#(`UT~zpL&*cEh$L|HPAu=P8`OQZV!-}l`noSp_ zQ-1$q$R-gDL)?6YaM!=8H=QGW$NT2SeZlb8PKJdc=F-cT@j7Xags+Pr*jPtlHFnf- zh?q<6;)27IdPc^Wdy-mX%2s84C1xZq9Xms+==F4);O`VUASmu3(RlgE#0+#giLh-& zcxm3_e}n4{%|X zJp{G_j+%`j_q5}k{eW&TlP}J2wtZ2^<^E(O)4OQX8FDp6RJq!F{(6eHWSD3=f~(h} zJXCf7=r<16X{pHkm%yzYI_=VDP&9bmI1*)YXZeB}F? z(%QsB5fo*FUZxK$oX~X^69;x~j7ms8xlzpt-T15e9}$4T-pC z6PFg@;B-j|Ywajpe4~bk#S6(fO^|mm1hKOPfA%8-_iGCfICE|=P_~e;Wz6my&)h_~ zkv&_xSAw7AZ%ThYF(4jADW4vg=oEdJGVOs>FqamoL3Np8>?!W#!R-0%2Bg4h?kz5I zKV-rKN2n(vUL%D<4oj@|`eJ>0i#TmYBtYmfla;c!ATW%;xGQ0*TW@PTlGG><@dxUI zg>+3SiGdZ%?5N=8uoLA|$4isK$aJ%i{hECP$bK{J#0W2gQ3YEa zZQ50Stn6hqdfxJ*9#NuSLwKFCUGk@c=(igyVL;;2^wi4o30YXSIb2g_ud$ zgpCr@H0qWtk2hK8Q|&wx)}4+hTYlf;$a4#oUM=V@Cw#!$(nOFFpZ;0lc!qd=c$S}Z zGGI-0jg~S~cgVT=4Vo)b)|4phjStD49*EqC)IPwyeKBLcN;Wu@Aeph;emROAwJ-0< z_#>wVm$)ygH|qyxZaet&(Vf%pVdnvKWJn9`%DAxj3ot;v>S$I}jJ$FLBF*~iZ!ZXE zkvui&p}fI0Y=IDX)mm0@tAd|fEHl~J&K}ZX(Mm3cm1UAuwJ42+AO5@HwYfDH7ipIc zmI;1J;J@+aCNG1M`Btf>YT>~c&3j~Qi@Py5JT6;zjx$cvOQW@3oQ>|}GH?TW-E z1R;q^QFjm5W~7f}c3Ww|awg1BAJ^slEV~Pk`Kd`PS$7;SqJZNj->it4DW2l15}xP6 zoCl$kyEF%yJni0(L!Z&14m!1urXh6Btj_5JYt1{#+H8w?5QI%% zo-$KYWNMJVH?Hh@1n7OSu~QhSswL8x0=$<8QG_zepi_`y_79=nK=_ZP_`Em2UI*tyQoB+r{1QYZCpb?2OrgUw#oRH$?^Tj!Req>XiE#~B|~ z+%HB;=ic+R@px4Ld8mwpY;W^A%8%l8$@B@1m5n`TlKI6bz2mp*^^^1mK$COW$HOfp zUGTz-cN9?BGEp}5A!mDFjaiWa2_J2Iq8qj0mXzk; z66JBKRP{p%wN7XobR0YjhAuW9T1Gw3FDvR5dWJ8ElNYF94eF3ebu+QwKjtvVu4L zI9ip#mQ@4uqVdkl-TUQMb^XBJVLW(-$s;Nq;@5gr4`UfLgF$adIhd?rHOa%D);whv z=;krPp~@I+-Z|r#s3yCH+c1US?dnm+C*)r{m+86sTJusLdNu^sqLrfWed^ndHXH`m zd3#cOe3>w-ga(Dus_^ppG9AC>Iq{y%%CK+Cro_sqLCs{VLuK=dev>OL1dis4(PQ5R zcz)>DjEkfV+MO;~>VUlYF00SgfUo~@(&9$Iy2|G0T9BSP?&T22>K46D zL*~j#yJ?)^*%J3!16f)@Y2Z^kS*BzwfAQ7K96rFRIh>#$*$_Io;z>ux@}G98!fWR@ zGTFxv4r~v)Gsd|pF91*-eaZ3Qw1MH$K^7JhWIdX%o$2kCbvGDXy)a?@8T&1dY4`;L z4Kn+f%SSFWE_rpEpL9bnlmYq`D!6F%di<&Hh=+!VI~j)2mfil03T#jJ_s?}VV0_hp z7T9bWxc>Jm2Z0WMU?`Z$xE74Gu~%s{mW!d4uvKCx@WD+gPUQ zV0vQS(Ig++z=EHN)BR44*EDSWIyT~R4$FcF*VEY*8@l=218Q05D2$|fXKFhRgBIEE zdDFB}1dKkoO^7}{5crKX!p?dZWNz$m>1icsXG2N+((x0OIST9Zo^DW_tytvlwXGpn zs8?pJXjEG;T@qrZi%#h93?FP$!&P4JA(&H61tqQi=opRzNpm zkrG}$^t9&XduK*Qa1?355wd8G2CI6QEh@Ua>AsD;7oRUNLPb76m4HG3K?)wF~IyS3`fXuNM>${?wmB zpVz;?6_(Fiadfd{vUCBM*_kt$+F3J+IojI;9L(gc9n3{sEZyzR9o!_mOwFC#tQ{Q~ zP3-`#uK#tP3Q7~Q;4H|wjZHO8h7e4IuBxl&vz2w~D8)w=Wtg31zpZhz%+kzSzL*dV zwp@{WU4i;hJ7c2f1O;7Mz6qRKeASoIv0_bV=i@NMG*l<#+;INk-^`5w@}Dj~;k=|}qM1vq_P z|GpBGe_IKq|LNy9SJhKOQ$c=5L{Dv|Q_lZl=-ky*BFBJLW9&y_C|!vyM~rQx=!vun z?rZJQB5t}Dctmui5i31C_;_}CEn}_W%>oSXtt>@kE1=JW*4*v4tPp;O6 zmAk{)m!)}34pTWg8{i>($%NQ(Tl;QC@J@FfBoc%Gr&m560^kgSfodAFrIjF}aIw)X zoXZ`@IsMkc8_=w%-7`D6Y4e*CG8k%Ud=GXhsTR50jUnm+R*0A(O3UKFg0`K;qp1bl z7``HN=?39ic_kR|^R^~w-*pa?Vj#7|e9F1iRx{GN2?wK!xR1GW!qa=~pjJb-#u1K8 zeR?Y2i-pt}yJq;SCiVHODIvQJX|ZJaT8nO+(?HXbLefulKKgM^B(UIO1r+S=7;kLJ zcH}1J=Px2jsh3Tec&v8Jcbng8;V-`#*UHt?hB(pmOipKwf3Lz8rG$heEB30Sg*2rx zV<|KN86$soN(I!BwO`1n^^uF2*x&vJ$2d$>+`(romzHP|)K_KkO6Hc>_dwMW-M(#S zK(~SiXT1@fvc#U+?|?PniDRm01)f^#55;nhM|wi?oG>yBsa?~?^xTU|fX-R(sTA+5 zaq}-8Tx7zrOy#3*JLIIVsBmHYLdD}!0NP!+ITW+Thn0)8SS!$@)HXwB3tY!fMxc#1 zMp3H?q3eD?u&Njx4;KQ5G>32+GRp1Ee5qMO0lZjaRRu&{W<&~DoJNGkcYF<5(Ab+J zgO>VhBl{okDPn78<%&e2mR{jwVCz5Og;*Z;;3%VvoGo_;HaGLWYF7q#jDX=Z#Ml`H z858YVV$%J|e<1n`%6Vsvq7GmnAV0wW4$5qQ3uR@1i>tW{xrl|ExywIc?fNgYlA?C5 zh$ezAFb5{rQu6i7BSS5*J-|9DQ{6^BVQ{b*lq`xS@RyrsJN?-t=MTMPY;WYeKBCNg z^2|pN!Q^WPJuuO4!|P@jzt&tY1Y8d%FNK5xK(!@`jO2aEA*4 zkO6b|UVBipci?){-Ke=+1;mGlND8)6+P;8sq}UXw2hn;fc7nM>g}GSMWu&v&fqh

iViYT=fZ(|3Ox^$aWPp4a8h24tD<|8-!aK0lHgL$N7Efw}J zVIB!7=T$U`ao1?upi5V4Et*-lTG0XvExbf!ya{cua==$WJyVG(CmA6Of*8E@DSE%L z`V^$qz&RU$7G5mg;8;=#`@rRG`-uS18$0WPN@!v2d{H2sOqP|!(cQ@ zUHo!d>>yFArLPf1q`uBvY32miqShLT1B@gDL4XoVTK&@owOoD)OIHXrYK-a1d$B{v zF^}8D3Y^g%^cnvScOSJR5QNH+BI%d|;J;wWM3~l>${fb8DNPg)wrf|GBP8p%LNGN# z3EaIiItgwtGgT&iYCFy9-LG}bMI|4LdmmJt@V@% zb6B)1kc=T)(|L@0;wr<>=?r04N;E&ef+7C^`wPWtyQe(*pD1pI_&XHy|0gIGHMekd zF_*M4yi6J&Z4LQj65)S zXwdM{SwUo%3SbPwFsHgqF@V|6afT|R6?&S;lw=8% z3}@9B=#JI3@B*#4s!O))~z zc>2_4Q_#&+5V`GFd?88^;c1i7;Vv_I*qt!_Yx*n=;rj!82rrR2rQ8u5(Ejlo{15P% zs~!{%XJ>FmJ})H^I9bn^Re&38H{xA!0l3^89k(oU;bZWXM@kn$#aoS&Y4l^-WEn-fH39Jb9lA%s*WsKJQl?n9B7_~P z-XM&WL7Z!PcoF6_D>V@$CvUIEy=+Z&0kt{szMk=f1|M+r*a43^$$B^MidrT0J;RI` z(?f!O<8UZkm$_Ny$Hth1J#^4ni+im8M9mr&k|3cIgwvjAgjH z8`N&h25xV#v*d$qBX5jkI|xOhQn!>IYZK7l5#^P4M&twe9&Ey@@GxYMxBZq2e7?`q z$~Szs0!g{2fGcp9PZEt|rdQ6bhAgpcLHPz?f-vB?$dc*!9OL?Q8mn7->bFD2Si60* z!O%y)fCdMSV|lkF9w%x~J*A&srMyYY3{=&$}H zGQ4VG_?$2X(0|vT0{=;W$~icCI{b6W{B!Q8xdGhF|D{25G_5_+%s(46lhvNLkik~R z>nr(&C#5wwOzJZQo9m|U<;&Wk!_#q|V>fsmj1g<6%hB{jGoNUPjgJslld>xmODzGjYc?7JSuA?A_QzjDw5AsRgi@Y|Z0{F{!1=!NES-#*f^s4l0Hu zz468))2IY5dmD9pa*(yT5{EyP^G>@ZWumealS-*WeRcZ}B%gxq{MiJ|RyX-^C1V=0 z@iKdrGi1jTe8Ya^x7yyH$kBNvM4R~`fbPq$BzHum-3Zo8C6=KW@||>zsA8-Y9uV5V z#oq-f5L5}V<&wF4@X@<3^C%ptp6+Ce)~hGl`kwj)bsAjmo_GU^r940Z-|`<)oGnh7 zFF0Tde3>ui?8Yj{sF-Z@)yQd~CGZ*w-6p2U<8}JO-sRsVI5dBji`01W8A&3$?}lxBaC&vn0E$c5tW* zX>5(zzZ=qn&!J~KdsPl;P@bmA-Pr8T*)eh_+Dv5=Ma|XSle6t(k8qcgNyar{*ReQ8 zTXwi=8vr>!3Ywr+BhggHDw8ke==NTQVMCK`$69fhzEFB*4+H9LIvdt-#IbhZvpS}} zO3lz;P?zr0*0$%-Rq_y^k(?I{Mk}h@w}cZpMUp|ucs55bcloL2)($u%mXQw({Wzc~ z;6nu5MkjP)0C(@%6Q_I_vsWrfhl7Zpoxw#WoE~r&GOSCz;_ro6i(^hM>I$8y>`!wW z*U^@?B!MMmb89I}2(hcE4zN2G^kwyWCZp5JG>$Ez7zP~D=J^LMjSM)27_0B_X^C(M z`fFT+%DcKlu?^)FCK>QzSnV%IsXVcUFhFdBP!6~se&xxrIxsvySAWu++IrH;FbcY$ z2DWTvSBRfLwdhr0nMx+URA$j3i7_*6BWv#DXfym?ZRDcX9C?cY9sD3q)uBDR3uWg= z(lUIzB)G$Hr!){>E{s4Dew+tb9kvToZp-1&c?y2wn@Z~(VBhqz`cB;{E4(P3N2*nJ z_>~g@;UF2iG{Kt(<1PyePTKahF8<)pozZ*xH~U-kfoAayCwJViIrnqwqO}7{0pHw$ zs2Kx?s#vQr7XZ264>5RNKSL8|Ty^=PsIx^}QqOOcfpGUU4tRkUc|kc7-!Ae6!+B{o~7nFpm3|G5^=0#Bnm6`V}oSQlrX(u%OWnC zoLPy&Q;1Jui&7ST0~#+}I^&?vcE*t47~Xq#YwvA^6^} z`WkC)$AkNub|t@S!$8CBlwbV~?yp&@9h{D|3z-vJXgzRC5^nYm+PyPcgRzAnEi6Q^gslXYRv4nycsy-SJu?lMps-? zV`U*#WnFsdPLL)Q$AmD|0`UaC4ND07+&UmOu!eHruzV|OUox<+Jl|Mr@6~C`T@P%s zW7sgXLF2SSe9Fl^O(I*{9wsFSYb2l%-;&Pi^dpv!{)C3d0AlNY6!4fgmSgj_wQ*7Am7&$z;Jg&wgR-Ih;lUvWS|KTSg!&s_E9_bXBkZvGiC6bFKDWZxsD$*NZ#_8bl zG1P-#@?OQzED7@jlMJTH@V!6k;W>auvft)}g zhoV{7$q=*;=l{O>Q4a@ ziMjf_u*o^PsO)#BjC%0^h>Xp@;5$p{JSYDt)zbb}s{Kbt!T*I@Pk@X0zds6wsefuU zW$XY%yyRGC94=6mf?x+bbA5CDQ2AgW1T-jVAJbm7K(gp+;v6E0WI#kuACgV$r}6L? zd|Tj?^%^*N&b>Dd{Wr$FS2qI#Ucs1yd4N+RBUQiSZGujH`#I)mG&VKoDh=KKFl4=G z&MagXl6*<)$6P}*Tiebpz5L=oMaPrN+caUXRJ`D?=K9!e0f{@D&cZLKN?iNP@X0aF zE(^pl+;*T5qt?1jRC=5PMgV!XNITRLS_=9{CJExaQj;lt!&pdzpK?8p>%Mb+D z?yO*uSung=-`QQ@yX@Hyd4@CI^r{2oiu`%^bNkz+Nkk!IunjwNC|WcqvX~k=><-I3 zDQdbdb|!v+Iz01$w@aMl!R)koD77Xp;eZwzSl-AT zr@Vu{=xvgfq9akRrrM)}=!=xcs+U1JO}{t(avgz`6RqiiX<|hGG1pmop8k6Q+G_mv zJv|RfDheUp2L3=^C=4aCBMBn0aRCU(DQwX-W(RkRwmLeuJYF<0urcaf(=7)JPg<3P zQs!~G)9CT18o!J4{zX{_e}4eS)U-E)0FAt}wEI(c0%HkxgggW;(1E=>J17_hsH^sP z%lT0LGgbUXHx-K*CI-MCrP66UP0PvGqM$MkeLyqHdbgP|_Cm!7te~b8p+e6sQ_3k| zVcwTh6d83ltdnR>D^)BYQpDKlLk3g0Hdcgz2}%qUs9~~Rie)A-BV1mS&naYai#xcZ z(d{8=-LVpTp}2*y)|gR~;qc7fp26}lPcLZ#=JpYcn3AT9(UIdOyg+d(P5T7D&*P}# zQCYplZO5|7+r19%9e`v^vfSS1sbX1c%=w1;oyruXB%Kl$ACgKQ6=qNWLsc=28xJjg zwvsI5-%SGU|3p>&zXVl^vVtQT3o-#$UT9LI@Npz~6=4!>mc431VRNN8od&Ul^+G_kHC`G=6WVWM z%9eWNyy(FTO|A+@x}Ou3CH)oi;t#7rAxdIXfNFwOj_@Y&TGz6P_sqiB`Q6Lxy|Q{`|fgmRG(k+!#b*M+Z9zFce)f-7;?Km5O=LHV9f9_87; zF7%R2B+$?@sH&&-$@tzaPYkw0;=i|;vWdI|Wl3q_Zu>l;XdIw2FjV=;Mq5t1Q0|f< zs08j54Bp`3RzqE=2enlkZxmX6OF+@|2<)A^RNQpBd6o@OXl+i)zO%D4iGiQNuXd+zIR{_lb96{lc~bxsBveIw6umhShTX+3@ZJ=YHh@ zWY3(d0azg;7oHn>H<>?4@*RQbi>SmM=JrHvIG(~BrvI)#W(EAeO6fS+}mxxcc+X~W6&YVl86W9WFSS}Vz-f9vS?XUDBk)3TcF z8V?$4Q)`uKFq>xT=)Y9mMFVTUk*NIA!0$?RP6Ig0TBmUFrq*Q-Agq~DzxjStQyJ({ zBeZ;o5qUUKg=4Hypm|}>>L=XKsZ!F$yNTDO)jt4H0gdQ5$f|d&bnVCMMXhNh)~mN z@_UV6D7MVlsWz+zM+inZZp&P4fj=tm6fX)SG5H>OsQf_I8c~uGCig$GzuwViK54bcgL;VN|FnyQl>Ed7(@>=8$a_UKIz|V6CeVSd2(P z0Uu>A8A+muM%HLFJQ9UZ5c)BSAv_zH#1f02x?h9C}@pN@6{>UiAp>({Fn(T9Q8B z^`zB;kJ5b`>%dLm+Ol}ty!3;8f1XDSVX0AUe5P#@I+FQ-`$(a;zNgz)4x5hz$Hfbg z!Q(z26wHLXko(1`;(BAOg_wShpX0ixfWq3ponndY+u%1gyX)_h=v1zR#V}#q{au6; z!3K=7fQwnRfg6FXtNQmP>`<;!N137paFS%y?;lb1@BEdbvQHYC{976l`cLqn;b8lp zIDY>~m{gDj(wfnK!lpW6pli)HyLEiUrNc%eXTil|F2s(AY+LW5hkKb>TQ3|Q4S9rr zpDs4uK_co6XPsn_z$LeS{K4jFF`2>U`tbgKdyDne`xmR<@6AA+_hPNKCOR-Zqv;xk zu5!HsBUb^!4uJ7v0RuH-7?l?}b=w5lzzXJ~gZcxRKOovSk@|#V+MuX%Y+=;14i*%{)_gSW9(#4%)AV#3__kac1|qUy!uyP{>?U#5wYNq}y$S9pCc zFc~4mgSC*G~j0u#qqp9 z${>3HV~@->GqEhr_Xwoxq?Hjn#=s2;i~g^&Hn|aDKpA>Oc%HlW(KA1?BXqpxB;Ydx)w;2z^MpjJ(Qi(X!$5RC z*P{~%JGDQqojV>2JbEeCE*OEu!$XJ>bWA9Oa_Hd;y)F%MhBRi*LPcdqR8X`NQ&1L# z5#9L*@qxrx8n}LfeB^J{%-?SU{FCwiWyHp682F+|pa+CQa3ZLzBqN1{)h4d6+vBbV zC#NEbQLC;}me3eeYnOG*nXOJZEU$xLZ1<1Y=7r0(-U0P6-AqwMAM`a(Ed#7vJkn6plb4eI4?2y3yOTGmmDQ!z9`wzbf z_OY#0@5=bnep;MV0X_;;SJJWEf^E6Bd^tVJ9znWx&Ks8t*B>AM@?;D4oWUGc z!H*`6d7Cxo6VuyS4Eye&L1ZRhrRmN6Lr`{NL(wDbif|y&z)JN>Fl5#Wi&mMIr5i;x zBx}3YfF>>8EC(fYnmpu~)CYHuHCyr5*`ECap%t@y=jD>!_%3iiE|LN$mK9>- zHdtpy8fGZtkZF?%TW~29JIAfi2jZT8>OA7=h;8T{{k?c2`nCEx9$r zS+*&vt~2o^^J+}RDG@+9&M^K*z4p{5#IEVbz`1%`m5c2};aGt=V?~vIM}ZdPECDI)47|CWBCfDWUbxBCnmYivQ*0Nu_xb*C>~C9(VjHM zxe<*D<#dQ8TlpMX2c@M<9$w!RP$hpG4cs%AI){jp*Sj|*`m)5(Bw*A0$*i-(CA5#%>a)$+jI2C9r6|(>J8InryENI z$NohnxDUB;wAYDwrb*!N3noBTKPpPN}~09SEL18tkG zxgz(RYU_;DPT{l?Q$+eaZaxnsWCA^ds^0PVRkIM%bOd|G2IEBBiz{&^JtNsODs;5z zICt_Zj8wo^KT$7Bg4H+y!Df#3mbl%%?|EXe!&(Vmac1DJ*y~3+kRKAD=Ovde4^^%~ zw<9av18HLyrf*_>Slp;^i`Uy~`mvBjZ|?Ad63yQa#YK`4+c6;pW4?XIY9G1(Xh9WO8{F-Aju+nS9Vmv=$Ac0ienZ+p9*O%NG zMZKy5?%Z6TAJTE?o5vEr0r>f>hb#2w2U3DL64*au_@P!J!TL`oH2r*{>ffu6|A7tv zL4juf$DZ1MW5ZPsG!5)`k8d8c$J$o;%EIL0va9&GzWvkS%ZsGb#S(?{!UFOZ9<$a| zY|a+5kmD5N&{vRqkgY>aHsBT&`rg|&kezoD)gP0fsNYHsO#TRc_$n6Lf1Z{?+DLziXlHrq4sf(!>O{?Tj;Eh@%)+nRE_2VxbN&&%%caU#JDU%vL3}Cb zsb4AazPI{>8H&d=jUaZDS$-0^AxE@utGs;-Ez_F(qC9T=UZX=>ok2k2 ziTn{K?y~a5reD2A)P${NoI^>JXn>`IeArow(41c-Wm~)wiryEP(OS{YXWi7;%dG9v zI?mwu1MxD{yp_rrk!j^cKM)dc4@p4Ezyo%lRN|XyD}}>v=Xoib0gOcdXrQ^*61HNj z=NP|pd>@yfvr-=m{8$3A8TQGMTE7g=z!%yt`8`Bk-0MMwW~h^++;qyUP!J~ykh1GO z(FZ59xuFR$(WE;F@UUyE@Sp>`aVNjyj=Ty>_Vo}xf`e7`F;j-IgL5`1~-#70$9_=uBMq!2&1l zomRgpD58@)YYfvLtPW}{C5B35R;ZVvB<<#)x%srmc_S=A7F@DW8>QOEGwD6suhwCg z>Pa+YyULhmw%BA*4yjDp|2{!T98~<6Yfd(wo1mQ!KWwq0eg+6)o1>W~f~kL<-S+P@$wx*zeI|1t7z#Sxr5 zt6w+;YblPQNplq4Z#T$GLX#j6yldXAqj>4gAnnWtBICUnA&-dtnlh=t0Ho_vEKwV` z)DlJi#!@nkYV#$!)@>udAU*hF?V`2$Hf=V&6PP_|r#Iv*J$9)pF@X3`k;5})9^o4y z&)~?EjX5yX12O(BsFy-l6}nYeuKkiq`u9145&3Ssg^y{5G3Pse z9w(YVa0)N-fLaBq1`P!_#>SS(8fh_5!f{UrgZ~uEdeMJIz7DzI5!NHHqQtm~#CPij z?=N|J>nPR6_sL7!f4hD_|KH`vf8(Wpnj-(gPWH+ZvID}%?~68SwhPTC3u1_cB`otq z)U?6qo!ZLi5b>*KnYHWW=3F!p%h1;h{L&(Q&{qY6)_qxNfbP6E3yYpW!EO+IW3?@J z);4>g4gnl^8klu7uA>eGF6rIGSynacogr)KUwE_R4E5Xzi*Qir@b-jy55-JPC8c~( zo!W8y9OGZ&`xmc8;=4-U9=h{vCqfCNzYirONmGbRQlR`WWlgnY+1wCXbMz&NT~9*| z6@FrzP!LX&{no2!Ln_3|I==_4`@}V?4a;YZKTdw;vT<+K+z=uWbW(&bXEaWJ^W8Td z-3&1bY^Z*oM<=M}LVt>_j+p=2Iu7pZmbXrhQ_k)ysE9yXKygFNw$5hwDn(M>H+e1&9BM5!|81vd%r%vEm zqxY3?F@fb6O#5UunwgAHR9jp_W2zZ}NGp2%mTW@(hz7$^+a`A?mb8|_G*GNMJ) zjqegXQio=i@AINre&%ofexAr95aop5C+0MZ0m-l=MeO8m3epm7U%vZB8+I+C*iNFM z#T3l`gknX;D$-`2XT^Cg*vrv=RH+P;_dfF++cP?B_msQI4j+lt&rX2)3GaJx%W*Nn zkML%D{z5tpHH=dksQ*gzc|}gzW;lwAbxoR07VNgS*-c3d&8J|;@3t^ zVUz*J*&r7DFRuFVDCJDK8V9NN5hvpgGjwx+5n)qa;YCKe8TKtdnh{I7NU9BCN!0dq zczrBk8pE{{@vJa9ywR@mq*J=v+PG;?fwqlJVhijG!3VmIKs>9T6r7MJpC)m!Tc#>g zMtVsU>wbwFJEfwZ{vB|ZlttNe83)$iz`~#8UJ^r)lJ@HA&G#}W&ZH*;k{=TavpjWE z7hdyLZPf*X%Gm}i`Y{OGeeu^~nB8=`{r#TUrM-`;1cBvEd#d!kPqIgYySYhN-*1;L z^byj%Yi}Gx)Wnkosi337BKs}+5H5dth1JA{Ir-JKN$7zC)*}hqeoD(WfaUDPT>0`- z(6sa0AoIqASwF`>hP}^|)a_j2s^PQn*qVC{Q}htR z5-)duBFXT_V56-+UohKXlq~^6uf!6sA#ttk1o~*QEy_Y-S$gAvq47J9Vtk$5oA$Ct zYhYJ@8{hsC^98${!#Ho?4y5MCa7iGnfz}b9jE~h%EAAv~Qxu)_rAV;^cygV~5r_~?l=B`zObj7S=H=~$W zPtI_m%g$`kL_fVUk9J@>EiBH zOO&jtn~&`hIFMS5S`g8w94R4H40mdNUH4W@@XQk1sr17b{@y|JB*G9z1|CrQjd+GX z6+KyURG3;!*BQrentw{B2R&@2&`2}n(z-2&X7#r!{yg@Soy}cRD~j zj9@UBW+N|4HW4AWapy4wfUI- zZ`gSL6DUlgj*f1hSOGXG0IVH8HxK?o2|3HZ;KW{K+yPAlxtb)NV_2AwJm|E)FRs&& z=c^e7bvUsztY|+f^k7NXs$o1EUq>cR7C0$UKi6IooHWlK_#?IWDkvywnzg&ThWo^? z2O_N{5X39#?eV9l)xI(>@!vSB{DLt*oY!K1R8}_?%+0^C{d9a%N4 zoxHVT1&Lm|uDX%$QrBun5e-F`HJ^T$ zmzv)p@4ZHd_w9!%Hf9UYNvGCw2TTTbrj9pl+T9%-_-}L(tES>Or-}Z4F*{##n3~L~TuxjirGuIY#H7{%$E${?p{Q01 zi6T`n;rbK1yIB9jmQNycD~yZq&mbIsFWHo|ZAChSFPQa<(%d8mGw*V3fh|yFoxOOiWJd(qvVb!Z$b88cg->N=qO*4k~6;R==|9ihg&riu#P~s4Oap9O7f%crSr^rljeIfXDEg>wi)&v*a%7zpz<9w z*r!3q9J|390x`Zk;g$&OeN&ctp)VKRpDSV@kU2Q>jtok($Y-*x8_$2piTxun81@vt z!Vj?COa0fg2RPXMSIo26T=~0d`{oGP*eV+$!0I<(4azk&Vj3SiG=Q!6mX0p$z7I}; z9BJUFgT-K9MQQ-0@Z=^7R<{bn2Fm48endsSs`V7_@%8?Bxkqv>BDoVcj?K#dV#uUP zL1ND~?D-|VGKe3Rw_7-Idpht>H6XRLh*U7epS6byiGvJpr%d}XwfusjH9g;Z98H`x zyde%%5mhGOiL4wljCaWCk-&uE4_OOccb9c!ZaWt4B(wYl!?vyzl%7n~QepN&eFUrw zFIOl9c({``6~QD+43*_tzP{f2x41h(?b43^y6=iwyB)2os5hBE!@YUS5?N_tXd=h( z)WE286Fbd>R4M^P{!G)f;h<3Q>Fipuy+d2q-)!RyTgt;wr$(?9ox3;q+{E*ZQHhOn;lM`cjnu9 zXa48ks-v(~b*;MAI<>YZH(^NV8vjb34beE<_cwKlJoR;k6lJNSP6v}uiyRD?|0w+X@o1ONrH8a$fCxXpf? z?$DL0)7|X}Oc%h^zrMKWc-NS9I0Utu@>*j}b@tJ=ixQSJ={4@854wzW@E>VSL+Y{i z#0b=WpbCZS>kUCO_iQz)LoE>P5LIG-hv9E+oG}DtlIDF>$tJ1aw9^LuhLEHt?BCj& z(O4I8v1s#HUi5A>nIS-JK{v!7dJx)^Yg%XjNmlkWAq2*cv#tHgz`Y(bETc6CuO1VkN^L-L3j_x<4NqYb5rzrLC-7uOv z!5e`GZt%B782C5-fGnn*GhDF$%(qP<74Z}3xx+{$4cYKy2ikxI7B2N+2r07DN;|-T->nU&!=Cm#rZt%O_5c&1Z%nlWq3TKAW0w zQqemZw_ue--2uKQsx+niCUou?HjD`xhEjjQd3%rrBi82crq*~#uA4+>vR<_S{~5ce z-2EIl?~s z1=GVL{NxP1N3%=AOaC}j_Fv=ur&THz zyO!d9kHq|c73kpq`$+t+8Bw7MgeR5~`d7ChYyGCBWSteTB>8WAU(NPYt2Dk`@#+}= zI4SvLlyk#pBgVigEe`?NG*vl7V6m+<}%FwPV=~PvvA)=#ths==DRTDEYh4V5}Cf$z@#;< zyWfLY_5sP$gc3LLl2x+Ii)#b2nhNXJ{R~vk`s5U7Nyu^3yFg&D%Txwj6QezMX`V(x z=C`{76*mNb!qHHs)#GgGZ_7|vkt9izl_&PBrsu@}L`X{95-2jf99K)0=*N)VxBX2q z((vkpP2RneSIiIUEnGb?VqbMb=Zia+rF~+iqslydE34cSLJ&BJW^3knX@M;t*b=EA zNvGzv41Ld_T+WT#XjDB840vovUU^FtN_)G}7v)1lPetgpEK9YS^OWFkPoE{ovj^=@ zO9N$S=G$1ecndT_=5ehth2Lmd1II-PuT~C9`XVePw$y8J#dpZ?Tss<6wtVglm(Ok7 z3?^oi@pPio6l&!z8JY(pJvG=*pI?GIOu}e^EB6QYk$#FJQ%^AIK$I4epJ+9t?KjqA+bkj&PQ*|vLttme+`9G=L% ziadyMw_7-M)hS(3E$QGNCu|o23|%O+VN7;Qggp?PB3K-iSeBa2b}V4_wY`G1Jsfz4 z9|SdB^;|I8E8gWqHKx!vj_@SMY^hLEIbSMCuE?WKq=c2mJK z8LoG-pnY!uhqFv&L?yEuxo{dpMTsmCn)95xanqBrNPTgXP((H$9N${Ow~Is-FBg%h z53;|Y5$MUN)9W2HBe2TD`ct^LHI<(xWrw}$qSoei?}s)&w$;&!14w6B6>Yr6Y8b)S z0r71`WmAvJJ`1h&poLftLUS6Ir zC$bG9!Im_4Zjse)#K=oJM9mHW1{%l8sz$1o?ltdKlLTxWWPB>Vk22czVt|1%^wnN@*!l)}?EgtvhC>vlHm^t+ogpgHI1_$1ox9e;>0!+b(tBrmXRB`PY1vp-R**8N7 zGP|QqI$m(Rdu#=(?!(N}G9QhQ%o!aXE=aN{&wtGP8|_qh+7a_j_sU5|J^)vxq;# zjvzLn%_QPHZZIWu1&mRAj;Sa_97p_lLq_{~j!M9N^1yp3U_SxRqK&JnR%6VI#^E12 z>CdOVI^_9aPK2eZ4h&^{pQs}xsijXgFYRIxJ~N7&BB9jUR1fm!(xl)mvy|3e6-B3j zJn#ajL;bFTYJ2+Q)tDjx=3IklO@Q+FFM}6UJr6km7hj7th9n_&JR7fnqC!hTZoM~T zBeaVFp%)0cbPhejX<8pf5HyRUj2>aXnXBqDJe73~J%P(2C?-RT{c3NjE`)om! zl$uewSgWkE66$Kb34+QZZvRn`fob~Cl9=cRk@Es}KQm=?E~CE%spXaMO6YmrMl%9Q zlA3Q$3|L1QJ4?->UjT&CBd!~ru{Ih^in&JXO=|<6J!&qp zRe*OZ*cj5bHYlz!!~iEKcuE|;U4vN1rk$xq6>bUWD*u(V@8sG^7>kVuo(QL@Ki;yL zWC!FT(q{E8#on>%1iAS0HMZDJg{Z{^!De(vSIq&;1$+b)oRMwA3nc3mdTSG#3uYO_ z>+x;7p4I;uHz?ZB>dA-BKl+t-3IB!jBRgdvAbW!aJ(Q{aT>+iz?91`C-xbe)IBoND z9_Xth{6?(y3rddwY$GD65IT#f3<(0o#`di{sh2gm{dw*#-Vnc3r=4==&PU^hCv$qd zjw;>i&?L*Wq#TxG$mFIUf>eK+170KG;~+o&1;Tom9}}mKo23KwdEM6UonXgc z!6N(@k8q@HPw{O8O!lAyi{rZv|DpgfU{py+j(X_cwpKqcalcqKIr0kM^%Br3SdeD> zHSKV94Yxw;pjzDHo!Q?8^0bb%L|wC;4U^9I#pd5O&eexX+Im{ z?jKnCcsE|H?{uGMqVie_C~w7GX)kYGWAg%-?8|N_1#W-|4F)3YTDC+QSq1s!DnOML3@d`mG%o2YbYd#jww|jD$gotpa)kntakp#K;+yo-_ZF9qrNZw<%#C zuPE@#3RocLgPyiBZ+R_-FJ_$xP!RzWm|aN)S+{$LY9vvN+IW~Kf3TsEIvP+B9Mtm! zpfNNxObWQpLoaO&cJh5>%slZnHl_Q~(-Tfh!DMz(dTWld@LG1VRF`9`DYKhyNv z2pU|UZ$#_yUx_B_|MxUq^glT}O5Xt(Vm4Mr02><%C)@v;vPb@pT$*yzJ4aPc_FZ3z z3}PLoMBIM>q_9U2rl^sGhk1VUJ89=*?7|v`{!Z{6bqFMq(mYiA?%KbsI~JwuqVA9$H5vDE+VocjX+G^%bieqx->s;XWlKcuv(s%y%D5Xbc9+ zc(_2nYS1&^yL*ey664&4`IoOeDIig}y-E~_GS?m;D!xv5-xwz+G`5l6V+}CpeJDi^ z%4ed$qowm88=iYG+(`ld5Uh&>Dgs4uPHSJ^TngXP_V6fPyl~>2bhi20QB%lSd#yYn zO05?KT1z@?^-bqO8Cg`;ft>ilejsw@2%RR7;`$Vs;FmO(Yr3Fp`pHGr@P2hC%QcA|X&N2Dn zYf`MqXdHi%cGR@%y7Rg7?d3?an){s$zA{!H;Ie5exE#c~@NhQUFG8V=SQh%UxUeiV zd7#UcYqD=lk-}sEwlpu&H^T_V0{#G?lZMxL7ih_&{(g)MWBnCZxtXg znr#}>U^6!jA%e}@Gj49LWG@*&t0V>Cxc3?oO7LSG%~)Y5}f7vqUUnQ;STjdDU}P9IF9d9<$;=QaXc zL1^X7>fa^jHBu_}9}J~#-oz3Oq^JmGR#?GO7b9a(=R@fw@}Q{{@`Wy1vIQ#Bw?>@X z-_RGG@wt|%u`XUc%W{J z>iSeiz8C3H7@St3mOr_mU+&bL#Uif;+Xw-aZdNYUpdf>Rvu0i0t6k*}vwU`XNO2he z%miH|1tQ8~ZK!zmL&wa3E;l?!!XzgV#%PMVU!0xrDsNNZUWKlbiOjzH-1Uoxm8E#r`#2Sz;-o&qcqB zC-O_R{QGuynW14@)7&@yw1U}uP(1cov)twxeLus0s|7ayrtT8c#`&2~Fiu2=R;1_4bCaD=*E@cYI>7YSnt)nQc zohw5CsK%m?8Ack)qNx`W0_v$5S}nO|(V|RZKBD+btO?JXe|~^Qqur%@eO~<8-L^9d z=GA3-V14ng9L29~XJ>a5k~xT2152zLhM*@zlp2P5Eu}bywkcqR;ISbas&#T#;HZSf z2m69qTV(V@EkY(1Dk3`}j)JMo%ZVJ*5eB zYOjIisi+igK0#yW*gBGj?@I{~mUOvRFQR^pJbEbzFxTubnrw(Muk%}jI+vXmJ;{Q6 zrSobKD>T%}jV4Ub?L1+MGOD~0Ir%-`iTnWZN^~YPrcP5y3VMAzQ+&en^VzKEb$K!Q z<7Dbg&DNXuow*eD5yMr+#08nF!;%4vGrJI++5HdCFcGLfMW!KS*Oi@=7hFwDG!h2< zPunUEAF+HncQkbfFj&pbzp|MU*~60Z(|Ik%Tn{BXMN!hZOosNIseT?R;A`W?=d?5X zK(FB=9mZusYahp|K-wyb={rOpdn=@;4YI2W0EcbMKyo~-#^?h`BA9~o285%oY zfifCh5Lk$SY@|2A@a!T2V+{^!psQkx4?x0HSV`(w9{l75QxMk!)U52Lbhn{8ol?S) zCKo*7R(z!uk<6*qO=wh!Pul{(qq6g6xW;X68GI_CXp`XwO zxuSgPRAtM8K7}5E#-GM!*ydOOG_{A{)hkCII<|2=ma*71ci_-}VPARm3crFQjLYV! z9zbz82$|l01mv`$WahE2$=fAGWkd^X2kY(J7iz}WGS z@%MyBEO=A?HB9=^?nX`@nh;7;laAjs+fbo!|K^mE!tOB>$2a_O0y-*uaIn8k^6Y zSbuv;5~##*4Y~+y7Z5O*3w4qgI5V^17u*ZeupVGH^nM&$qmAk|anf*>r zWc5CV;-JY-Z@Uq1Irpb^O`L_7AGiqd*YpGUShb==os$uN3yYvb`wm6d=?T*it&pDk zo`vhw)RZX|91^^Wa_ti2zBFyWy4cJu#g)_S6~jT}CC{DJ_kKpT`$oAL%b^!2M;JgT zM3ZNbUB?}kP(*YYvXDIH8^7LUxz5oE%kMhF!rnPqv!GiY0o}NR$OD=ITDo9r%4E>E0Y^R(rS^~XjWyVI6 zMOR5rPXhTp*G*M&X#NTL`Hu*R+u*QNoiOKg4CtNPrjgH>c?Hi4MUG#I917fx**+pJfOo!zFM&*da&G_x)L(`k&TPI*t3e^{crd zX<4I$5nBQ8Ax_lmNRa~E*zS-R0sxkz`|>7q_?*e%7bxqNm3_eRG#1ae3gtV9!fQpY z+!^a38o4ZGy9!J5sylDxZTx$JmG!wg7;>&5H1)>f4dXj;B+@6tMlL=)cLl={jLMxY zbbf1ax3S4>bwB9-$;SN2?+GULu;UA-35;VY*^9Blx)Jwyb$=U!D>HhB&=jSsd^6yw zL)?a|>GxU!W}ocTC(?-%z3!IUhw^uzc`Vz_g>-tv)(XA#JK^)ZnC|l1`@CdX1@|!| z_9gQ)7uOf?cR@KDp97*>6X|;t@Y`k_N@)aH7gY27)COv^P3ya9I{4z~vUjLR9~z1Z z5=G{mVtKH*&$*t0@}-i_v|3B$AHHYale7>E+jP`ClqG%L{u;*ff_h@)al?RuL7tOO z->;I}>%WI{;vbLP3VIQ^iA$4wl6@0sDj|~112Y4OFjMs`13!$JGkp%b&E8QzJw_L5 zOnw9joc0^;O%OpF$Qp)W1HI!$4BaXX84`%@#^dk^hFp^pQ@rx4g(8Xjy#!X%+X5Jd@fs3amGT`}mhq#L97R>OwT5-m|h#yT_-v@(k$q7P*9X~T*3)LTdzP!*B} z+SldbVWrrwQo9wX*%FyK+sRXTa@O?WM^FGWOE?S`R(0P{<6p#f?0NJvnBia?k^fX2 zNQs7K-?EijgHJY}&zsr;qJ<*PCZUd*x|dD=IQPUK_nn)@X4KWtqoJNHkT?ZWL_hF? zS8lp2(q>;RXR|F;1O}EE#}gCrY~#n^O`_I&?&z5~7N;zL0)3Tup`%)oHMK-^r$NT% zbFg|o?b9w(q@)6w5V%si<$!U<#}s#x@0aX-hP>zwS#9*75VXA4K*%gUc>+yzupTDBOKH8WR4V0pM(HrfbQ&eJ79>HdCvE=F z|J>s;;iDLB^3(9}?biKbxf1$lI!*Z%*0&8UUq}wMyPs_hclyQQi4;NUY+x2qy|0J; zhn8;5)4ED1oHwg+VZF|80<4MrL97tGGXc5Sw$wAI#|2*cvQ=jB5+{AjMiDHmhUC*a zlmiZ`LAuAn_}hftXh;`Kq0zblDk8?O-`tnilIh|;3lZp@F_osJUV9`*R29M?7H{Fy z`nfVEIDIWXmU&YW;NjU8)EJpXhxe5t+scf|VXM!^bBlwNh)~7|3?fWwo_~ZFk(22% zTMesYw+LNx3J-_|DM~`v93yXe=jPD{q;li;5PD?Dyk+b? zo21|XpT@)$BM$%F=P9J19Vi&1#{jM3!^Y&fr&_`toi`XB1!n>sbL%U9I5<7!@?t)~ z;&H%z>bAaQ4f$wIzkjH70;<8tpUoxzKrPhn#IQfS%9l5=Iu))^XC<58D!-O z{B+o5R^Z21H0T9JQ5gNJnqh#qH^na|z92=hONIM~@_iuOi|F>jBh-?aA20}Qx~EpDGElELNn~|7WRXRFnw+Wdo`|# zBpU=Cz3z%cUJ0mx_1($X<40XEIYz(`noWeO+x#yb_pwj6)R(__%@_Cf>txOQ74wSJ z0#F3(zWWaR-jMEY$7C*3HJrohc79>MCUu26mfYN)f4M~4gD`}EX4e}A!U}QV8!S47 z6y-U-%+h`1n`*pQuKE%Av0@)+wBZr9mH}@vH@i{v(m-6QK7Ncf17x_D=)32`FOjjo zg|^VPf5c6-!FxN{25dvVh#fog=NNpXz zfB$o+0jbRkHH{!TKhE709f+jI^$3#v1Nmf80w`@7-5$1Iv_`)W^px8P-({xwb;D0y z7LKDAHgX<84?l!I*Dvi2#D@oAE^J|g$3!)x1Ua;_;<@#l1fD}lqU2_tS^6Ht$1Wl} zBESo7o^)9-Tjuz$8YQSGhfs{BQV6zW7dA?0b(Dbt=UnQs&4zHfe_sj{RJ4uS-vQpC zX;Bbsuju4%!o8?&m4UZU@~ZZjeFF6ex2ss5_60_JS_|iNc+R0GIjH1@Z z=rLT9%B|WWgOrR7IiIwr2=T;Ne?30M!@{%Qf8o`!>=s<2CBpCK_TWc(DX51>e^xh8 z&@$^b6CgOd7KXQV&Y4%}_#uN*mbanXq(2=Nj`L7H7*k(6F8s6{FOw@(DzU`4-*77{ zF+dxpv}%mFpYK?>N_2*#Y?oB*qEKB}VoQ@bzm>ptmVS_EC(#}Lxxx730trt0G)#$b zE=wVvtqOct1%*9}U{q<)2?{+0TzZzP0jgf9*)arV)*e!f`|jgT{7_9iS@e)recI#z zbzolURQ+TOzE!ymqvBY7+5NnAbWxvMLsLTwEbFqW=CPyCsmJ}P1^V30|D5E|p3BC5 z)3|qgw@ra7aXb-wsa|l^in~1_fm{7bS9jhVRkYVO#U{qMp z)Wce+|DJ}4<2gp8r0_xfZpMo#{Hl2MfjLcZdRB9(B(A(f;+4s*FxV{1F|4d`*sRNd zp4#@sEY|?^FIJ;tmH{@keZ$P(sLh5IdOk@k^0uB^BWr@pk6mHy$qf&~rI>P*a;h0C{%oA*i!VjWn&D~O#MxN&f@1Po# zKN+ zrGrkSjcr?^R#nGl<#Q722^wbYcgW@{+6CBS<1@%dPA8HC!~a`jTz<`g_l5N1M@9wn9GOAZ>nqNgq!yOCbZ@1z`U_N`Z>}+1HIZxk*5RDc&rd5{3qjRh8QmT$VyS;jK z;AF+r6XnnCp=wQYoG|rT2@8&IvKq*IB_WvS%nt%e{MCFm`&W*#LXc|HrD?nVBo=(8*=Aq?u$sDA_sC_RPDUiQ+wnIJET8vx$&fxkW~kP9qXKt zozR)@xGC!P)CTkjeWvXW5&@2?)qt)jiYWWBU?AUtzAN}{JE1I)dfz~7$;}~BmQF`k zpn11qmObXwRB8&rnEG*#4Xax3XBkKlw(;tb?Np^i+H8m(Wyz9k{~ogba@laiEk;2! zV*QV^6g6(QG%vX5Um#^sT&_e`B1pBW5yVth~xUs#0}nv?~C#l?W+9Lsb_5)!71rirGvY zTIJ$OPOY516Y|_014sNv+Z8cc5t_V=i>lWV=vNu#!58y9Zl&GsMEW#pPYPYGHQ|;vFvd*9eM==$_=vc7xnyz0~ zY}r??$<`wAO?JQk@?RGvkWVJlq2dk9vB(yV^vm{=NVI8dhsX<)O(#nr9YD?I?(VmQ z^r7VfUBn<~p3()8yOBjm$#KWx!5hRW)5Jl7wY@ky9lNM^jaT##8QGVsYeaVywmpv>X|Xj7gWE1Ezai&wVLt3p)k4w~yrskT-!PR!kiyQlaxl(( zXhF%Q9x}1TMt3~u@|#wWm-Vq?ZerK={8@~&@9r5JW}r#45#rWii};t`{5#&3$W)|@ zbAf2yDNe0q}NEUvq_Quq3cTjcw z@H_;$hu&xllCI9CFDLuScEMg|x{S7GdV8<&Mq=ezDnRZAyX-8gv97YTm0bg=d)(>N z+B2FcqvI9>jGtnK%eO%y zoBPkJTk%y`8TLf4)IXPBn`U|9>O~WL2C~C$z~9|0m*YH<-vg2CD^SX#&)B4ngOSG$ zV^wmy_iQk>dfN@Pv(ckfy&#ak@MLC7&Q6Ro#!ezM*VEh`+b3Jt%m(^T&p&WJ2Oqvj zs-4nq0TW6cv~(YI$n0UkfwN}kg3_fp?(ijSV#tR9L0}l2qjc7W?i*q01=St0eZ=4h zyGQbEw`9OEH>NMuIe)hVwYHsGERWOD;JxEiO7cQv%pFCeR+IyhwQ|y@&^24k+|8fD zLiOWFNJ2&vu2&`Jv96_z-Cd5RLgmeY3*4rDOQo?Jm`;I_(+ejsPM03!ly!*Cu}Cco zrQSrEDHNyzT(D5s1rZq!8#?f6@v6dB7a-aWs(Qk>N?UGAo{gytlh$%_IhyL7h?DLXDGx zgxGEBQoCAWo-$LRvM=F5MTle`M})t3vVv;2j0HZY&G z22^iGhV@uaJh(XyyY%} zd4iH_UfdV#T=3n}(Lj^|n;O4|$;xhu*8T3hR1mc_A}fK}jfZ7LX~*n5+`8N2q#rI$ z@<_2VANlYF$vIH$ zl<)+*tIWW78IIINA7Rr7i{<;#^yzxoLNkXL)eSs=%|P>$YQIh+ea_3k z_s7r4%j7%&*NHSl?R4k%1>Z=M9o#zxY!n8sL5>BO-ZP;T3Gut>iLS@U%IBrX6BA3k z)&@q}V8a{X<5B}K5s(c(LQ=%v1ocr`t$EqqY0EqVjr65usa=0bkf|O#ky{j3)WBR(((L^wmyHRzoWuL2~WTC=`yZ zn%VX`L=|Ok0v7?s>IHg?yArBcync5rG#^+u)>a%qjES%dRZoIyA8gQ;StH z1Ao7{<&}6U=5}4v<)1T7t!J_CL%U}CKNs-0xWoTTeqj{5{?Be$L0_tk>M9o8 zo371}S#30rKZFM{`H_(L`EM9DGp+Mifk&IP|C2Zu_)Ghr4Qtpmkm1osCf@%Z$%t+7 zYH$Cr)Ro@3-QDeQJ8m+x6%;?YYT;k6Z0E-?kr>x33`H%*ueBD7Zx~3&HtWn0?2Wt} zTG}*|v?{$ajzt}xPzV%lL1t-URi8*Zn)YljXNGDb>;!905Td|mpa@mHjIH%VIiGx- zd@MqhpYFu4_?y5N4xiHn3vX&|e6r~Xt> zZG`aGq|yTNjv;9E+Txuoa@A(9V7g?1_T5FzRI;!=NP1Kqou1z5?%X~Wwb{trRfd>i z8&y^H)8YnKyA_Fyx>}RNmQIczT?w2J4SNvI{5J&}Wto|8FR(W;Qw#b1G<1%#tmYzQ zQ2mZA-PAdi%RQOhkHy9Ea#TPSw?WxwL@H@cbkZwIq0B!@ns}niALidmn&W?!Vd4Gj zO7FiuV4*6Mr^2xlFSvM;Cp_#r8UaqIzHJQg_z^rEJw&OMm_8NGAY2)rKvki|o1bH~ z$2IbfVeY2L(^*rMRU1lM5Y_sgrDS`Z??nR2lX;zyR=c%UyGb*%TC-Dil?SihkjrQy~TMv6;BMs7P8il`H7DmpVm@rJ;b)hW)BL)GjS154b*xq-NXq2cwE z^;VP7ua2pxvCmxrnqUYQMH%a%nHmwmI33nJM(>4LznvY*k&C0{8f*%?zggpDgkuz&JBx{9mfb@wegEl2v!=}Sq2Gaty0<)UrOT0{MZtZ~j5y&w zXlYa_jY)I_+VA-^#mEox#+G>UgvM!Ac8zI<%JRXM_73Q!#i3O|)lOP*qBeJG#BST0 zqohi)O!|$|2SeJQo(w6w7%*92S})XfnhrH_Z8qe!G5>CglP=nI7JAOW?(Z29;pXJ9 zR9`KzQ=WEhy*)WH>$;7Cdz|>*i>=##0bB)oU0OR>>N<21e4rMCHDemNi2LD>Nc$;& zQRFthpWniC1J6@Zh~iJCoLOxN`oCKD5Q4r%ynwgUKPlIEd#?QViIqovY|czyK8>6B zSP%{2-<;%;1`#0mG^B(8KbtXF;Nf>K#Di72UWE4gQ%(_26Koiad)q$xRL~?pN71ZZ zujaaCx~jXjygw;rI!WB=xrOJO6HJ!!w}7eiivtCg5K|F6$EXa)=xUC za^JXSX98W`7g-tm@uo|BKj39Dl;sg5ta;4qjo^pCh~{-HdLl6qI9Ix6f$+qiZ$}s= zNguKrU;u+T@ko(Vr1>)Q%h$?UKXCY>3se%&;h2osl2D zE4A9bd7_|^njDd)6cI*FupHpE3){4NQ*$k*cOWZ_?CZ>Z4_fl@n(mMnYK62Q1d@+I zr&O))G4hMihgBqRIAJkLdk(p(D~X{-oBUA+If@B}j& zsHbeJ3RzTq96lB7d($h$xTeZ^gP0c{t!Y0c)aQE;$FY2!mACg!GDEMKXFOPI^)nHZ z`aSPJpvV0|bbrzhWWkuPURlDeN%VT8tndV8?d)eN*i4I@u zVKl^6{?}A?P)Fsy?3oi#clf}L18t;TjNI2>eI&(ezDK7RyqFxcv%>?oxUlonv(px) z$vnPzRH`y5A(x!yOIfL0bmgeMQB$H5wenx~!ujQK*nUBW;@Em&6Xv2%s(~H5WcU2R z;%Nw<$tI)a`Ve!>x+qegJnQsN2N7HaKzrFqM>`6R*gvh%O*-%THt zrB$Nk;lE;z{s{r^PPm5qz(&lM{sO*g+W{sK+m3M_z=4=&CC>T`{X}1Vg2PEfSj2x_ zmT*(x;ov%3F?qoEeeM>dUn$a*?SIGyO8m806J1W1o+4HRhc2`9$s6hM#qAm zChQ87b~GEw{ADfs+5}FJ8+|bIlIv(jT$Ap#hSHoXdd9#w<#cA<1Rkq^*EEkknUd4& zoIWIY)sAswy6fSERVm&!SO~#iN$OgOX*{9@_BWFyJTvC%S++ilSfCrO(?u=Dc?CXZ zzCG&0yVR{Z`|ZF0eEApWEo#s9osV>F{uK{QA@BES#&;#KsScf>y zvs?vIbI>VrT<*!;XmQS=bhq%46-aambZ(8KU-wOO2=en~D}MCToB_u;Yz{)1ySrPZ z@=$}EvjTdzTWU7c0ZI6L8=yP+YRD_eMMos}b5vY^S*~VZysrkq<`cK3>>v%uy7jgq z0ilW9KjVDHLv0b<1K_`1IkbTOINs0=m-22c%M~l=^S}%hbli-3?BnNq?b`hx^HX2J zIe6ECljRL0uBWb`%{EA=%!i^4sMcj+U_TaTZRb+~GOk z^ZW!nky0n*Wb*r+Q|9H@ml@Z5gU&W`(z4-j!OzC1wOke`TRAYGZVl$PmQ16{3196( zO*?`--I}Qf(2HIwb2&1FB^!faPA2=sLg(@6P4mN)>Dc3i(B0;@O-y2;lM4akD>@^v z=u>*|!s&9zem70g7zfw9FXl1bpJW(C#5w#uy5!V?Q(U35A~$dR%LDVnq@}kQm13{} zd53q3N(s$Eu{R}k2esbftfjfOITCL;jWa$}(mmm}d(&7JZ6d3%IABCapFFYjdEjdK z&4Edqf$G^MNAtL=uCDRs&Fu@FXRgX{*0<(@c3|PNHa>L%zvxWS={L8%qw`STm+=Rd zA}FLspESSIpE_^41~#5yI2bJ=9`oc;GIL!JuW&7YetZ?0H}$$%8rW@*J37L-~Rsx!)8($nI4 zZhcZ2^=Y+p4YPl%j!nFJA|*M^gc(0o$i3nlphe+~-_m}jVkRN{spFs(o0ajW@f3K{ zDV!#BwL322CET$}Y}^0ixYj2w>&Xh12|R8&yEw|wLDvF!lZ#dOTHM9pK6@Nm-@9Lnng4ZHBgBSrr7KI8YCC9DX5Kg|`HsiwJHg2(7#nS;A{b3tVO?Z% za{m5b3rFV6EpX;=;n#wltDv1LE*|g5pQ+OY&*6qCJZc5oDS6Z6JD#6F)bWxZSF@q% z+1WV;m!lRB!n^PC>RgQCI#D1br_o^#iPk>;K2hB~0^<~)?p}LG%kigm@moD#q3PE+ zA^Qca)(xnqw6x>XFhV6ku9r$E>bWNrVH9fum0?4s?Rn2LG{Vm_+QJHse6xa%nzQ?k zKug4PW~#Gtb;#5+9!QBgyB@q=sk9=$S{4T>wjFICStOM?__fr+Kei1 z3j~xPqW;W@YkiUM;HngG!;>@AITg}vAE`M2Pj9Irl4w1fo4w<|Bu!%rh%a(Ai^Zhi zs92>v5;@Y(Zi#RI*ua*h`d_7;byQSa*v9E{2x$<-_=5Z<7{%)}4XExANcz@rK69T0x3%H<@frW>RA8^swA+^a(FxK| zFl3LD*ImHN=XDUkrRhp6RY5$rQ{bRgSO*(vEHYV)3Mo6Jy3puiLmU&g82p{qr0F?ohmbz)f2r{X2|T2 z$4fdQ=>0BeKbiVM!e-lIIs8wVTuC_m7}y4A_%ikI;Wm5$9j(^Y z(cD%U%k)X>_>9~t8;pGzL6L-fmQO@K; zo&vQzMlgY95;1BSkngY)e{`n0!NfVgf}2mB3t}D9@*N;FQ{HZ3Pb%BK6;5#-O|WI( zb6h@qTLU~AbVW#_6?c!?Dj65Now7*pU{h!1+eCV^KCuPAGs28~3k@ueL5+u|Z-7}t z9|lskE`4B7W8wMs@xJa{#bsCGDFoRSNSnmNYB&U7 zVGKWe%+kFB6kb)e;TyHfqtU6~fRg)f|>=5(N36)0+C z`hv65J<$B}WUc!wFAb^QtY31yNleq4dzmG`1wHTj=c*=hay9iD071Hc?oYoUk|M*_ zU1GihAMBsM@5rUJ(qS?9ZYJ6@{bNqJ`2Mr+5#hKf?doa?F|+^IR!8lq9)wS3tF_9n zW_?hm)G(M+MYb?V9YoX^_mu5h-LP^TL^!Q9Z7|@sO(rg_4+@=PdI)WL(B7`!K^ND- z-uIuVDCVEdH_C@c71YGYT^_Scf_dhB8Z2Xy6vGtBSlYud9vggOqv^L~F{BraSE_t} zIkP+Hp2&nH^-MNEs}^`oMLy11`PQW$T|K(`Bu*(f@)mv1-qY(_YG&J2M2<7k;;RK~ zL{Fqj9yCz8(S{}@c)S!65aF<=&eLI{hAMErCx&>i7OeDN>okvegO87OaG{Jmi<|}D zaT@b|0X{d@OIJ7zvT>r+eTzgLq~|Dpu)Z&db-P4z*`M$UL51lf>FLlq6rfG)%doyp z)3kk_YIM!03eQ8Vu_2fg{+osaEJPtJ-s36R+5_AEG12`NG)IQ#TF9c@$99%0iye+ zUzZ57=m2)$D(5Nx!n)=5Au&O0BBgwxIBaeI(mro$#&UGCr<;C{UjJVAbVi%|+WP(a zL$U@TYCxJ=1{Z~}rnW;7UVb7+ZnzgmrogDxhjLGo>c~MiJAWs&&;AGg@%U?Y^0JhL ze(x6Z74JG6FlOFK(T}SXQfhr}RIFl@QXKnIcXYF)5|V~e-}suHILKT-k|<*~Ij|VF zC;t@=uj=hot~*!C68G8hTA%8SzOfETOXQ|3FSaIEjvBJp(A)7SWUi5!Eu#yWgY+;n zlm<$+UDou*V+246_o#V4kMdto8hF%%Lki#zPh}KYXmMf?hrN0;>Mv%`@{0Qn`Ujp) z=lZe+13>^Q!9zT);H<(#bIeRWz%#*}sgUX9P|9($kexOyKIOc`dLux}c$7It4u|Rl z6SSkY*V~g_B-hMPo_ak>>z@AVQ(_N)VY2kB3IZ0G(iDUYw+2d7W^~(Jq}KY=JnWS( z#rzEa&0uNhJ>QE8iiyz;n2H|SV#Og+wEZv=f2%1ELX!SX-(d3tEj$5$1}70Mp<&eI zCkfbByL7af=qQE@5vDVxx1}FSGt_a1DoE3SDI+G)mBAna)KBG4p8Epxl9QZ4BfdAN zFnF|Y(umr;gRgG6NLQ$?ZWgllEeeq~z^ZS7L?<(~O&$5|y)Al^iMKy}&W+eMm1W z7EMU)u^ke(A1#XCV>CZ71}P}0x)4wtHO8#JRG3MA-6g=`ZM!FcICCZ{IEw8Dm2&LQ z1|r)BUG^0GzI6f946RrBlfB1Vs)~8toZf~7)+G;pv&XiUO(%5bm)pl=p>nV^o*;&T z;}@oZSibzto$arQgfkp|z4Z($P>dTXE{4O=vY0!)kDO* zGF8a4wq#VaFpLfK!iELy@?-SeRrdz%F*}hjKcA*y@mj~VD3!it9lhRhX}5YOaR9$} z3mS%$2Be7{l(+MVx3 z(4?h;P!jnRmX9J9sYN#7i=iyj_5q7n#X(!cdqI2lnr8T$IfOW<_v`eB!d9xY1P=2q&WtOXY=D9QYteP)De?S4}FK6#6Ma z=E*V+#s8>L;8aVroK^6iKo=MH{4yEZ_>N-N z`(|;aOATba1^asjxlILk<4}f~`39dBFlxj>Dw(hMYKPO3EEt1@S`1lxFNM+J@uB7T zZ8WKjz7HF1-5&2=l=fqF-*@>n5J}jIxdDwpT?oKM3s8Nr`x8JnN-kCE?~aM1H!hAE z%%w(3kHfGwMnMmNj(SU(w42OrC-euI>Dsjk&jz3ts}WHqmMpzQ3vZrsXrZ|}+MHA7 z068obeXZTsO*6RS@o3x80E4ok``rV^Y3hr&C1;|ZZ0|*EKO`$lECUYG2gVFtUTw)R z4Um<0ZzlON`zTdvVdL#KFoMFQX*a5wM0Czp%wTtfK4Sjs)P**RW&?lP$(<}q%r68Z zS53Y!d@&~ne9O)A^tNrXHhXBkj~$8j%pT1%%mypa9AW5E&s9)rjF4@O3ytH{0z6riz|@< zB~UPh*wRFg2^7EbQrHf0y?E~dHlkOxof_a?M{LqQ^C!i2dawHTPYUE=X@2(3<=OOxs8qn_(y>pU>u^}3y&df{JarR0@VJn0f+U%UiF=$Wyq zQvnVHESil@d|8&R<%}uidGh7@u^(%?$#|&J$pvFC-n8&A>utA=n3#)yMkz+qnG3wd zP7xCnF|$9Dif@N~L)Vde3hW8W!UY0BgT2v(wzp;tlLmyk2%N|0jfG$%<;A&IVrOI< z!L)o>j>;dFaqA3pL}b-Je(bB@VJ4%!JeX@3x!i{yIeIso^=n?fDX`3bU=eG7sTc%g%ye8$v8P@yKE^XD=NYxTb zbf!Mk=h|otpqjFaA-vs5YOF-*GwWPc7VbaOW&stlANnCN8iftFMMrUdYNJ_Bnn5Vt zxfz@Ah|+4&P;reZxp;MmEI7C|FOv8NKUm8njF7Wb6Gi7DeODLl&G~}G4be&*Hi0Qw z5}77vL0P+7-B%UL@3n1&JPxW^d@vVwp?u#gVcJqY9#@-3X{ok#UfW3<1fb%FT`|)V~ggq z(3AUoUS-;7)^hCjdT0Kf{i}h)mBg4qhtHHBti=~h^n^OTH5U*XMgDLIR@sre`AaB$ zg)IGBET_4??m@cx&c~bA80O7B8CHR7(LX7%HThkeC*@vi{-pL%e)yXp!B2InafbDF zjPXf1mko3h59{lT6EEbxKO1Z5GF71)WwowO6kY|6tjSVSWdQ}NsK2x{>i|MKZK8%Q zfu&_0D;CO-Jg0#YmyfctyJ!mRJp)e#@O0mYdp|8x;G1%OZQ3Q847YWTyy|%^cpA;m zze0(5p{tMu^lDkpe?HynyO?a1$_LJl2L&mpeKu%8YvgRNr=%2z${%WThHG=vrWY@4 zsA`OP#O&)TetZ>s%h!=+CE15lOOls&nvC~$Qz0Ph7tHiP;O$i|eDwpT{cp>+)0-|; zY$|bB+Gbel>5aRN3>c0x)4U=|X+z+{ zn*_p*EQoquRL+=+p;=lm`d71&1NqBz&_ph)MXu(Nv6&XE7(RsS)^MGj5Q?Fwude-(sq zjJ>aOq!7!EN>@(fK7EE#;i_BGvli`5U;r!YA{JRodLBc6-`n8K+Fjgwb%sX;j=qHQ z7&Tr!)!{HXoO<2BQrV9Sw?JRaLXV8HrsNevvnf>Y-6|{T!pYLl7jp$-nEE z#X!4G4L#K0qG_4Z;Cj6=;b|Be$hi4JvMH!-voxqx^@8cXp`B??eFBz2lLD8RRaRGh zn7kUfy!YV~p(R|p7iC1Rdgt$_24i0cd-S8HpG|`@my70g^y`gu%#Tf_L21-k?sRRZHK&at(*ED0P8iw{7?R$9~OF$Ko;Iu5)ur5<->x!m93Eb zFYpIx60s=Wxxw=`$aS-O&dCO_9?b1yKiPCQmSQb>T)963`*U+Ydj5kI(B(B?HNP8r z*bfSBpSu)w(Z3j7HQoRjUG(+d=IaE~tv}y14zHHs|0UcN52fT8V_<@2ep_ee{QgZG zmgp8iv4V{k;~8@I%M3<#B;2R>Ef(Gg_cQM7%}0s*^)SK6!Ym+~P^58*wnwV1BW@eG z4sZLqsUvBbFsr#8u7S1r4teQ;t)Y@jnn_m5jS$CsW1um!p&PqAcc8!zyiXHVta9QC zY~wCwCF0U%xiQPD_INKtTb;A|Zf29(mu9NI;E zc-e>*1%(LSXB`g}kd`#}O;veb<(sk~RWL|f3ljxCnEZDdNSTDV6#Td({6l&y4IjKF z^}lIUq*ZUqgTPumD)RrCN{M^jhY>E~1pn|KOZ5((%F)G|*ZQ|r4zIbrEiV%42hJV8 z3xS)=!X1+=olbdGJ=yZil?oXLct8FM{(6ikLL3E%=q#O6(H$p~gQu6T8N!plf!96| z&Q3=`L~>U0zZh;z(pGR2^S^{#PrPxTRHD1RQOON&f)Siaf`GLj#UOk&(|@0?zm;Sx ztsGt8=29-MZs5CSf1l1jNFtNt5rFNZxJPvkNu~2}7*9468TWm>nN9TP&^!;J{-h)_ z7WsHH9|F%I`Pb!>KAS3jQWKfGivTVkMJLO-HUGM_a4UQ_%RgL6WZvrW+Z4ujZn;y@ zz9$=oO!7qVTaQAA^BhX&ZxS*|5dj803M=k&2%QrXda`-Q#IoZL6E(g+tN!6CA!CP* zCpWtCujIea)ENl0liwVfj)Nc<9mV%+e@=d`haoZ*`B7+PNjEbXBkv=B+Pi^~L#EO$D$ZqTiD8f<5$eyb54-(=3 zh)6i8i|jp(@OnRrY5B8t|LFXFQVQ895n*P16cEKTrT*~yLH6Z4e*bZ5otpRDri&+A zfNbK1D5@O=sm`fN=WzWyse!za5n%^+6dHPGX#8DyIK>?9qyX}2XvBWVqbP%%D)7$= z=#$WulZlZR<{m#gU7lwqK4WS1Ne$#_P{b17qe$~UOXCl>5b|6WVh;5vVnR<%d+Lnp z$uEmML38}U4vaW8>shm6CzB(Wei3s#NAWE3)a2)z@i{4jTn;;aQS)O@l{rUM`J@K& l00vQ5JBs~;vo!vr%%-k{2_Fq1Mn4QF81S)AQ99zk{{c4yR+0b! literal 0 HcmV?d00001 diff --git a/examples/kvp-demo/producer/gradle/wrapper/gradle-wrapper.properties b/examples/kvp-demo/producer/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..9355b415 --- /dev/null +++ b/examples/kvp-demo/producer/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/examples/kvp-demo/producer/gradlew b/examples/kvp-demo/producer/gradlew new file mode 100755 index 00000000..1aa94a42 --- /dev/null +++ b/examples/kvp-demo/producer/gradlew @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://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. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/examples/kvp-demo/producer/gradlew.bat b/examples/kvp-demo/producer/gradlew.bat new file mode 100644 index 00000000..93e3f59f --- /dev/null +++ b/examples/kvp-demo/producer/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/examples/kvp-demo/producer/settings.gradle b/examples/kvp-demo/producer/settings.gradle new file mode 100644 index 00000000..8a11498a --- /dev/null +++ b/examples/kvp-demo/producer/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'quickstart-producer' diff --git a/examples/kvp-demo/producer/src/main/java/com/lightstreamer/kafka/examples/quick_start/producer/FeedSimulator.java b/examples/kvp-demo/producer/src/main/java/com/lightstreamer/kafka/examples/quick_start/producer/FeedSimulator.java new file mode 100644 index 00000000..a0d85edf --- /dev/null +++ b/examples/kvp-demo/producer/src/main/java/com/lightstreamer/kafka/examples/quick_start/producer/FeedSimulator.java @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2024 Lightstreamer Srl + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +package com.lightstreamer.kafka.examples.quick_start.producer; + +import java.security.SecureRandom; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +/** + * Simulates an external data feed that supplies quote values for all the stocks + * needed for the + * demo. + */ +public class FeedSimulator { + + public interface ExternalFeedListener { + + void onEvent(String topic, String symbol, String data); + } + + enum FIELD { + NAME, + TRow, + TMSTMP, + QCHART, + QCHARTTOT, + VTOT, + QTOT, + QA, + Q, + PA, + PCHART, + NTRAD, + PLIM_MIN, + PLIM_MAX + } + + /** + * Used to automatically generate the updates for the 30 stocks: mean and + * standard deviation of the times between consecutive updates for the same + * stock. + */ + private static final double[] UPDATE_TIME_MEANS = { + 30000, 500, 3000, 90000, + 7000, 10000, 3000, 7000, + 7000, 7000, 500, 3000, + 20000, 20000, 20000, 30000, + 500, 3000, 90000, 7000, + 10000, 3000, 7000, 7000, + 7000, 500, 3000, 20000, + 20000, 20000, + }; + + private static final double[] UPDATE_TIME_STD_DEVS = { + 6000, 300, 1000, 1000, + 100, 5000, 1000, 3000, + 1000, 6000, 300, 1000, + 1000, 4000, 1000, 6000, + 300, 1000, 1000, 100, + 5000, 1000, 3000, 1000, + 6000, 300, 1000, 1000, + 4000, 1000, + }; + + private static final String[] SYMBOL_NAMES = { + "Anduct", "Ations Europe", + "Bagies Consulting", "BAY Corporation", + "CON Consulting", "Corcor PLC", + "CVS Asia", "Datio PLC", + "Dentems", "ELE Manufacturing", + "Exacktum Systems", "KLA Systems Inc", + "Lted Europe", "Magasconall Capital", + "MED", "Mice Investments", + "Micropline PLC", "Nologicroup Devices", + "Phing Technology", "Pres Partners", + "Quips Devices", "Ress Devices", + "Sacle Research", "Seaging Devices", + "Sems Systems, Inc", "Softwora Consulting", + "Systeria Develop", "Thewlec Asia", + "Virtutis", "Yahl" + }; + + private static String TOPIC_PREFIX = "MPDF.SIT.DATA.QUOTE"; + + private static final String[] TOPICS = { + "SAMA.1010", "SAMA.1020", + "SAMA.1030", "SAMA.1040", + "SAMA.1050", "SAMA.1060", + "SAMA.1070", "SAMA.1080", + "SAMA.1090", "SAMA.1100", + }; + + /** Manages the current state and generates update events for a single stock. */ + private static class QuoteProducer { + + private static final DateTimeFormatter TMSTP_FORMATTER = DateTimeFormatter.ofPattern("YYYY-MM-ddHH:mm:ss"); + private static final Random random = new SecureRandom(); + + private final double mean, stddev; + private final String name; + private String targetTopic; + + /** Initializes stock data based on the already prepared values. */ + public QuoteProducer(int itemPos) { + this.name = SYMBOL_NAMES[itemPos % TOPICS.length]; + this.targetTopic = formatTopic(itemPos % TOPICS.length); // A topic hosts only one symbol + this.mean = UPDATE_TIME_MEANS[itemPos % UPDATE_TIME_MEANS.length]; + this.stddev = UPDATE_TIME_STD_DEVS[itemPos % UPDATE_TIME_STD_DEVS.length]; + } + + public String getName() { + return name; + } + + public String getTopic() { + return targetTopic; + } + + /** + * Decides, for ease of simulation, the time at which the next update for the + * stock will happen. + */ + public long computeNextWaitTime() { + long millis; + do { + millis = gaussian(mean, stddev); + } while (millis <= 0); + return millis; + } + + public String formatTopic(int topicIndex) { + return "%s.%s".formatted(TOPIC_PREFIX, TOPICS[topicIndex]); + } + + public Map getCurrentValues() { + EnumMap event = new EnumMap<>(FIELD.class); + event.put(FIELD.NAME, name); + event.put(FIELD.TRow, String.valueOf(random.nextInt(1000, 200000))); + event.put(FIELD.TMSTMP, LocalDateTime.now().format(TMSTP_FORMATTER)); + event.put(FIELD.QCHARTTOT, String.valueOf(random.nextInt(2000, 2040))); + event.put(FIELD.QCHART, String.valueOf(random.nextInt(1, 2))); + event.put(FIELD.VTOT, String.valueOf(random.nextInt(8000, 81316))); + event.put(FIELD.QTOT, String.valueOf(random.nextInt(2000, 2040))); + event.put(FIELD.Q, String.valueOf(random.nextInt(1, 2))); + event.put(FIELD.QA, String.valueOf(random.nextInt(9012, 9050))); + event.put(FIELD.PA, String.valueOf(random.nextInt(30, 45))); + event.put(FIELD.PCHART, String.valueOf(random.nextInt(40, 50))); + event.put(FIELD.NTRAD, String.valueOf(random.nextInt(100, 106))); + event.put(FIELD.PLIM_MIN, String.valueOf(random.nextInt(9, 28))); + event.put(FIELD.PLIM_MAX, String.valueOf(random.nextInt(50, 70))); + return event; + } + + private long gaussian(double mean, double stddev) { + double base = random.nextGaussian(); + return (long) (base * stddev + mean); + } + } + + /** Used to keep the contexts of the 30 stocks. */ + private final List stockGenerators = new ArrayList<>(); + + /** The internal listener for the update events. */ + private final ExternalFeedListener listener; + + private ScheduledExecutorService scheduler; + + public FeedSimulator(ExternalFeedListener listener) { + this.listener = listener; + this.scheduler = Executors.newScheduledThreadPool(4); + } + + /** + * Starts generating update events for the stocks. Simulates attaching and + * reading from an external broadcast feed. + */ + public void start() { + for (int i = 0; i < TOPICS.length; i++) { + QuoteProducer quote = new QuoteProducer(i); + stockGenerators.add(quote); + long waitTime = 0; // stock.computeNextWaitTime(); + scheduleGenerator(quote, waitTime); + } + } + + /** + * Generates new values and sends a new update event at the time the producer + * declared to do it. + */ + private void scheduleGenerator(QuoteProducer quoteProducer, long waitTime) { + scheduler.schedule( + () -> { + long nextWaitTime; + synchronized (quoteProducer) { + listener.onEvent( + quoteProducer.getTopic(), quoteProducer.getName(), + formatEvent(quoteProducer.getCurrentValues())); + + nextWaitTime = quoteProducer.computeNextWaitTime(); + } + scheduleGenerator(quoteProducer, nextWaitTime); + }, + waitTime, + TimeUnit.MILLISECONDS); + } + + static String formatEvent(Map event) { + return event.entrySet() + .stream() + .map(e -> e.getKey() + "=" + e.getValue()) + .collect(Collectors.joining(";")); + } + + public static void main(String[] args) { + FeedSimulator feedSimulator = new FeedSimulator( + new ExternalFeedListener() { + + @Override + public void onEvent(String topic, String symbol, String quote) { + System.out.println("topic: " + topic + ", symbol: " + symbol + ", quote: " + quote); + } + }); + feedSimulator.start(); + } +} diff --git a/examples/kvp-demo/producer/src/main/java/com/lightstreamer/kafka/examples/quick_start/producer/Producer.java b/examples/kvp-demo/producer/src/main/java/com/lightstreamer/kafka/examples/quick_start/producer/Producer.java new file mode 100644 index 00000000..58a6a45d --- /dev/null +++ b/examples/kvp-demo/producer/src/main/java/com/lightstreamer/kafka/examples/quick_start/producer/Producer.java @@ -0,0 +1,102 @@ + +/* + * Copyright (C) 2024 Lightstreamer Srl + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +package com.lightstreamer.kafka.examples.quick_start.producer; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Properties; + +import org.apache.kafka.clients.producer.Callback; +import org.apache.kafka.clients.producer.KafkaProducer; +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.clients.producer.ProducerRecord; +import org.apache.kafka.clients.producer.RecordMetadata; +import org.apache.kafka.common.serialization.Serdes; + +import picocli.CommandLine; +import picocli.CommandLine.Option; + +public class Producer implements Runnable, FeedSimulator.ExternalFeedListener { + + @Option(names = "--bootstrap-servers", description = "The Kafka connection string", required = true) + private String bootstrapServers; + + @Option(names = "--config-file", description = "Optional producer config file", required = false) + private String configFile; + + private KafkaProducer producer; + + public void run() { + // Create producer configs + Properties properties = new Properties(); + if (configFile != null) { + if (!Files.exists(Paths.get(configFile))) { + System.err.println("Unable to find the specifed configuration file " + configFile); + System.exit(-1); + } + try (InputStream is = Files.newInputStream(Paths.get(configFile))) { + properties.load(is); + } catch (IOException e) { + e.printStackTrace(); + System.exit(-1); + } + } + + properties.setProperty(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers); + String serializer = Serdes.String().serializer().getClass().getName(); + properties.setProperty(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, serializer); + properties.setProperty(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, serializer); + + // Create the producer + producer = new KafkaProducer<>(properties); + + // Create and start the feed simulator. + FeedSimulator simulator = new FeedSimulator(this); + simulator.start(); + } + + @Override + public void onEvent(String topic, String symbol, String quote) { + try { + ProducerRecord record = new ProducerRecord<>(topic, "", quote); + producer.send( + record, + new Callback() { + public void onCompletion(RecordMetadata metadata, Exception e) { + if (e != null) { + e.printStackTrace(); + System.err.println("Send failed"); + return; + } + String reducedValueStr = record.value().substring(0, 35) + "..."; + System.out.printf( + "Sent record [topic = %2s, offset = %6d, value = %s]%n", + record.topic(), metadata.offset(), reducedValueStr); + } + }); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static void main(String[] args) { + new CommandLine(new Producer()).execute(args); + } +} diff --git a/examples/kvp-demo/producer/src/main/resources/log4j.properties b/examples/kvp-demo/producer/src/main/resources/log4j.properties new file mode 100644 index 00000000..8ea71f01 --- /dev/null +++ b/examples/kvp-demo/producer/src/main/resources/log4j.properties @@ -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. + +log4j.rootLogger=WARN, stdout + +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=[%d] [%-10c{1}] %-5p %m%n +log4j.appender.stdout.Target=System.out + +log4j.appender.stderr=org.apache.log4j.ConsoleAppender +log4j.appender.stderr.layout=org.apache.log4j.PatternLayout +log4j.appender.stderr.layout.ConversionPattern=[%d] [%t-%c] %p %m%n +log4j.appender.stderr.Target=System.err diff --git a/examples/kvp-demo/producer/start-publisher.sh b/examples/kvp-demo/producer/start-publisher.sh new file mode 100755 index 00000000..c7e0a4c5 --- /dev/null +++ b/examples/kvp-demo/producer/start-publisher.sh @@ -0,0 +1,3 @@ +#!/bin/bash +set -ex +java -jar build/libs/quickstart-producer-all.jar --bootstrap-servers pkc-z9doz.eu-west-1.aws.confluent.cloud:9092 --topic stocks --config-file producer.properties diff --git a/examples/kvp-demo/screen_small.png b/examples/kvp-demo/screen_small.png new file mode 100644 index 0000000000000000000000000000000000000000..c1962caa0d2030d6b4e77ddf6cabf5ff0d92a63d GIT binary patch literal 10987 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

~e{}w9m7r|lkXpuT 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 literal 0 HcmV?d00001 diff --git a/examples/kvp-demo/start.sh b/examples/kvp-demo/start.sh new file mode 120000 index 00000000..dd868ce4 --- /dev/null +++ b/examples/kvp-demo/start.sh @@ -0,0 +1 @@ +../compose-templates/start.sh \ No newline at end of file diff --git a/examples/kvp-demo/stop.sh b/examples/kvp-demo/stop.sh new file mode 120000 index 00000000..515a8a07 --- /dev/null +++ b/examples/kvp-demo/stop.sh @@ -0,0 +1 @@ +../compose-templates/stop.sh \ No newline at end of file diff --git a/examples/kvp-demo/web/css/table.css b/examples/kvp-demo/web/css/table.css new file mode 100644 index 00000000..f426da3e --- /dev/null +++ b/examples/kvp-demo/web/css/table.css @@ -0,0 +1,110 @@ +/* + * LIGHTSTREAMER - www.lightstreamer.com + * Basic Stock-List Demo + * + * Copyright (c) Lightstreamer Srl + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + body { + font-family: Verdana, Arial, Helvetica, Sans-serif; + } + .banner { + width: 100%; + position: relative; + background-image: url("../images/banner.jpg"); + background-size: cover; + background-position: center; + font-size: 40px; + text-align: center; + color: #003D06; + padding: 20px; +} +.logo { + height: 60px; +} +.banner .title { + display: inline; + vertical-align: super; + padding-left: 4px; +} +.banner .subtitle { + font-size: 20px; +} +.ribbon { + z-index: 1; + background-color: #003D06; + overflow: hidden; + white-space: nowrap; + position: absolute; + right: -50px; + top: 40px; + -webkit-transform: rotate(45deg); + -moz-transform: rotate(45deg); + -ms-transform: rotate(45deg); + -o-transform: rotate(45deg); + transform: rotate(45deg); + -webkit-box-shadow: 0 0 10px #888; + -moz-box-shadow: 0 0 10px #888; + box-shadow: 0 0 10px #888; +} +.ribbon a { + color: #fff; + display: block; + font: bold 81.25% 'Helvetica Neue', Helvetica, Arial, sans-serif; + margin: 1px 0; + padding: 10px 50px; + text-align: center; + text-decoration: none; + text-shadow: 0 0 5px #444; +} +.tableContainer { + overflow-x:auto; + margin-top: 1em; +} +.tableTitle { + color: #ffffff; + background-color: #003D06; + text-align: right; +} +.disc { + margin-top: 5px; + color: #000000; + background: #ffffff; + font-weight: normal; + text-align: right; + font-size: 10px; +} +.stockNameOdd { + color: #003D06; + background-color: #fff; + text-align: left; +} +.stockNameEven { + color: #003D06; + background-color: #e9fbf2; + text-align: left; +} +.coldOdd { + color: #003D06; + background-color: #fff; + text-align: right; +} +.coldEven { + color: #003D06; + background-color: #e9fbf2; + text-align: right; +} +#wrap { + margin: 0 auto; +} diff --git a/examples/kvp-demo/web/images/banner.jpg b/examples/kvp-demo/web/images/banner.jpg new file mode 100644 index 0000000000000000000000000000000000000000..40bc11a5d646a22e1f64ccfa3b20de47ad39b25e GIT binary patch literal 97103 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 F{{t!a2Ri@& literal 0 HcmV?d00001 diff --git a/examples/kvp-demo/web/images/logo.png b/examples/kvp-demo/web/images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..f074fe923941c11360fe7921d3d483e93aac3e56 GIT binary patch literal 14354 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 M07*qoM6N<$f(oq#XaE2J literal 0 HcmV?d00001 diff --git a/examples/kvp-demo/web/index.html b/examples/kvp-demo/web/index.html new file mode 100644 index 00000000..ccbd8e87 --- /dev/null +++ b/examples/kvp-demo/web/index.html @@ -0,0 +1,255 @@ + + + + + + + + + + Lightstreamer Kafka Connector | Basic Stock-List Demo + + + + + + +

+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SYMBOLQCHARTTOTTRowTMSTMPQCHARTVTOTQTOTQAQPAPCHARTNTRADPLIM_MINPLIM_MAX
Loading...
Loading...
-
-
-
-
-
-
-
-
-
-
-
-
Loading...
Loading...
-
-
-
-
-
-
-
-
-
-
-
-
Loading...
Loading...
-
-
-
-
-
-
-
-
-
-
-
-
Loading...
Loading...
-
-
-
-
-
-
-
-
-
-
-
-
Loading...
Loading...
-
-
-
-
-
-
-
-
-
-
-
-
Loading...
Loading...
-
-
-
-
-
-
-
-
-
-
-
-
Loading...
Loading...
-
-
-
-
-
-
-
-
-
-
-
-
Loading...
Loading...
-
-
-
-
-
-
-
-
-
-
-
-
Loading...
Loading...
-
-
-
-
-
-
-
-
-
-
-
-
+
+
Simulated market data.
+ +
+ + + diff --git a/kafka-connector-project/gradle.properties b/kafka-connector-project/gradle.properties index fa4cbf13..adfc8dc6 100644 --- a/kafka-connector-project/gradle.properties +++ b/kafka-connector-project/gradle.properties @@ -1,5 +1,5 @@ -version=1.1.0 +version=1.1.1-kvp brandedProjectName=Lightstreamer Kafka Connector -release_date=2024-12-11 +release_date=2024-12-16 connect_owner=lightstreamer connect_componentName=kafka-connect-lightstreamer diff --git a/kafka-connector-project/kafka-connector/src/main/java/com/lightstreamer/kafka/adapters/ConnectorConfigurator.java b/kafka-connector-project/kafka-connector/src/main/java/com/lightstreamer/kafka/adapters/ConnectorConfigurator.java index 7f1da417..0542cd28 100644 --- a/kafka-connector-project/kafka-connector/src/main/java/com/lightstreamer/kafka/adapters/ConnectorConfigurator.java +++ b/kafka-connector-project/kafka-connector/src/main/java/com/lightstreamer/kafka/adapters/ConnectorConfigurator.java @@ -27,6 +27,7 @@ import com.lightstreamer.kafka.adapters.mapping.selectors.WrapperKeyValueSelectorSuppliers.KeyValueDeserializers; import com.lightstreamer.kafka.adapters.mapping.selectors.avro.GenericRecordSelectorsSuppliers; import com.lightstreamer.kafka.adapters.mapping.selectors.json.JsonNodeSelectorsSuppliers; +import com.lightstreamer.kafka.adapters.mapping.selectors.kvp.KvpSelectorsSuppliers; import com.lightstreamer.kafka.adapters.mapping.selectors.others.OthersSelectorSuppliers; import com.lightstreamer.kafka.common.config.ConfigException; import com.lightstreamer.kafka.common.config.FieldConfigs; @@ -96,7 +97,7 @@ private static ConsumerTriggerConfigImpl doConfigure( TopicConfigurations.of(config.getItemTemplateConfigs(), config.getTopicMappings()); ItemTemplates itemTemplates = Items.templatesFrom(topicsConfig, sSuppliers); - DataExtractor fieldsExtractor = fieldConfigs.extractor(sSuppliers); + DataExtractor fieldsExtractor = fieldConfigs.extractor(sSuppliers, true); return new ConsumerTriggerConfigImpl<>( config.getAdapterName(), @@ -118,6 +119,7 @@ private static ConsumerTriggerConfigImpl doConfigure( return switch (type) { case JSON -> new JsonNodeSelectorsSuppliers(config); case AVRO -> new GenericRecordSelectorsSuppliers(config); + case KVP -> new KvpSelectorsSuppliers(); default -> new OthersSelectorSuppliers(config); }; }; diff --git a/kafka-connector-project/kafka-connector/src/main/java/com/lightstreamer/kafka/adapters/config/specs/ConfigTypes.java b/kafka-connector-project/kafka-connector/src/main/java/com/lightstreamer/kafka/adapters/config/specs/ConfigTypes.java index 3803d82b..51fade84 100644 --- a/kafka-connector-project/kafka-connector/src/main/java/com/lightstreamer/kafka/adapters/config/specs/ConfigTypes.java +++ b/kafka-connector-project/kafka-connector/src/main/java/com/lightstreamer/kafka/adapters/config/specs/ConfigTypes.java @@ -185,6 +185,7 @@ public static Set names() { public enum EvaluatorType { AVRO, JSON, + KVP, STRING, INTEGER, BOOLEAN, diff --git a/kafka-connector-project/kafka-connector/src/main/java/com/lightstreamer/kafka/adapters/consumers/offsets/Offsets.java b/kafka-connector-project/kafka-connector/src/main/java/com/lightstreamer/kafka/adapters/consumers/offsets/Offsets.java index 3664fd41..ec890ed4 100644 --- a/kafka-connector-project/kafka-connector/src/main/java/com/lightstreamer/kafka/adapters/consumers/offsets/Offsets.java +++ b/kafka-connector-project/kafka-connector/src/main/java/com/lightstreamer/kafka/adapters/consumers/offsets/Offsets.java @@ -174,7 +174,7 @@ public void initStore( offsetRepo.computeIfAbsent( partition, p -> new OffsetAndMetadata(startOffsets.get(p))); // Store the offsets that have been already delivered to clients, - // bot not yet committed (most likely due to an exception while processing in + // but not yet committed (most likely due to an exception while processing in // parallel). pendingOffsetsMap.put(partition, decode(offsetAndMetadata.metadata())); } diff --git a/kafka-connector-project/kafka-connector/src/main/java/com/lightstreamer/kafka/adapters/consumers/processor/RecordConsumerSupport.java b/kafka-connector-project/kafka-connector/src/main/java/com/lightstreamer/kafka/adapters/consumers/processor/RecordConsumerSupport.java index 47c4ebdd..d5984a74 100644 --- a/kafka-connector-project/kafka-connector/src/main/java/com/lightstreamer/kafka/adapters/consumers/processor/RecordConsumerSupport.java +++ b/kafka-connector-project/kafka-connector/src/main/java/com/lightstreamer/kafka/adapters/consumers/processor/RecordConsumerSupport.java @@ -282,17 +282,19 @@ void consumeRecord(ConsumerRecord record) { try { recordProcessor.process(record); offsetService.updateOffsets(record); - } catch (ValueException ve) { + } catch (Throwable ve) { logger.atWarn().log("Error while extracting record: {}", ve.getMessage()); logger.atWarn().log("Applying the {} strategy", errorStrategy); switch (errorStrategy) { case IGNORE_AND_CONTINUE -> { - logger.atWarn().log("Ignoring error"); + // We we log the error to catch the stack trace + logger.atWarn().setCause(ve).log("Ignoring error"); offsetService.updateOffsets(record); } case FORCE_UNSUBSCRIPTION -> { + // Do not log the error, which will fully logged from the consuming loop logger.atWarn().log("Forcing unsubscription"); throw new KafkaException(ve); } diff --git a/kafka-connector-project/kafka-connector/src/main/java/com/lightstreamer/kafka/adapters/consumers/wrapper/ConsumerWrapperImpl.java b/kafka-connector-project/kafka-connector/src/main/java/com/lightstreamer/kafka/adapters/consumers/wrapper/ConsumerWrapperImpl.java index 4fad97ce..3f537f6c 100644 --- a/kafka-connector-project/kafka-connector/src/main/java/com/lightstreamer/kafka/adapters/consumers/wrapper/ConsumerWrapperImpl.java +++ b/kafka-connector-project/kafka-connector/src/main/java/com/lightstreamer/kafka/adapters/consumers/wrapper/ConsumerWrapperImpl.java @@ -47,6 +47,7 @@ import java.util.concurrent.CountDownLatch; import java.util.function.Function; import java.util.function.Supplier; +import java.util.regex.Pattern; import java.util.stream.Collectors; class ConsumerWrapperImpl implements ConsumerWrapper { @@ -186,28 +187,30 @@ protected boolean subscribed() { options.timeoutMs(30000); // Retain from the original requestes topics the available ones. - Set existingTopics = admin.listTopics(options); - boolean notAllPresent = topics.retainAll(existingTopics); - - // Can't subscribe at all. Force unsubscription and exit the loop. - if (topics.isEmpty()) { - log.atWarn().log("Not found requested topics"); - metadataListener.forceUnsubscriptionAll(); - return false; - } - - // Just warn that not all requested topics can be subscribed. - if (notAllPresent) { - String loggableTopics = - topics.stream() - .map(s -> "\"%s\"".formatted(s)) - .collect(Collectors.joining(",")); - log.atWarn() - .log( - "Actually subscribing to the following existing topics [{}]", - loggableTopics); - } - consumer.subscribe(topics, offsetService); + // Set existingTopics = admin.listTopics(options); + // boolean notAllPresent = topics.retainAll(existingTopics); + + // // Can't subscribe at all. Force unsubscription and exit the loop. + // if (topics.isEmpty()) { + // log.atWarn().log("Not found requested topics"); + // metadataListener.forceUnsubscriptionAll(); + // return false; + // } + + // // Just warn that not all requested topics can be subscribed. + // if (notAllPresent) { + // String loggableTopics = + // topics.stream() + // .map(s -> "\"%s\"".formatted(s)) + // .collect(Collectors.joining(",")); + // log.atWarn() + // .log( + // "Actually subscribing to the following existing topics [{}]", + // loggableTopics); + // } + String regex = topics.stream().collect(Collectors.joining("|")); + log.debug("Subscribing to {}", regex); + consumer.subscribe(Pattern.compile(regex), offsetService); return true; } catch (Exception e) { log.atError().setCause(e).log(); diff --git a/kafka-connector-project/kafka-connector/src/main/java/com/lightstreamer/kafka/adapters/mapping/selectors/kvp/KvpSelectorsSuppliers.java b/kafka-connector-project/kafka-connector/src/main/java/com/lightstreamer/kafka/adapters/mapping/selectors/kvp/KvpSelectorsSuppliers.java new file mode 100644 index 00000000..36337079 --- /dev/null +++ b/kafka-connector-project/kafka-connector/src/main/java/com/lightstreamer/kafka/adapters/mapping/selectors/kvp/KvpSelectorsSuppliers.java @@ -0,0 +1,248 @@ + +/* + * Copyright (C) 2024 Lightstreamer Srl + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +package com.lightstreamer.kafka.adapters.mapping.selectors.kvp; + +import com.lightstreamer.kafka.adapters.mapping.selectors.KeyValueSelectorSuppliersMaker; +import com.lightstreamer.kafka.common.expressions.Constant; +import com.lightstreamer.kafka.common.expressions.Expressions.ExtractionExpression; +import com.lightstreamer.kafka.common.mapping.selectors.Data; +import com.lightstreamer.kafka.common.mapping.selectors.ExtractionException; +import com.lightstreamer.kafka.common.mapping.selectors.KafkaRecord; +import com.lightstreamer.kafka.common.mapping.selectors.KeySelector; +import com.lightstreamer.kafka.common.mapping.selectors.KeySelectorSupplier; +import com.lightstreamer.kafka.common.mapping.selectors.Parsers.Node; +import com.lightstreamer.kafka.common.mapping.selectors.StructuredBaseSelector; +import com.lightstreamer.kafka.common.mapping.selectors.ValueSelector; +import com.lightstreamer.kafka.common.mapping.selectors.ValueSelectorSupplier; +import com.lightstreamer.kafka.common.utils.Split; + +import org.apache.kafka.common.serialization.Deserializer; +import org.apache.kafka.common.serialization.Serdes; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class KvpSelectorsSuppliers implements KeyValueSelectorSuppliersMaker { + + interface KvpNode extends Node { + + @Override + default boolean has(String propertyName) { + return false; + } + + @Override + default KvpNode get(String propertyName) { + return null; + } + + @Override + default KvpNode get(int index) { + return null; + } + + @Override + default boolean isArray() { + return false; + } + + @Override + default int size() { + return 0; + } + + @Override + default boolean isScalar() { + return true; + } + } + + static class KvpValue implements KvpNode { + + static final KvpValue NULL_VALUE = new KvpValue("NULL_VALUE"); + + private String value; + + KvpValue(String value) { + this.value = value; + } + + @Override + public String asText(String defaultStr) { + return this != NULL_VALUE ? value : defaultStr; + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean isNull() { + return this == NULL_VALUE; + } + } + + static class KvpMap implements KvpNode { + + static final KvpMap NULL_MAP = new KvpMap(Collections.emptyMap()); + + final Map values; + + KvpMap(Map values) { + this.values = values; + } + + @Override + public boolean has(String propertyName) { + return values.containsKey(propertyName); + } + + @Override + public KvpNode get(String propertyName) { + return values.get(propertyName); + } + + @Override + public int size() { + return values.size(); + } + + @Override + public boolean isScalar() { + return false; + } + + @Override + public String asText(String defaultStr) { + return this != NULL_MAP ? toString() : defaultStr; + } + + @Override + public String toString() { + return values.toString(); + } + + @Override + public boolean isNull() { + return this == NULL_MAP; + } + + static KvpMap fromString(String text) { + if (text == null || text.isBlank()) { + return KvpMap.NULL_MAP; + } + + List tokens = Split.bySemicolon(text); + Map values = new LinkedHashMap<>(); + for (int i = 0; i < tokens.size(); i++) { + String splittable = tokens.get(i); + Split.asPair(splittable, '=') + .ifPresentOrElse( + pair -> values.put(pair.key(), new KvpValue(pair.value())), + () -> values.put(splittable, KvpValue.NULL_VALUE)); + } + + return new KvpMap(values); + } + } + + private static class KvpNodeKeySelectorSupplier implements KeySelectorSupplier { + + private final Deserializer deseralizer; + + KvpNodeKeySelectorSupplier() { + this.deseralizer = Serdes.String().deserializer(); + } + + @Override + public KeySelector newSelector(String name, ExtractionExpression expression) + throws ExtractionException { + return new KvpNodeKeySelector(name, expression); + } + + @Override + public Deserializer deseralizer() { + return deseralizer; + } + } + + private static final class KvpNodeKeySelector extends StructuredBaseSelector + implements KeySelector { + + KvpNodeKeySelector(String name, ExtractionExpression expression) + throws ExtractionException { + super(name, expression, Constant.KEY); + } + + @Override + public Data extractKey(KafkaRecord record) { + KvpMap node = KvpMap.fromString(record.key()); + return super.eval(node); + } + } + + private static class KvpNodeValueSelectorSupplier implements ValueSelectorSupplier { + + private final Deserializer deseralizer; + + KvpNodeValueSelectorSupplier() { + this.deseralizer = Serdes.String().deserializer(); + } + + @Override + public ValueSelector newSelector(String name, ExtractionExpression expression) + throws ExtractionException { + return new KvpNodeValueSelector(name, expression); + } + + @Override + public Deserializer deseralizer() { + return deseralizer; + } + } + + private static final class KvpNodeValueSelector extends StructuredBaseSelector + implements ValueSelector { + + KvpNodeValueSelector(String name, ExtractionExpression expression) + throws ExtractionException { + super(name, expression, Constant.VALUE); + } + + @Override + public Data extractValue(KafkaRecord record) { + KvpMap node = KvpMap.fromString(record.value()); + return super.eval(node); + } + } + + public KvpSelectorsSuppliers() {} + + @Override + public KeySelectorSupplier makeKeySelectorSupplier() { + return new KvpNodeKeySelectorSupplier(); + } + + @Override + public ValueSelectorSupplier makeValueSelectorSupplier() { + return new KvpNodeValueSelectorSupplier(); + } +} diff --git a/kafka-connector-project/kafka-connector/src/main/java/com/lightstreamer/kafka/common/config/FieldConfigs.java b/kafka-connector-project/kafka-connector/src/main/java/com/lightstreamer/kafka/common/config/FieldConfigs.java index 348cae6c..69927a9c 100644 --- a/kafka-connector-project/kafka-connector/src/main/java/com/lightstreamer/kafka/common/config/FieldConfigs.java +++ b/kafka-connector-project/kafka-connector/src/main/java/com/lightstreamer/kafka/common/config/FieldConfigs.java @@ -65,6 +65,12 @@ public ExtractionExpression getExression(String fieldName) { public DataExtractor extractor(KeyValueSelectorSuppliers selectorSuppliers) throws ExtractionException { - return DataExtractor.extractor(selectorSuppliers, SCHEMA_NAME, expressions); + return extractor(selectorSuppliers, false); + } + + public DataExtractor extractor( + KeyValueSelectorSuppliers selectorSuppliers, boolean skipOnFailure) + throws ExtractionException { + return DataExtractor.extractor(selectorSuppliers, SCHEMA_NAME, expressions, skipOnFailure); } } diff --git a/kafka-connector-project/kafka-connector/src/main/java/com/lightstreamer/kafka/common/mapping/RecordMapper.java b/kafka-connector-project/kafka-connector/src/main/java/com/lightstreamer/kafka/common/mapping/RecordMapper.java index 2045d807..bf3214ce 100644 --- a/kafka-connector-project/kafka-connector/src/main/java/com/lightstreamer/kafka/common/mapping/RecordMapper.java +++ b/kafka-connector-project/kafka-connector/src/main/java/com/lightstreamer/kafka/common/mapping/RecordMapper.java @@ -144,7 +144,14 @@ public boolean hasFieldExtractor() { @Override public MappedRecord map(KafkaRecord record) throws ValueException { - var extractors = templateExtractors.getOrDefault(record.topic(), emptySet()); + String topic = record.topic(); + // templateExtractors.keySet().stream().filter(t->topic.matches(t)); + var extractors = + templateExtractors.entrySet().stream() + .filter(e -> topic.matches(e.getKey())) + .flatMap(e -> e.getValue().stream()) + .collect(Collectors.toSet()); + // var extractors = templateExtractors.getOrDefault(topic, emptySet()); if (extractors.isEmpty()) { return DefaultMappedRecord.NOPRecord; } diff --git a/kafka-connector-project/kafka-connector/src/main/java/com/lightstreamer/kafka/common/mapping/selectors/DataExtractor.java b/kafka-connector-project/kafka-connector/src/main/java/com/lightstreamer/kafka/common/mapping/selectors/DataExtractor.java index ccdbfeb7..8a892fd0 100644 --- a/kafka-connector-project/kafka-connector/src/main/java/com/lightstreamer/kafka/common/mapping/selectors/DataExtractor.java +++ b/kafka-connector-project/kafka-connector/src/main/java/com/lightstreamer/kafka/common/mapping/selectors/DataExtractor.java @@ -32,24 +32,33 @@ public interface DataExtractor { Schema schema(); + public static DataExtractor extractor( + KeyValueSelectorSuppliers sSuppliers, + String schemaName, + Map expressions, + boolean skipOnFailure) + throws ExtractionException { + return DataExtractorSupport.extractor(sSuppliers, schemaName, expressions, skipOnFailure); + } + public static DataExtractor extractor( KeyValueSelectorSuppliers sSuppliers, String schemaName, Map expressions) throws ExtractionException { - return DataExtractorSupport.extractor(sSuppliers, schemaName, expressions); + return DataExtractorSupport.extractor(sSuppliers, schemaName, expressions, false); } public static DataExtractor extractor( KeyValueSelectorSuppliers sSuppliers, TemplateExpression expression) throws ExtractionException { - return extractor(sSuppliers, expression.prefix(), expression.params()); + return extractor(sSuppliers, expression.prefix(), expression.params(), false); } public static DataExtractor extractor( KeyValueSelectorSuppliers sSuppliers, String schemaName) throws ExtractionException { - return extractor(sSuppliers, schemaName, Collections.emptyMap()); + return extractor(sSuppliers, schemaName, Collections.emptyMap(), false); } public static DataExtractor extractor( @@ -58,6 +67,6 @@ public static DataExtractor extractor( String parameter, String expression) throws ExpressionException, ExtractionException { - return extractor(sSuppliers, schema, Map.of(parameter, Expression(expression))); + return extractor(sSuppliers, schema, Map.of(parameter, Expression(expression)), false); } } diff --git a/kafka-connector-project/kafka-connector/src/main/java/com/lightstreamer/kafka/common/mapping/selectors/DataExtractorSupport.java b/kafka-connector-project/kafka-connector/src/main/java/com/lightstreamer/kafka/common/mapping/selectors/DataExtractorSupport.java index 35894218..8fbdc7a5 100644 --- a/kafka-connector-project/kafka-connector/src/main/java/com/lightstreamer/kafka/common/mapping/selectors/DataExtractorSupport.java +++ b/kafka-connector-project/kafka-connector/src/main/java/com/lightstreamer/kafka/common/mapping/selectors/DataExtractorSupport.java @@ -26,6 +26,7 @@ import java.util.Objects; import java.util.Set; import java.util.function.Function; +import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -34,24 +35,28 @@ class DataExtractorSupport { public static DataExtractor extractor( KeyValueSelectorSuppliers suppliers, String schema, - Map expressions) + Map expressions, + boolean skipOnFailure) throws ExtractionException { - return new DataExtractorImpl<>(suppliers, schema, expressions); + return new DataExtractorImpl<>(suppliers, schema, expressions, skipOnFailure); } private static final class DataExtractorImpl implements DataExtractor { private final Schema schema; private final WrapperSelectors wrapperSelectors; + private final boolean skipOnFailure; DataExtractorImpl( KeyValueSelectorSuppliers sSuppliers, String schemaName, - Map expressions) + Map expressions, + boolean skipOnFailure) throws ExtractionException { this.wrapperSelectors = mkWrapperSelectors(sSuppliers, expressions); this.schema = mkSchema(schemaName); + this.skipOnFailure = skipOnFailure; } @Override @@ -62,21 +67,29 @@ public Schema schema() { public SchemaAndValues extractData(KafkaRecord record) throws ValueException { Map values = new HashMap<>(); for (KeySelector selector : wrapperSelectors.keySelectors()) { - Data data = selector.extractKey(record); - values.put(data.name(), data.text()); + tryFill(() -> selector.extractKey(record), values); } for (ValueSelector selector : wrapperSelectors.valueSelectors()) { - Data data = selector.extractValue(record); - values.put(data.name(), data.text()); + tryFill(() -> selector.extractValue(record), values); } for (ConstantSelector selector : wrapperSelectors.metaSelectors()) { - Data data = selector.extract(record); - values.put(data.name(), data.text()); + tryFill(() -> selector.extract(record), values); } return new DefaultSchemaAndValues(schema, values); } + private void tryFill(Supplier supplier, Map values) { + try { + Data data = supplier.get(); + values.put(data.name(), data.text()); + } catch (ValueException ve) { + if (!skipOnFailure) { + throw ve; + } + } + } + @Override public int hashCode() { return Objects.hash(wrapperSelectors, schema); diff --git a/kafka-connector-project/kafka-connector/src/test/java/com/lightstreamer/kafka/adapters/ConnectorConfiguratorTest.java b/kafka-connector-project/kafka-connector/src/test/java/com/lightstreamer/kafka/adapters/ConnectorConfiguratorTest.java index 46f978ee..5c838e3f 100644 --- a/kafka-connector-project/kafka-connector/src/test/java/com/lightstreamer/kafka/adapters/ConnectorConfiguratorTest.java +++ b/kafka-connector-project/kafka-connector/src/test/java/com/lightstreamer/kafka/adapters/ConnectorConfiguratorTest.java @@ -90,6 +90,11 @@ static Stream getMakersArguments() { Serdes.Integer().deserializer().getClass(), "JSON", KafkaJsonDeserializer.class), + Arguments.of( + "INTEGER", + Serdes.Integer().deserializer().getClass(), + "KVP", + Serdes.String().deserializer().getClass()), Arguments.of( "FLOAT", Serdes.Float().deserializer().getClass(), diff --git a/kafka-connector-project/kafka-connector/src/test/java/com/lightstreamer/kafka/adapters/config/ConnectorConfigTest.java b/kafka-connector-project/kafka-connector/src/test/java/com/lightstreamer/kafka/adapters/config/ConnectorConfigTest.java index 63e5f88b..5bc98c5c 100644 --- a/kafka-connector-project/kafka-connector/src/test/java/com/lightstreamer/kafka/adapters/config/ConnectorConfigTest.java +++ b/kafka-connector-project/kafka-connector/src/test/java/com/lightstreamer/kafka/adapters/config/ConnectorConfigTest.java @@ -759,6 +759,7 @@ public void shouldGetText() { strings = { "AVRO", "STRING", + "KVP", "JSON", "INTEGER", "SHORT", diff --git a/kafka-connector-project/kafka-connector/src/test/java/com/lightstreamer/kafka/adapters/mapping/selectors/kvp/KvpNodeSelectorsSuppliersTest.java b/kafka-connector-project/kafka-connector/src/test/java/com/lightstreamer/kafka/adapters/mapping/selectors/kvp/KvpNodeSelectorsSuppliersTest.java new file mode 100644 index 00000000..0af82d1f --- /dev/null +++ b/kafka-connector-project/kafka-connector/src/test/java/com/lightstreamer/kafka/adapters/mapping/selectors/kvp/KvpNodeSelectorsSuppliersTest.java @@ -0,0 +1,224 @@ + +/* + * Copyright (C) 2024 Lightstreamer Srl + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +package com.lightstreamer.kafka.adapters.mapping.selectors.kvp; + +import static com.google.common.truth.Truth.assertThat; +import static com.lightstreamer.kafka.test_utils.Records.fromKey; +import static com.lightstreamer.kafka.test_utils.Records.fromValue; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.google.common.truth.StringSubject; +import com.lightstreamer.kafka.common.expressions.Expressions; +import com.lightstreamer.kafka.common.expressions.Expressions.ExtractionExpression; +import com.lightstreamer.kafka.common.mapping.selectors.ExtractionException; +import com.lightstreamer.kafka.common.mapping.selectors.KeySelector; +import com.lightstreamer.kafka.common.mapping.selectors.KeySelectorSupplier; +import com.lightstreamer.kafka.common.mapping.selectors.ValueException; +import com.lightstreamer.kafka.common.mapping.selectors.ValueSelector; +import com.lightstreamer.kafka.common.mapping.selectors.ValueSelectorSupplier; + +import org.apache.kafka.common.serialization.StringDeserializer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +public class KvpNodeSelectorsSuppliersTest { + + private String TEXT = + "QCHARTTOT=2032;TRow=12790;QV=9;PV=43;TMSTMP=2024-04-3013:23:07;QCHART=1;VTOT=81316;QTOT=2032;O=30/04/2024-13:23:07;QA=9012;Q=1;PA=40;PCHART=43;NTRAD=106;UNDEFINED"; + + static ValueSelector valueSelector(ExtractionExpression expression) + throws ExtractionException { + return new KvpSelectorsSuppliers() + .makeValueSelectorSupplier() + .newSelector("name", expression); + } + + static KeySelector keySelector(ExtractionExpression expression) + throws ExtractionException { + return new KvpSelectorsSuppliers() + .makeKeySelectorSupplier() + .newSelector("name", expression); + } + + @Test + public void shouldMakeKeySelectorSupplierWith() { + KvpSelectorsSuppliers s = new KvpSelectorsSuppliers(); + KeySelectorSupplier keySelectorSupplier = s.makeKeySelectorSupplier(); + assertThat(keySelectorSupplier.deseralizer().getClass()) + .isEqualTo(StringDeserializer.class); + } + + @Test + public void shouldMakeValueSelectorSupplierWith() { + KvpSelectorsSuppliers s = new KvpSelectorsSuppliers(); + ValueSelectorSupplier keySelectorSupplier = s.makeValueSelectorSupplier(); + assertThat(keySelectorSupplier.deseralizer().getClass()) + .isEqualTo(StringDeserializer.class); + } + + @ParameterizedTest(name = "[{index}] {arguments}") + @CsvSource( + useHeadersInDisplayName = true, + textBlock = + """ + EXPRESSION, EXPECTED + VALUE.QCHARTTOT, 2032 + VALUE.TRow, 12790 + VALUE.QV, 9 + VALUE.PV, 43 + VALUE.TMSTMP, 2024-04-3013:23:07 + VALUE.QCHART, 1 + VALUE.VTOT, 81316 + VALUE.QTOT, 2032 + VALUE.O, 30/04/2024-13:23:07 + VALUE.QA, 9012 + VALUE.Q, 1 + VALUE.PA, 40 + VALUE.PCHART, 43 + VALUE.NTRAD, 106 + VALUE.UNDEFINED, NULL + """) + public void shouldExtractValue(String expressionStr, String expected) + throws ExtractionException { + String text = + "QCHARTTOT=2032;TRow=12790;QV=9;PV=43;TMSTMP=2024-04-3013:23:07;QCHART=1;VTOT=81316;QTOT=2032;O=30/04/2024-13:23:07;QA=9012;Q=1;PA=40;PCHART=43;NTRAD=106;UNDEFINED"; + ExtractionExpression expression = Expressions.Expression(expressionStr); + StringSubject subject = + assertThat(valueSelector(expression).extractValue(fromValue(text)).text()); + if (expected.equals("NULL")) { + subject.isNull(); + } else { + subject.isEqualTo(expected); + } + } + + @ParameterizedTest(name = "[{index}] {arguments}") + @CsvSource( + useHeadersInDisplayName = true, + textBlock = + """ + EXPRESSION, EXPECTED_ERROR_MESSAGE + VALUE.no_attrib, Field [no_attrib] not found + VALUE.no_children[0], Field [no_children] not found + VALUE.QCHARTTOT[0], Field [QCHARTTOT] is not indexed + VALUE.QCHARTTOT['no_key'], Field [no_key] not found + """) + public void shouldNotExtractValue(String expressionStr, String errorMessage) { + ExtractionExpression expression = Expressions.Expression(expressionStr); + ValueException ve = + assertThrows( + ValueException.class, + () -> valueSelector(expression).extractValue(fromValue(TEXT)).text()); + assertThat(ve.getMessage()).isEqualTo(errorMessage); + } + + @ParameterizedTest(name = "[{index}] {arguments}") + @CsvSource( + useHeadersInDisplayName = true, + textBlock = + """ + EXPRESSION, EXPECTED + KEY.QCHARTTOT, 2032 + KEY.TRow, 12790 + KEY.QV, 9 + KEY.PV, 43 + KEY.TMSTMP, 2024-04-3013:23:07 + KEY.QCHART, 1 + KEY.VTOT, 81316 + KEY.QTOT, 2032 + KEY.O, 30/04/2024-13:23:07 + KEY.QA, 9012 + KEY.Q, 1 + KEY.PA, 40 + KEY.PCHART, 43 + KEY.NTRAD, 106 + KEY.UNDEFINED, NULL + """) + public void shouldExtractKey(String expressionStr, String expected) throws ExtractionException { + ExtractionExpression expression = Expressions.Expression(expressionStr); + StringSubject subject = + assertThat(keySelector(expression).extractKey(fromKey(TEXT)).text()); + if (expected.equals("NULL")) { + subject.isNull(); + } else { + subject.isEqualTo(expected); + } + } + + @ParameterizedTest(name = "[{index}] {arguments}") + @CsvSource( + useHeadersInDisplayName = true, + textBlock = + """ + EXPRESSION, EXPECTED_ERROR_MESSAGE + KEY.no_attrib, Field [no_attrib] not found + KEY.no_children[0], Field [no_children] not found + KEY.QCHARTTOT[0], Field [QCHARTTOT] is not indexed + KEY.QCHARTTOT['no_key'], Field [no_key] not found + """) + public void shouldNotExtractKey(String expressionStr, String errorMessage) { + ExtractionExpression expression = Expressions.Expression(expressionStr); + ValueException ve = + assertThrows( + ValueException.class, + () -> keySelector(expression).extractKey(fromKey(TEXT)).text()); + assertThat(ve.getMessage()).isEqualTo(errorMessage); + } + + @ParameterizedTest(name = "[{index}] {arguments}") + @CsvSource( + useHeadersInDisplayName = true, + textBlock = + """ + EXPRESSION, EXPECTED_ERROR_MESSAGE + VALUE, Found the invalid expression [VALUE] with missing attribute while evaluating [name] + VALUE.a. .b, Found the invalid expression [VALUE.a. .b] with missing tokens while evaluating [name] + VALUE.attrib[], Found the invalid indexed expression [VALUE.attrib[]] while evaluating [name] + VALUE.attrib[0]xsd, Found the invalid indexed expression [VALUE.attrib[0]xsd] while evaluating [name] + VALUE.attrib[], Found the invalid indexed expression [VALUE.attrib[]] while evaluating [name] + VALUE.attrib[a], Found the invalid indexed expression [VALUE.attrib[a]] while evaluating [name] + """) + public void shouldNotCreateValueSelector(String expressionStr, String expectedErrorMessage) { + ExtractionExpression expression = Expressions.Expression(expressionStr); + ExtractionException ee = + assertThrows(ExtractionException.class, () -> valueSelector(expression)); + assertThat(ee.getMessage()).isEqualTo(expectedErrorMessage); + } + + @ParameterizedTest(name = "[{index}] {arguments}") + @CsvSource( + useHeadersInDisplayName = true, + textBlock = + """ + EXPRESSION, EXPECTED_ERROR_MESSAGE + KEY, Found the invalid expression [KEY] with missing attribute while evaluating [name] + KEY.a. .b, Found the invalid expression [KEY.a. .b] with missing tokens while evaluating [name] + KEY.attrib[], Found the invalid indexed expression [KEY.attrib[]] while evaluating [name] + KEY.attrib[0]xsd, Found the invalid indexed expression [KEY.attrib[0]xsd] while evaluating [name] + KEY.attrib[], Found the invalid indexed expression [KEY.attrib[]] while evaluating [name] + KEY.attrib[a], Found the invalid indexed expression [KEY.attrib[a]] while evaluating [name] + """) + public void shouldNotCreateKeySelector(String expressionStr, String expectedErrorMessage) { + ExtractionExpression expression = Expressions.Expression(expressionStr); + ExtractionException ee = + assertThrows(ExtractionException.class, () -> keySelector(expression)); + assertThat(ee.getMessage()).isEqualTo(expectedErrorMessage); + } +} diff --git a/kafka-connector-project/kafka-connector/src/test/java/com/lightstreamer/kafka/adapters/mapping/selectors/kvp/KvpNodeTest.java b/kafka-connector-project/kafka-connector/src/test/java/com/lightstreamer/kafka/adapters/mapping/selectors/kvp/KvpNodeTest.java new file mode 100644 index 00000000..ddf3c93f --- /dev/null +++ b/kafka-connector-project/kafka-connector/src/test/java/com/lightstreamer/kafka/adapters/mapping/selectors/kvp/KvpNodeTest.java @@ -0,0 +1,146 @@ + +/* + * Copyright (C) 2024 Lightstreamer Srl + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +package com.lightstreamer.kafka.adapters.mapping.selectors.kvp; + +import static com.google.common.truth.Truth.assertThat; + +import com.lightstreamer.kafka.adapters.mapping.selectors.kvp.KvpSelectorsSuppliers.KvpNode; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.NullAndEmptySource; +import org.junit.jupiter.params.provider.ValueSource; + +public class KvpNodeTest { + + @Test + public void shouldParseValidString() { + String text = "key1=value1;key2=value2"; + KvpSelectorsSuppliers.KvpMap csvMap = KvpSelectorsSuppliers.KvpMap.fromString(text); + + assertThat(csvMap.size()).isEqualTo(2); + assertThat(csvMap.isScalar()).isFalse(); + assertThat(csvMap.isArray()).isFalse(); + assertThat(csvMap.isNull()).isFalse(); + assertThat(csvMap.has("key1")).isTrue(); + + KvpNode key1 = csvMap.get("key1"); + assertThat(key1.size()).isEqualTo(0); + assertThat(key1.isScalar()).isTrue(); + assertThat(key1.isArray()).isFalse(); + assertThat(key1.isNull()).isFalse(); + assertThat(key1.has("anyProp")).isFalse(); + assertThat(key1.get("anyProp")).isNull(); + assertThat(key1.get(3)).isNull(); + assertThat(key1.asText("")).isEqualTo("value1"); + + assertThat(csvMap.has("key2")).isTrue(); + + KvpNode key2 = csvMap.get("key2"); + assertThat(key1.size()).isEqualTo(0); + assertThat(key2.isScalar()).isTrue(); + assertThat(key2.isArray()).isFalse(); + assertThat(key2.isNull()).isFalse(); + assertThat(key2.has("anyProp")).isFalse(); + assertThat(key2.get("anyProp")).isNull(); + assertThat(key2.get(3)).isNull(); + assertThat(key2.asText("")).isEqualTo("value2"); + + assertThat(csvMap.has("key3")).isFalse(); + } + + @Test + public void shouldParseStringWithNullValue() { + String text = "key1"; + KvpSelectorsSuppliers.KvpMap csvMap = KvpSelectorsSuppliers.KvpMap.fromString(text); + assertThat(csvMap.size()).isEqualTo(1); + assertThat(csvMap.isScalar()).isFalse(); + assertThat(csvMap.isArray()).isFalse(); + assertThat(csvMap.isNull()).isFalse(); + + KvpNode key1 = csvMap.get("key1"); + assertThat(key1.size()).isEqualTo(0); + assertThat(key1.isScalar()).isTrue(); + assertThat(key1.isArray()).isFalse(); + assertThat(key1.isNull()).isTrue(); + assertThat(key1.asText(null)).isNull(); + assertThat(key1.asText("NULL")).isEqualTo("NULL"); + } + + @Test + public void shouldParseStringEndingWithNullValue() { + String text = "key1;"; + KvpSelectorsSuppliers.KvpMap csvMap = KvpSelectorsSuppliers.KvpMap.fromString(text); + assertThat(csvMap.size()).isEqualTo(2); + assertThat(csvMap.isScalar()).isFalse(); + assertThat(csvMap.isArray()).isFalse(); + assertThat(csvMap.isNull()).isFalse(); + + KvpNode key1 = csvMap.get("key1"); + assertThat(key1.size()).isEqualTo(0); + assertThat(key1.isScalar()).isTrue(); + assertThat(key1.isArray()).isFalse(); + assertThat(key1.isNull()).isTrue(); + assertThat(key1.asText(null)).isNull(); + assertThat(key1.asText("NULL")).isEqualTo("NULL"); + + KvpNode nullKey = csvMap.get(""); + assertThat(nullKey.size()).isEqualTo(0); + assertThat(nullKey.isScalar()).isTrue(); + assertThat(nullKey.isArray()).isFalse(); + assertThat(nullKey.isNull()).isTrue(); + assertThat(nullKey.asText(null)).isNull(); + assertThat(nullKey.asText("NULL")).isEqualTo("NULL"); + } + + @ParameterizedTest + @NullAndEmptySource + @ValueSource(strings = {" ", " ", "\t", "\n"}) + public void shouldHandleEmptyOrNullString(String text) { + KvpSelectorsSuppliers.KvpMap csvMap = KvpSelectorsSuppliers.KvpMap.fromString(text); + assertThat(csvMap.size()).isEqualTo(0); + assertThat(csvMap.isScalar()).isFalse(); + assertThat(csvMap.isArray()).isFalse(); + assertThat(csvMap.isNull()).isTrue(); + assertThat(csvMap.asText(null)).isNull(); + assertThat(csvMap.asText("NULL")).isEqualTo("NULL"); + } + + @ParameterizedTest(name = "[{index}] {arguments}") + @CsvSource( + useHeadersInDisplayName = true, + delimiter = '|', + textBlock = + """ + INPUT | EXPECTED | EXPECTED_SIZE + key1 | {key1=NULL_VALUE} | 1 + key1=value1 | {key1=value1} | 1 + key1=value1;key2=value2 | {key1=value1, key2=value2} | 2 + key1=value1;key2 | {key1=value1, key2=NULL_VALUE} | 2 + key1=value1; | {key1=value1, =NULL_VALUE} | 2 + """) + public void shouldReturnAsText(String input, String expected, int expectedSize) { + KvpSelectorsSuppliers.KvpMap csvMap = KvpSelectorsSuppliers.KvpMap.fromString(input); + assertThat(csvMap.size()).isEqualTo(expectedSize); + assertThat(csvMap.isScalar()).isFalse(); + assertThat(csvMap.isArray()).isFalse(); + assertThat(csvMap.isNull()).isFalse(); + assertThat(csvMap.asText(null)).isEqualTo(expected); + } +} diff --git a/kafka-connector-project/kafka-connector/src/test/java/com/lightstreamer/kafka/common/mapping/RecordRoutingTest.java b/kafka-connector-project/kafka-connector/src/test/java/com/lightstreamer/kafka/common/mapping/RecordRoutingTest.java index 5f49f797..5e8c5b7a 100644 --- a/kafka-connector-project/kafka-connector/src/test/java/com/lightstreamer/kafka/common/mapping/RecordRoutingTest.java +++ b/kafka-connector-project/kafka-connector/src/test/java/com/lightstreamer/kafka/common/mapping/RecordRoutingTest.java @@ -28,6 +28,8 @@ import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.lightstreamer.kafka.adapters.mapping.selectors.WrapperKeyValueSelectorSuppliers; +import com.lightstreamer.kafka.adapters.mapping.selectors.kvp.KvpSelectorsSuppliers; import com.lightstreamer.kafka.adapters.mapping.selectors.others.OthersSelectorSuppliers; import com.lightstreamer.kafka.common.mapping.Items.ItemTemplates; import com.lightstreamer.kafka.common.mapping.Items.SubscribedItem; @@ -40,6 +42,7 @@ import com.lightstreamer.kafka.test_utils.Records; import org.apache.avro.generic.GenericRecord; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.CsvFileSource; @@ -180,6 +183,34 @@ public void shouldRoutesFromTemplates( } } + @Test + public void shimpleTest() throws ExtractionException { + // quote-[symbol=MPDF.SIT.DATA.QUOTE.SAMA.1010] + KvpSelectorsSuppliers kvpSelectorsSuppliers = new KvpSelectorsSuppliers(); + ItemTemplates templates = + ItemTemplatesUtils.ItemTemplates( + new WrapperKeyValueSelectorSuppliers<>( + kvpSelectorsSuppliers.makeKeySelectorSupplier(), + kvpSelectorsSuppliers.makeValueSelectorSupplier()), + List.of("MPDF.SIT.DATA.QUOTE.SAMA.1010*"), + List.of("quote-#{symbol=TOPIC}")); + + RecordMapper mapper = + RecordMapper.builder() + .withTemplateExtractors(templates.extractorsByTopicName()) + .build(); + + MappedRecord mapped = + mapper.map(Records.record("MPDF.SIT.DATA.QUOTE.SAMA.1010", "key", "test")); + Set route = + mapped.route( + Set.of( + Items.subscribedFrom( + "quote-[symbol=MPDF.SIT.DATA.QUOTE.SAMA.1010]", + new Object()))); + assertThat(route.isEmpty()).isFalse(); + } + static Stream templateArgsJson() { return Stream.of( arguments( diff --git a/kafka-connector-project/kafka-connector/src/test/java/com/lightstreamer/kafka/common/mapping/selectors/DataExtractorTest.java b/kafka-connector-project/kafka-connector/src/test/java/com/lightstreamer/kafka/common/mapping/selectors/DataExtractorTest.java index 4ce30ded..60c1231d 100644 --- a/kafka-connector-project/kafka-connector/src/test/java/com/lightstreamer/kafka/common/mapping/selectors/DataExtractorTest.java +++ b/kafka-connector-project/kafka-connector/src/test/java/com/lightstreamer/kafka/common/mapping/selectors/DataExtractorTest.java @@ -214,4 +214,41 @@ public void shouldNotCreate(String expression) throws ExtractionException { TEST_SCHEMA, Map.of("value", Expression(expression)))); } + + // @Test + // public void test() throws ExtractionException, JsonMappingException, + // JsonProcessingException { + + // FieldConfigs f = FieldConfigs.from(Map.of("pippo", "#{VALUE.pippo}")); + // var extractor = f.extractor(TestSelectorSuppliers.JsonValue(), true); + + // TopicConfigurations topicsConfig = + // TopicConfigurations.of( + // ItemTemplateConfigs.empty(), + // List.of( + // TopicMappingConfig.fromDelimitedMappings( + // "record-topic", "simple-item"))); + + // ItemTemplates templates = + // Items.templatesFrom(topicsConfig, TestSelectorSuppliers.JsonValue()); + + // RecordMapper mapper = + // RecordMapper.builder() + // .withFieldExtractor(extractor) + // .withTemplateExtractors(templates.extractorsByTopicName()) + // .build(); + + // ObjectMapper oo = new ObjectMapper(); + // String content = + // """ + // { + // "pippo":"pluto", + // "name":"pippo" + // } + // """; + // JsonNode tree = oo.readTree(content); + // KafkaRecord record = Records.record("key", tree); + // MappedRecord map = mapper.map(record); + // System.out.println(map); + // } } diff --git a/kafka-connector-project/kafka-connector/src/test/java/com/lightstreamer/kafka/test_utils/VersionUtils.java b/kafka-connector-project/kafka-connector/src/test/java/com/lightstreamer/kafka/test_utils/VersionUtils.java index 120ae831..f9a75aff 100644 --- a/kafka-connector-project/kafka-connector/src/test/java/com/lightstreamer/kafka/test_utils/VersionUtils.java +++ b/kafka-connector-project/kafka-connector/src/test/java/com/lightstreamer/kafka/test_utils/VersionUtils.java @@ -22,7 +22,7 @@ import java.util.regex.Pattern; public class VersionUtils { - static Pattern VERSION = Pattern.compile("\\d\\.\\d\\.\\d"); + static Pattern VERSION = Pattern.compile("\\d\\.\\d\\.\\d(-.+)?"); /** Gets the current version as stored in the resources/version.properties file */ public static String currentVersion() {