From 9b603705b02ed79fbbcf21cc59192906a1854462 Mon Sep 17 00:00:00 2001 From: Joris Borgdorff Date: Thu, 13 Jul 2017 12:56:26 +0200 Subject: [PATCH 01/12] Move streams to backend --- .../radarcns/stream/GeneralStreamGroup.java | 88 ------------------- .../org/radarcns/stream/StreamDefinition.java | 62 ------------- .../java/org/radarcns/stream/StreamGroup.java | 38 -------- .../radarcns/stream/StreamDefinitionTest.java | 53 ----------- 4 files changed, 241 deletions(-) delete mode 100644 src/main/java/org/radarcns/stream/GeneralStreamGroup.java delete mode 100644 src/main/java/org/radarcns/stream/StreamDefinition.java delete mode 100644 src/main/java/org/radarcns/stream/StreamGroup.java delete mode 100644 src/test/java/org/radarcns/stream/StreamDefinitionTest.java diff --git a/src/main/java/org/radarcns/stream/GeneralStreamGroup.java b/src/main/java/org/radarcns/stream/GeneralStreamGroup.java deleted file mode 100644 index 669a1e87..00000000 --- a/src/main/java/org/radarcns/stream/GeneralStreamGroup.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2017 The Hyve and King's College London - * - * 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 org.radarcns.stream; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import org.radarcns.topic.KafkaTopic; - -/** - * Implementation of a {@link StreamGroup}. Override to create specific streams for a given - * device: use the {@link #createStream(String, String)} to create an internal stream and - * {@link #createSensorStream(String)} to create a sensor stream. - * - *

To access the streams, create getter functions or use the {@link #getStreamDefinition(String)} - * method. - */ -public class GeneralStreamGroup implements StreamGroup { - - public static final String OUTPUT_LABEL = "_output"; - - private final Map topicMap; - private final Set topicNames; - - public GeneralStreamGroup() { - topicMap = new HashMap<>(); - topicNames = new HashSet<>(); - } - - /** - * Create a stream from input to output topic. By using this method, {@link #getTopicNames()} - * and {@link #getStreamDefinition(String)} automatically get updated. - * @param input input topic name - * @param output output topic name - * @return stream definition. - */ - protected StreamDefinition createStream(String input, String output) { - StreamDefinition ret = new StreamDefinition(new KafkaTopic(input), new KafkaTopic(output)); - topicMap.put(input, ret); - topicNames.add(input); - topicNames.add(output); - return ret; - } - - /** - * Create a sensor stream from input topic to a "[input]_output" topic. By using this method, - * {@link #getTopicNames()} and {@link #getStreamDefinition(String)} automatically get updated. - * @param input input topic name - * @return sensor stream definition - */ - protected StreamDefinition createSensorStream(String input) { - return createStream(input, input + OUTPUT_LABEL); - } - - @Override - public StreamDefinition getStreamDefinition(String inputTopic) { - StreamDefinition topic = topicMap.get(inputTopic); - if (topic == null) { - throw new IllegalArgumentException("Topic " + inputTopic + " unknown"); - } - return topic; - } - - @Override - public List getTopicNames() { - List topicList = new ArrayList<>(topicNames); - Collections.sort(topicList, String.CASE_INSENSITIVE_ORDER); - return topicList; - } -} diff --git a/src/main/java/org/radarcns/stream/StreamDefinition.java b/src/main/java/org/radarcns/stream/StreamDefinition.java deleted file mode 100644 index edc4227d..00000000 --- a/src/main/java/org/radarcns/stream/StreamDefinition.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2017 The Hyve and King's College London - * - * 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 org.radarcns.stream; - -import org.radarcns.topic.KafkaTopic; - -public class StreamDefinition { - - public static final String FROM_LABEL = "From-"; - public static final String TO_LABEL = "-To-"; - - private final KafkaTopic inputTopic; - private final KafkaTopic outputTopic; - - /** - * Constructor. It takes in input the topic name to be consumed and to topic name where the - * related stream will write the computed values. - * - * @param input source {@link KafkaTopic} - * @param output output {@link KafkaTopic} - */ - public StreamDefinition(KafkaTopic input, KafkaTopic output) { - if (input == null || output == null) { - throw new IllegalArgumentException("Input and output topic may not be null"); - } - this.inputTopic = input; - this.outputTopic = output; - } - - public KafkaTopic getInputTopic() { - return inputTopic; - } - - public KafkaTopic getOutputTopic() { - return outputTopic; - } - - /** - * Kafka Streams allows for stateful stream processing. The internal state is managed in - * so-called state stores. A fault-tolerant state store is an internally created and - * compacted changelog topic. This function return the changelog topic name. - * - * @return {@code String} representing the changelog topic name - */ - public String getStateStoreName() { - return FROM_LABEL + getInputTopic().getName() + TO_LABEL + getOutputTopic().getName(); - } -} diff --git a/src/main/java/org/radarcns/stream/StreamGroup.java b/src/main/java/org/radarcns/stream/StreamGroup.java deleted file mode 100644 index 26d0d221..00000000 --- a/src/main/java/org/radarcns/stream/StreamGroup.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2017 The Hyve and King's College London - * - * 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 org.radarcns.stream; - -import java.util.List; - -/** - * A group of Kafka stream definitions. For example, a stream group can be created for a type of - * device or a business case. - */ -public interface StreamGroup { - /** - * Get all topic names, input and output, that are defined in this stream group. - * @return alphabetically ordered list of topic names - */ - List getTopicNames(); - - /** - * Get the stream definition for a given input topic. - * @param inputTopic input topic name - * @return stream definition of given input topic - */ - StreamDefinition getStreamDefinition(String inputTopic); -} diff --git a/src/test/java/org/radarcns/stream/StreamDefinitionTest.java b/src/test/java/org/radarcns/stream/StreamDefinitionTest.java deleted file mode 100644 index 342fedfc..00000000 --- a/src/test/java/org/radarcns/stream/StreamDefinitionTest.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.radarcns.stream; - -/* - * Copyright 2017 King's College London and The Hyve - * - * 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. - */ - -import static org.junit.Assert.assertEquals; - -import org.apache.kafka.common.errors.InvalidTopicException; -import org.junit.Test; -import org.radarcns.topic.KafkaTopic; - -public class StreamDefinitionTest { - - private static final String INPUT = "android_empatica_e4_blood_volume_pulse"; - private static final String OUTPUT = INPUT + GeneralStreamGroup.OUTPUT_LABEL; - - @Test - public void nameValidation() { - KafkaTopic inputTopic = new KafkaTopic(INPUT); - KafkaTopic outputTopic = new KafkaTopic(OUTPUT); - - StreamDefinition definition = new StreamDefinition(inputTopic, outputTopic); - - kafka.common.Topic.validate(definition.getStateStoreName()); - - assertEquals("From-" + "android_empatica_e4_blood_volume_pulse" + "-To-" + - "android_empatica_e4_blood_volume_pulse" + "_output", - definition.getStateStoreName()); - } - - @Test(expected = InvalidTopicException.class) - public void faultyNameValidation() { - KafkaTopic inputTopic = new KafkaTopic(INPUT + "$"); - KafkaTopic outputTopic = new KafkaTopic(OUTPUT); - - StreamDefinition definition = new StreamDefinition(inputTopic, outputTopic); - - kafka.common.Topic.validate(definition.getStateStoreName()); - } -} From 39692c3e521a4916e41d82b951ea71b33dc638d0 Mon Sep 17 00:00:00 2001 From: Joris Borgdorff Date: Thu, 13 Jul 2017 15:07:59 +0200 Subject: [PATCH 02/12] Removed stale dependency --- build.gradle | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/build.gradle b/build.gradle index 87925c0c..ea6276cc 100644 --- a/build.gradle +++ b/build.gradle @@ -224,17 +224,6 @@ dependencies { testImplementation group: 'org.hamcrest', name: 'hamcrest-all', version: hamcrestVersion testImplementation group: 'com.squareup.okhttp3', name: 'mockwebserver', version: okhttpVersion - // For Topic name validation based on Kafka classes - testImplementation (group: 'org.apache.kafka', name: 'kafka_2.11', version: kafkaVersion) { - exclude group: 'org.apache.kafka', module: 'kafka-clients' - exclude group: 'net.sf.jopt-simple' - exclude group: 'com.yammer.metrics' - exclude group: 'org.scala-lang.modules' - exclude group: 'org.slf4j' - exclude group: 'com.101tec' - exclude group: 'org.apache.zookeeper' - } - codacy group: 'com.github.codacy', name: 'codacy-coverage-reporter', version: codacyVersion } From 1f9864482612d198717e7db3f218f2afafa307a3 Mon Sep 17 00:00:00 2001 From: Joris Borgdorff Date: Thu, 20 Jul 2017 17:19:44 +0200 Subject: [PATCH 03/12] Updated version to snapshot --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index eb0d628e..cbbe81f0 100644 --- a/build.gradle +++ b/build.gradle @@ -37,7 +37,7 @@ allprojects { // Configuration // //---------------------------------------------------------------------------// - version = '0.4.2' + version = '0.5-SNAPSHOT' group = 'org.radarcns' ext.githubRepoName = 'RADAR-CNS/RADAR-Commons' From 4be1fa51476c6d0c32f80638058ad827964e5b77 Mon Sep 17 00:00:00 2001 From: Joris Borgdorff Date: Tue, 25 Jul 2017 14:40:39 +0200 Subject: [PATCH 04/12] Javadoc fixes --- .../org/radarcns/stream/collector/DoubleArrayCollector.java | 5 +++-- .../org/radarcns/stream/collector/DoubleValueCollector.java | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/radarcns/stream/collector/DoubleArrayCollector.java b/src/main/java/org/radarcns/stream/collector/DoubleArrayCollector.java index c2a8d5c5..60874181 100644 --- a/src/main/java/org/radarcns/stream/collector/DoubleArrayCollector.java +++ b/src/main/java/org/radarcns/stream/collector/DoubleArrayCollector.java @@ -20,12 +20,13 @@ import java.util.List; /** - * Java class to aggregate data using Kafka Streams. Double Array is the base unit + * Java class to aggregate data using Kafka Streams. Double Array is the base type. */ public class DoubleArrayCollector { private DoubleValueCollector[] collectors; /** + * Add a sample to the collection. * @param value new sample that has to be analysed */ public DoubleArrayCollector add(double[] value) { @@ -55,4 +56,4 @@ public String toString() { public List getCollectors() { return Arrays.asList(collectors); } -} \ No newline at end of file +} diff --git a/src/main/java/org/radarcns/stream/collector/DoubleValueCollector.java b/src/main/java/org/radarcns/stream/collector/DoubleValueCollector.java index 4b7a5428..6fe9b70b 100644 --- a/src/main/java/org/radarcns/stream/collector/DoubleValueCollector.java +++ b/src/main/java/org/radarcns/stream/collector/DoubleValueCollector.java @@ -24,7 +24,7 @@ import java.util.Collections; import java.util.List; -/** Java class to aggregate data using Kafka Streams. Double is the base unit */ +/** Java class to aggregate data using Kafka Streams. Double is the base type. */ public class DoubleValueCollector { private double min = Double.MAX_VALUE; private double max = Double.MIN_VALUE; @@ -41,6 +41,7 @@ public DoubleValueCollector add(float value) { } /** + * Add a sample to the collection. * @param value new sample that has to be analysed */ public DoubleValueCollector add(double value) { From 8c3107ff01fd86aab2764f05923e892b20b11abf Mon Sep 17 00:00:00 2001 From: Joris Borgdorff Date: Tue, 25 Jul 2017 14:40:51 +0200 Subject: [PATCH 05/12] Kafka topic validation --- .../java/org/radarcns/topic/KafkaTopic.java | 22 ++++++++++++------- .../radarcns/stream/StreamDefinitionTest.java | 2 +- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/radarcns/topic/KafkaTopic.java b/src/main/java/org/radarcns/topic/KafkaTopic.java index 7e2d6531..49ec605f 100644 --- a/src/main/java/org/radarcns/topic/KafkaTopic.java +++ b/src/main/java/org/radarcns/topic/KafkaTopic.java @@ -16,29 +16,34 @@ package org.radarcns.topic; +import java.util.regex.Pattern; + /** - * Set of Avro Topics - * It defines:

+ * A topic that used by Apache Kafka. */ public class KafkaTopic { private final String name; + private static final Pattern TOPIC_NAME_PATTERN = Pattern.compile("[a-zA-Z][a-zA-Z0-9_]*"); /** + * Kafka topic with given name. * @param name topic name inside the Kafka cluster + * @throws IllegalArgumentException if the topic name is null or is not ASCII-alphanumeric with + * possible underscores. */ public KafkaTopic(String name) { if (name == null) { throw new IllegalArgumentException("Kafka topic name may not be null"); } + if (!TOPIC_NAME_PATTERN.matcher(name).matches()) { + throw new IllegalArgumentException("Kafka topic " + name + " is not ASCII-alphanumeric " + + "with possible underscores."); + } this.name = name; } /** + * Get the topic name. * @return topic name */ public String getName() { @@ -65,7 +70,8 @@ public int hashCode() { return name.hashCode(); } + @Override public String toString() { - return getName(); + return getClass().getSimpleName() + "<" + name + ">"; } } diff --git a/src/test/java/org/radarcns/stream/StreamDefinitionTest.java b/src/test/java/org/radarcns/stream/StreamDefinitionTest.java index 342fedfc..4979ff1b 100644 --- a/src/test/java/org/radarcns/stream/StreamDefinitionTest.java +++ b/src/test/java/org/radarcns/stream/StreamDefinitionTest.java @@ -41,7 +41,7 @@ public void nameValidation() { definition.getStateStoreName()); } - @Test(expected = InvalidTopicException.class) + @Test(expected = IllegalArgumentException.class) public void faultyNameValidation() { KafkaTopic inputTopic = new KafkaTopic(INPUT + "$"); KafkaTopic outputTopic = new KafkaTopic(OUTPUT); From 6c17414923791c7bfaa92ea4b2f33e8da0a67e5c Mon Sep 17 00:00:00 2001 From: Joris Borgdorff Date: Mon, 7 Aug 2017 16:15:39 +0200 Subject: [PATCH 06/12] Use Headers instead of list of entries --- .../radarcns/producer/rest/RestSender.java | 50 ++++++++----------- 1 file changed, 20 insertions(+), 30 deletions(-) diff --git a/src/main/java/org/radarcns/producer/rest/RestSender.java b/src/main/java/org/radarcns/producer/rest/RestSender.java index ab22cc24..434ca076 100644 --- a/src/main/java/org/radarcns/producer/rest/RestSender.java +++ b/src/main/java/org/radarcns/producer/rest/RestSender.java @@ -28,10 +28,8 @@ import java.util.Map.Entry; import java.util.Objects; import java.util.concurrent.TimeUnit; -import okhttp3.HttpUrl; -import okhttp3.MediaType; -import okhttp3.Request; -import okhttp3.Response; + +import okhttp3.*; import org.apache.avro.Schema; import org.radarcns.config.ServerConfig; import org.radarcns.data.AvroEncoder; @@ -73,14 +71,14 @@ public class RestSender implements KafkaSender { private HttpUrl schemalessKeyUrl; private HttpUrl schemalessValueUrl; - private Request isConnectedRequest; + private Request.Builder isConnectedRequest; private SchemaRetriever schemaRetriever; private RestClient httpClient; private String acceptType; private MediaType contentType; private boolean useCompression; private final ConnectionState state; - private List> additionalHeaders; + private Headers additionalHeaders; /** * Construct a RestSender. @@ -90,11 +88,11 @@ public class RestSender implements KafkaSender { * @param valueEncoder non-null Avro encoder for values * @param useCompression use compression to send data * @param sharedState shared connection state - * @param additionalHeaders + * @param additionalHeaders headers to add to requests */ private RestSender(RestClient httpClient, SchemaRetriever schemaRetriever, AvroEncoder keyEncoder, AvroEncoder valueEncoder, boolean useCompression, - ConnectionState sharedState, List> additionalHeaders) { + ConnectionState sharedState, Headers additionalHeaders) { this.schemaRetriever = schemaRetriever; this.keyEncoder = keyEncoder; this.valueEncoder = valueEncoder; @@ -132,7 +130,7 @@ private void setRestClient(RestClient newClient) { try { schemalessKeyUrl = HttpUrl.get(newClient.getRelativeUrl("topics/schemaless-key")); schemalessValueUrl = HttpUrl.get(newClient.getRelativeUrl("topics/schemaless-value")); - isConnectedRequest = newClient.requestBuilder("").head().build(); + isConnectedRequest = newClient.requestBuilder("").head(); } catch (MalformedURLException ex) { throw new IllegalArgumentException("Schemaless topics do not have a valid URL", ex); } @@ -160,7 +158,7 @@ private synchronized HttpUrl getSchemalessKeyUrl() { } private synchronized Request getIsConnectedRequest() { - return isConnectedRequest; + return isConnectedRequest.headers(additionalHeaders).build(); } public synchronized void setCompression(boolean useCompression) { @@ -171,11 +169,11 @@ private synchronized boolean hasCompression() { return this.useCompression; } - public synchronized List> getHeaders() { + public synchronized Headers getHeaders() { return additionalHeaders; } - public synchronized void setHeaders(List> additionalHeaders) { + public synchronized void setHeaders(Headers additionalHeaders) { this.additionalHeaders = additionalHeaders; } @@ -270,7 +268,7 @@ private Request buildRequest(List> records) throws IOException { MediaType currentContentType; String currentAcceptType; - List> currentHeaders; + Headers currentHeaders; synchronized (RestSender.this) { currentContentType = contentType; @@ -281,12 +279,9 @@ private Request buildRequest(List> records) throws IOException { TopicRequestBody requestBody; Request.Builder requestBuilder = new Request.Builder() .url(sendToUrl) + .headers(currentHeaders) .addHeader("Accept", currentAcceptType); - for (Map.Entry header : currentHeaders) { - requestBuilder.addHeader(header.getKey(), header.getValue()); - } - if (hasCompression()) { requestBody = new GzipTopicRequestBody(requestData, currentContentType); requestBuilder.addHeader("Content-Encoding", "gzip"); @@ -413,7 +408,7 @@ public static class Builder { private long timeout = 10; private ConnectionState state; private ManagedConnectionPool pool; - private List> additionalHeaders; + private Headers.Builder additionalHeaders; public Builder server(ServerConfig kafkaConfig) { this.kafkaConfig = kafkaConfig; @@ -452,15 +447,15 @@ public Builder connectionPool(ManagedConnectionPool pool) { } public Builder headers(List> headers) { - this.additionalHeaders = headers; + additionalHeaders = new Headers.Builder(); + for (Entry header : headers) { + additionalHeaders.add(header.getKey(), header.getValue()); + } return this; } public Builder addHeader(String header, String value) { - if (additionalHeaders == null) { - additionalHeaders = new ArrayList<>(); - } - additionalHeaders.add(new AbstractMap.SimpleImmutableEntry<>(header, value)); + additionalHeaders.add(header, value); return this; } @@ -474,7 +469,6 @@ public RestSender build() { } ConnectionState useState; ManagedConnectionPool usePool; - List> useHeaders; if (state != null) { useState = state; @@ -486,14 +480,10 @@ public RestSender build() { } else { usePool = ManagedConnectionPool.GLOBAL_POOL; } - if (additionalHeaders != null) { - useHeaders = additionalHeaders; - } else { - useHeaders = Collections.emptyList(); - } return new RestSender<>(new RestClient(kafkaConfig, timeout, usePool), - retriever, keyEncoder, valueEncoder, compression, useState, useHeaders); + retriever, keyEncoder, valueEncoder, compression, useState, + additionalHeaders.build()); } } } From 1501b5538b332c74ef5c5044874e209b39c675b4 Mon Sep 17 00:00:00 2001 From: Joris Borgdorff Date: Mon, 7 Aug 2017 16:36:34 +0200 Subject: [PATCH 07/12] Logical restructuring of rest senders --- .../{rest => }/BatchedKafkaSender.java | 4 +- .../{rest => }/ThreadedKafkaSender.java | 10 +-- .../radarcns/producer/rest/RestSender.java | 33 +++---- .../producer/{ => rest}/SchemaRetriever.java | 5 +- .../util/serde/AbstractKafkaAvroSerde.java | 2 +- .../util/serde/KafkaAvroSerializer.java | 2 +- .../producer/rest/RestSenderTest.java | 86 +++++++------------ .../{ => rest}/SchemaRetrieverTest.java | 3 +- .../util/serde/KafkaAvroSerializerTest.java | 2 +- .../java/org/radarcns/mock/MockProducer.java | 4 +- 10 files changed, 63 insertions(+), 88 deletions(-) rename src/main/java/org/radarcns/producer/{rest => }/BatchedKafkaSender.java (97%) rename src/main/java/org/radarcns/producer/{rest => }/ThreadedKafkaSender.java (96%) rename src/main/java/org/radarcns/producer/{ => rest}/SchemaRetriever.java (98%) rename src/test/java/org/radarcns/producer/{ => rest}/SchemaRetrieverTest.java (98%) diff --git a/src/main/java/org/radarcns/producer/rest/BatchedKafkaSender.java b/src/main/java/org/radarcns/producer/BatchedKafkaSender.java similarity index 97% rename from src/main/java/org/radarcns/producer/rest/BatchedKafkaSender.java rename to src/main/java/org/radarcns/producer/BatchedKafkaSender.java index 57aa8c10..dbf09267 100644 --- a/src/main/java/org/radarcns/producer/rest/BatchedKafkaSender.java +++ b/src/main/java/org/radarcns/producer/BatchedKafkaSender.java @@ -14,12 +14,10 @@ * limitations under the License. */ -package org.radarcns.producer.rest; +package org.radarcns.producer; import org.radarcns.data.Record; import org.radarcns.topic.AvroTopic; -import org.radarcns.producer.KafkaSender; -import org.radarcns.producer.KafkaTopicSender; import java.io.IOException; import java.util.ArrayList; diff --git a/src/main/java/org/radarcns/producer/rest/ThreadedKafkaSender.java b/src/main/java/org/radarcns/producer/ThreadedKafkaSender.java similarity index 96% rename from src/main/java/org/radarcns/producer/rest/ThreadedKafkaSender.java rename to src/main/java/org/radarcns/producer/ThreadedKafkaSender.java index 946afefc..4afc4526 100644 --- a/src/main/java/org/radarcns/producer/rest/ThreadedKafkaSender.java +++ b/src/main/java/org/radarcns/producer/ThreadedKafkaSender.java @@ -14,11 +14,10 @@ * limitations under the License. */ -package org.radarcns.producer.rest; +package org.radarcns.producer; import org.radarcns.data.Record; -import org.radarcns.producer.KafkaSender; -import org.radarcns.producer.KafkaTopicSender; +import org.radarcns.producer.rest.ConnectionState; import org.radarcns.topic.AvroTopic; import org.radarcns.util.RollingTimeAverage; import org.slf4j.Logger; @@ -34,9 +33,8 @@ import java.util.concurrent.TimeUnit; /** - * Send Avro Records to a Kafka REST Proxy. - * - * This queues messages for a specified amount of time and then sends all messages up to that time. + * Send Avro Records to a Kafka REST Proxy. This queues messages for a specified amount of time + * and then sends all messages up to that time. */ public class ThreadedKafkaSender implements KafkaSender { private static final Logger logger = LoggerFactory.getLogger(ThreadedKafkaSender.class); diff --git a/src/main/java/org/radarcns/producer/rest/RestSender.java b/src/main/java/org/radarcns/producer/rest/RestSender.java index 434ca076..b9af887c 100644 --- a/src/main/java/org/radarcns/producer/rest/RestSender.java +++ b/src/main/java/org/radarcns/producer/rest/RestSender.java @@ -17,32 +17,35 @@ package org.radarcns.producer.rest; import com.fasterxml.jackson.core.JsonFactory; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.AbstractMap; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; -import java.util.concurrent.TimeUnit; - -import okhttp3.*; +import okhttp3.Headers; +import okhttp3.HttpUrl; +import okhttp3.MediaType; +import okhttp3.Request; +import okhttp3.Response; import org.apache.avro.Schema; import org.radarcns.config.ServerConfig; import org.radarcns.data.AvroEncoder; import org.radarcns.data.Record; import org.radarcns.producer.AuthenticationException; +import org.radarcns.producer.BatchedKafkaSender; import org.radarcns.producer.KafkaSender; import org.radarcns.producer.KafkaTopicSender; -import org.radarcns.producer.SchemaRetriever; +import org.radarcns.producer.ThreadedKafkaSender; import org.radarcns.producer.rest.ConnectionState.State; import org.radarcns.topic.AvroTopic; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.concurrent.TimeUnit; + /** * RestSender sends records to the Kafka REST Proxy. It does so using an Avro JSON encoding. A new * sender must be constructed with {@link #sender(AvroTopic)} per AvroTopic. This implementation is @@ -408,7 +411,7 @@ public static class Builder { private long timeout = 10; private ConnectionState state; private ManagedConnectionPool pool; - private Headers.Builder additionalHeaders; + private Headers.Builder additionalHeaders = new Headers.Builder(); public Builder server(ServerConfig kafkaConfig) { this.kafkaConfig = kafkaConfig; diff --git a/src/main/java/org/radarcns/producer/SchemaRetriever.java b/src/main/java/org/radarcns/producer/rest/SchemaRetriever.java similarity index 98% rename from src/main/java/org/radarcns/producer/SchemaRetriever.java rename to src/main/java/org/radarcns/producer/rest/SchemaRetriever.java index fa3fc5b2..7d3477c8 100644 --- a/src/main/java/org/radarcns/producer/SchemaRetriever.java +++ b/src/main/java/org/radarcns/producer/rest/SchemaRetriever.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.radarcns.producer; +package org.radarcns.producer.rest; import com.fasterxml.jackson.core.JsonEncoding; import com.fasterxml.jackson.core.JsonFactory; @@ -39,9 +39,6 @@ import org.apache.avro.Schema.Type; import org.apache.avro.generic.GenericContainer; import org.radarcns.config.ServerConfig; -import org.radarcns.producer.rest.ManagedConnectionPool; -import org.radarcns.producer.rest.ParsedSchemaMetadata; -import org.radarcns.producer.rest.RestClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/org/radarcns/util/serde/AbstractKafkaAvroSerde.java b/src/main/java/org/radarcns/util/serde/AbstractKafkaAvroSerde.java index a2a0443e..fc431dc1 100644 --- a/src/main/java/org/radarcns/util/serde/AbstractKafkaAvroSerde.java +++ b/src/main/java/org/radarcns/util/serde/AbstractKafkaAvroSerde.java @@ -21,7 +21,7 @@ import org.apache.kafka.common.config.ConfigException; import org.apache.kafka.common.serialization.Serializer; import org.radarcns.config.ServerConfig; -import org.radarcns.producer.SchemaRetriever; +import org.radarcns.producer.rest.SchemaRetriever; /** * Abstract class for KafkaAvro(De)serializer. diff --git a/src/main/java/org/radarcns/util/serde/KafkaAvroSerializer.java b/src/main/java/org/radarcns/util/serde/KafkaAvroSerializer.java index dcce8261..22c207d6 100644 --- a/src/main/java/org/radarcns/util/serde/KafkaAvroSerializer.java +++ b/src/main/java/org/radarcns/util/serde/KafkaAvroSerializer.java @@ -25,7 +25,7 @@ import org.apache.avro.specific.SpecificDatumWriter; import org.apache.avro.specific.SpecificRecord; import org.apache.kafka.common.errors.SerializationException; -import org.radarcns.producer.SchemaRetriever; +import org.radarcns.producer.rest.SchemaRetriever; import org.radarcns.producer.rest.ParsedSchemaMetadata; import org.radarcns.util.Serialization; diff --git a/src/test/java/org/radarcns/producer/rest/RestSenderTest.java b/src/test/java/org/radarcns/producer/rest/RestSenderTest.java index b7d5d918..a9b88ef0 100644 --- a/src/test/java/org/radarcns/producer/rest/RestSenderTest.java +++ b/src/test/java/org/radarcns/producer/rest/RestSenderTest.java @@ -16,25 +16,10 @@ package org.radarcns.producer.rest; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectReader; import com.fasterxml.jackson.databind.node.JsonNodeType; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.AbstractMap.SimpleImmutableEntry; -import java.util.Arrays; -import java.util.Map.Entry; -import java.util.zip.GZIPInputStream; import okhttp3.Headers; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; @@ -50,9 +35,16 @@ import org.radarcns.key.MeasurementKey; import org.radarcns.phone.PhoneLight; import org.radarcns.producer.KafkaTopicSender; -import org.radarcns.producer.SchemaRetriever; import org.radarcns.topic.AvroTopic; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.zip.GZIPInputStream; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + public class RestSenderTest { private SchemaRetriever retriever; private RestSender sender; @@ -80,9 +72,11 @@ public void sender() throws Exception { Schema valueSchema = PhoneLight.getClassSchema(); AvroTopic topic = new AvroTopic<>("test", keySchema, valueSchema, MeasurementKey.class, PhoneLight.class); - sender.setHeaders(Arrays.>asList( - new SimpleImmutableEntry<>("Cookie", "ab"), - new SimpleImmutableEntry<>("Cookie", "bc"))); + Headers headers = new Headers.Builder() + .add("Cookie", "ab") + .add("Cookie", "bc") + .build(); + sender.setHeaders(headers); KafkaTopicSender topicSender = sender.sender(topic); MeasurementKey key = new MeasurementKey("a", "b"); @@ -117,19 +111,9 @@ public void sender() throws Exception { JsonNode records = body.get("records"); assertEquals(JsonNodeType.ARRAY, records.getNodeType()); assertEquals(1, records.size()); - for (JsonNode child : records) { - JsonNode jsonKey = child.get("key"); - assertEquals(JsonNodeType.OBJECT, jsonKey.getNodeType()); - assertEquals("a", jsonKey.get("userId").asText()); - assertEquals("b", jsonKey.get("sourceId").asText()); - JsonNode jsonValue = child.get("value"); - assertEquals(JsonNodeType.OBJECT, jsonValue.getNodeType()); - assertEquals(0.1, jsonValue.get("time").asDouble(), 0); - assertEquals(0.2, jsonValue.get("timeReceived").asDouble(), 0); - assertEquals(0.3f, (float)jsonValue.get("light").asDouble(), 0); - } - Headers headers = request.getHeaders(); - assertEquals(Arrays.asList("ab", "bc"), headers.values("Cookie")); + checkChildren(records); + Headers receivedHeaders = request.getHeaders(); + assertEquals(Arrays.asList("ab", "bc"), receivedHeaders.values("Cookie")); } @Test @@ -174,17 +158,7 @@ public void sendTwo() throws Exception { JsonNode records = body.get("records"); assertEquals(JsonNodeType.ARRAY, records.getNodeType()); assertEquals(2, records.size()); - for (JsonNode child : records) { - JsonNode jsonKey = child.get("key"); - assertEquals(JsonNodeType.OBJECT, jsonKey.getNodeType()); - assertEquals("a", jsonKey.get("userId").asText()); - assertEquals("b", jsonKey.get("sourceId").asText()); - JsonNode jsonValue = child.get("value"); - assertEquals(JsonNodeType.OBJECT, jsonValue.getNodeType()); - assertEquals(0.1, jsonValue.get("time").asDouble(), 0); - assertEquals(0.2, jsonValue.get("timeReceived").asDouble(), 0); - assertEquals(0.3f, (float)jsonValue.get("light").asDouble(), 0); - } + checkChildren(records); } @Test @@ -252,17 +226,21 @@ public void withCompression() throws IOException, InterruptedException { JsonNode records = body.get("records"); assertEquals(JsonNodeType.ARRAY, records.getNodeType()); assertEquals(1, records.size()); - for (JsonNode child : records) { - JsonNode jsonKey = child.get("key"); - assertEquals(JsonNodeType.OBJECT, jsonKey.getNodeType()); - assertEquals("a", jsonKey.get("userId").asText()); - assertEquals("b", jsonKey.get("sourceId").asText()); - JsonNode jsonValue = child.get("value"); - assertEquals(JsonNodeType.OBJECT, jsonValue.getNodeType()); - assertEquals(0.1, jsonValue.get("time").asDouble(), 0); - assertEquals(0.2, jsonValue.get("timeReceived").asDouble(), 0); - assertEquals(0.3f, (float)jsonValue.get("light").asDouble(), 0); - } + checkChildren(records); + } + } + + private static void checkChildren(JsonNode records) { + for (JsonNode child : records) { + JsonNode jsonKey = child.get("key"); + assertEquals(JsonNodeType.OBJECT, jsonKey.getNodeType()); + assertEquals("a", jsonKey.get("userId").asText()); + assertEquals("b", jsonKey.get("sourceId").asText()); + JsonNode jsonValue = child.get("value"); + assertEquals(JsonNodeType.OBJECT, jsonValue.getNodeType()); + assertEquals(0.1, jsonValue.get("time").asDouble(), 0); + assertEquals(0.2, jsonValue.get("timeReceived").asDouble(), 0); + assertEquals(0.3f, (float)jsonValue.get("light").asDouble(), 0); } } } diff --git a/src/test/java/org/radarcns/producer/SchemaRetrieverTest.java b/src/test/java/org/radarcns/producer/rest/SchemaRetrieverTest.java similarity index 98% rename from src/test/java/org/radarcns/producer/SchemaRetrieverTest.java rename to src/test/java/org/radarcns/producer/rest/SchemaRetrieverTest.java index 566f14ec..fcbb4566 100644 --- a/src/test/java/org/radarcns/producer/SchemaRetrieverTest.java +++ b/src/test/java/org/radarcns/producer/rest/SchemaRetrieverTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.radarcns.producer; +package org.radarcns.producer.rest; import static org.junit.Assert.assertEquals; @@ -33,6 +33,7 @@ import org.junit.rules.ExpectedException; import org.radarcns.config.ServerConfig; import org.radarcns.producer.rest.ParsedSchemaMetadata; +import org.radarcns.producer.rest.SchemaRetriever; public class SchemaRetrieverTest { private MockWebServer server; diff --git a/src/test/java/org/radarcns/util/serde/KafkaAvroSerializerTest.java b/src/test/java/org/radarcns/util/serde/KafkaAvroSerializerTest.java index 44bb6229..b682fa50 100644 --- a/src/test/java/org/radarcns/util/serde/KafkaAvroSerializerTest.java +++ b/src/test/java/org/radarcns/util/serde/KafkaAvroSerializerTest.java @@ -34,7 +34,7 @@ import org.apache.avro.generic.GenericRecord; import org.junit.Test; import org.radarcns.key.MeasurementKey; -import org.radarcns.producer.SchemaRetriever; +import org.radarcns.producer.rest.SchemaRetriever; import org.radarcns.producer.rest.ParsedSchemaMetadata; public class KafkaAvroSerializerTest { diff --git a/testing/src/main/java/org/radarcns/mock/MockProducer.java b/testing/src/main/java/org/radarcns/mock/MockProducer.java index 529442de..70e224df 100644 --- a/testing/src/main/java/org/radarcns/mock/MockProducer.java +++ b/testing/src/main/java/org/radarcns/mock/MockProducer.java @@ -46,9 +46,9 @@ import org.radarcns.mock.data.MockCsvParser; import org.radarcns.mock.data.RecordGenerator; import org.radarcns.producer.KafkaSender; -import org.radarcns.producer.SchemaRetriever; +import org.radarcns.producer.rest.SchemaRetriever; import org.radarcns.producer.direct.DirectSender; -import org.radarcns.producer.rest.BatchedKafkaSender; +import org.radarcns.producer.BatchedKafkaSender; import org.radarcns.producer.rest.ConnectionState; import org.radarcns.producer.rest.ManagedConnectionPool; import org.radarcns.producer.rest.RestSender; From 875a6b8c937b09f2493fcc137290d863f8481b63 Mon Sep 17 00:00:00 2001 From: Joris Borgdorff Date: Fri, 11 Aug 2017 14:13:35 +0200 Subject: [PATCH 08/12] Removed all logging implementations from library --- build.gradle | 1 + src/main/resources/log4j.properties | 33 ----------------------------- testing/.gitignore | 1 + testing/build.gradle | 5 +++++ 4 files changed, 7 insertions(+), 33 deletions(-) delete mode 100644 src/main/resources/log4j.properties diff --git a/build.gradle b/build.gradle index cbbe81f0..c1866387 100644 --- a/build.gradle +++ b/build.gradle @@ -217,6 +217,7 @@ dependencies { // Direct producer uses KafkaAvroSerializer if initialized testImplementation (group: 'io.confluent', name: 'kafka-avro-serializer', version: confluentVersion) { exclude group: 'com.101tec' + exclude group: 'org.slf4j', module: 'slf4j-log4j12' } testImplementation group: 'org.radarcns', name: 'radar-schemas-commons', version: radarSchemasVersion testImplementation group: 'junit', name: 'junit', version: junitVersion diff --git a/src/main/resources/log4j.properties b/src/main/resources/log4j.properties deleted file mode 100644 index 333af55e..00000000 --- a/src/main/resources/log4j.properties +++ /dev/null @@ -1,33 +0,0 @@ -# -# Copyright 2017 Kings College London and The Hyve -# -# 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. -# - -# Root logger option -log4j.rootLogger=INFO, stdout, file - -# Redirect log messages to console -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.Target=System.out -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=[%d{yyyy-MM-dd HH:mm:ss zzz}] %5p [%t] (%F:%L) - %m (%c)%n -#log4j.appender.stdout.layout.ConversionPattern=[%d{yyyy-MM-dd HH:mm:ss zzz}] %-5p %t %C.%M:%L - %m%n - -# Redirect log messages to a log file, support file rolling. -log4j.appender.file=org.apache.log4j.RollingFileAppender -log4j.appender.file.File=./backend.log -log4j.appender.file.MaxFileSize=10MB -log4j.appender.file.MaxBackupIndex=10 -log4j.appender.file.layout=org.apache.log4j.PatternLayout -log4j.appender.file.layout.ConversionPattern=[%d{yyyy-MM-dd HH:mm:ss zzz}] %-5p %t %c{1}:%L - %m%n diff --git a/testing/.gitignore b/testing/.gitignore index ab55db0e..c9ebf0d6 100644 --- a/testing/.gitignore +++ b/testing/.gitignore @@ -1 +1,2 @@ mock.yml +/out/ diff --git a/testing/build.gradle b/testing/build.gradle index 8fa8d783..fccab482 100644 --- a/testing/build.gradle +++ b/testing/build.gradle @@ -102,6 +102,11 @@ publishing { root.appendNode('description', description) root.appendNode('name', testingName) root.appendNode('url', githubUrl) + root.dependencies.'*'.findAll() { + it.artifactId.text() == 'slf4j-simple' + }.each() { + it.parent().remove(it) + } root.children().last() + pomConfig } } From 8f4ba705536aa07d564337779d6fb908afeeac9f Mon Sep 17 00:00:00 2001 From: Joris Borgdorff Date: Wed, 30 Aug 2017 11:40:09 +0200 Subject: [PATCH 09/12] Increased error reporting length --- src/main/java/org/radarcns/producer/rest/RestSender.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/radarcns/producer/rest/RestSender.java b/src/main/java/org/radarcns/producer/rest/RestSender.java index ab22cc24..e924ac6c 100644 --- a/src/main/java/org/radarcns/producer/rest/RestSender.java +++ b/src/main/java/org/radarcns/producer/rest/RestSender.java @@ -58,6 +58,8 @@ @SuppressWarnings("PMD.GodClass") public class RestSender implements KafkaSender { private static final Logger logger = LoggerFactory.getLogger(RestSender.class); + private static final int LOG_CONTENT_LENGTH = 1024; + public static final String KAFKA_REST_ACCEPT_ENCODING = "application/vnd.kafka.v2+json, application/vnd.kafka+json, application/json"; public static final String KAFKA_REST_ACCEPT_LEGACY_ENCODING = @@ -243,7 +245,7 @@ public void send(List> records) throws IOException { String content = response.body().string(); String requestContent = ((TopicRequestBody)request.body()).content(); requestContent = requestContent.substring(0, - Math.min(requestContent.length(), 255)); + Math.min(requestContent.length(), LOG_CONTENT_LENGTH)); logger.error("FAILED to transmit message: {} -> {}...", content, requestContent); throw new IOException("Failed to submit (HTTP status code " + response.code() @@ -253,7 +255,7 @@ public void send(List> records) throws IOException { state.didDisconnect(); String requestContent = ((TopicRequestBody)request.body()).content(); requestContent = requestContent.substring(0, - Math.min(requestContent.length(), 255)); + Math.min(requestContent.length(), LOG_CONTENT_LENGTH)); logger.error("FAILED to transmit message:\n{}...", requestContent); throw ex; } finally { From f2a89c3345071359e8b02c8fb2b9405b4362e2da Mon Sep 17 00:00:00 2001 From: Joris Borgdorff Date: Wed, 30 Aug 2017 11:43:12 +0200 Subject: [PATCH 10/12] Updated gradle version --- build.gradle | 4 ++-- gradle/wrapper/gradle-wrapper.jar | Bin 54212 -> 54783 bytes gradle/wrapper/gradle-wrapper.properties | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index cbbe81f0..b063bae9 100644 --- a/build.gradle +++ b/build.gradle @@ -369,6 +369,6 @@ artifactoryPublish { } task wrapper(type: Wrapper) { - gradleVersion = '3.4.1' - distributionUrl distributionUrl.replace("bin", "all") + gradleVersion = '3.5' + distributionType 'all' } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 63861da7ae7af0f13fda6f8596877e7ad178f0b3..a9a857c81b0ee59d98b348c723d2523a82296091 100644 GIT binary patch delta 6730 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 j0zSW={1^Q1^$*?$|MSyo2L?yEs6 Date: Wed, 30 Aug 2017 11:47:33 +0200 Subject: [PATCH 11/12] Updated to Gradle 4.1 --- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.jar | Bin 54783 -> 54708 bytes gradle/wrapper/gradle-wrapper.properties | 3 +-- gradlew | 6 +++--- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/build.gradle b/build.gradle index b063bae9..dfed7129 100644 --- a/build.gradle +++ b/build.gradle @@ -369,6 +369,6 @@ artifactoryPublish { } task wrapper(type: Wrapper) { - gradleVersion = '3.5' + gradleVersion = '4.1' distributionType 'all' } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index a9a857c81b0ee59d98b348c723d2523a82296091..7a3265ee94c0ab25cf079ac8ccdf87f41d455d42 100644 GIT binary patch delta 17061 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 Date: Wed, 30 Aug 2017 12:01:37 +0200 Subject: [PATCH 12/12] Bumped version --- README.md | 6 +++--- build.gradle | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e8009297..4a635df0 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ repositories { } dependencies { - compile group: 'org.radarcns', name: 'radar-commons', version: '0.4.2' + compile group: 'org.radarcns', name: 'radar-commons', version: '0.5' } ``` @@ -26,7 +26,7 @@ repositories { } dependencies { - testCompile group: 'org.radarcns', name: 'radar-commons-testing', version: '0.4.2' + testCompile group: 'org.radarcns', name: 'radar-commons-testing', version: '0.5' } ``` @@ -51,7 +51,7 @@ configurations.all { } dependencies { - compile group: 'org.radarcns', name: 'radar-commons', version: '0.5-SNAPSHOT', changing: true + compile group: 'org.radarcns', name: 'radar-commons', version: '0.5.1-SNAPSHOT', changing: true } ``` diff --git a/build.gradle b/build.gradle index a141a57f..68a99669 100644 --- a/build.gradle +++ b/build.gradle @@ -37,7 +37,7 @@ allprojects { // Configuration // //---------------------------------------------------------------------------// - version = '0.5-SNAPSHOT' + version = '0.5' group = 'org.radarcns' ext.githubRepoName = 'RADAR-CNS/RADAR-Commons'